堆排序是利用数据结构堆(heap),进行排序的一种方式。理解起来需要花费一番功夫。笔者在阅读《算法导论》之后,根据伪代码写出了代码,现附录于下,建议配合原书中图片一起理解。
测试代码
先给出测试代码,因为其中定义的一些全局变量可能下面的函数需要使用。先注意里面的全局变量,至于调用的函数是什么意思,下面会有解释。
#include<iostream>
using namespace std;
//0占去A[0];
int A[]={0,16,14,10,9,8,7,4,3,2,1};
//从A[1]开始。算作第一个。所以A的长度减一(减去A[0])
int length=sizeof(A)/sizeof(int)-1;
//length之后要改变,所以和k分开。
int k=sizeof(A)/sizeof(int)-1;
//镶嵌函数部分。
int main()
{
for(int i=1;i<=k;i++)
{
Left(i);
Right(i);
Parent(i);
}
Heapsort(A);
for(int i=1;i<=k;i++)
{
cout<<A[i]<<' ';
}
}
“堆”
伪代码在英文原版152页
本段代码的目的是把每个节点的“父节点”(母节点)和“子节点”找出来,编号原则具体看本节内容的图片。
//编号i节点的父节点
int Parent(int i)
{
return i/2;
}
//左子节点
int Left(int i)
{
return 2*i;
}
//右子节点
int Right(int i)
{
return 2*i+1;
}
维护“堆”的性质
原书154页
本节代码的目的是维护“堆”的性质,即使得父节点里的值要比子节点大,抽象起来就是“堆”,但具体使用起来有些细节要注意。
void Max_heapify(int* A,int i)
{
int l=Left(i);
int r=Right(i);
int largest;
if(l<=length&&A[l]>A[i])
largest=l;
else largest=i;
if(r<=length&&A[r]>A[largest])
largest=r;
if(largest!=i)
{
int t=A[i];
A[i]=A[largest];
A[largest]=t;
Max_heapify(A,largest);
}
}
可以看出,对于一个组成堆得基本元素(一个父节点,两个子节点),以上函数总是将节点中最大的值放到父节点上,有读者可能想到,由于函数是递归的,调用子节点的子节点时,会不会使得子节点的子节点中的值交换到子节点上,从而使得子节点中的值又大于了父节点呢(有些拗口,但意思应该明确),别急,还有下面的函数。
构建堆
void Build_max_heap(int* A)
{
for(int i=k/2;i>=1;i--)
{
Max_heapify(A,i);
}
}
可以看出,从编号大的节点到编号小的节点,分别运行所示函数,可以构建出一个严格的堆,即父节点一定大于子节点。k是数组长度,数学证明从k/2开始即可,有兴趣可见原书。这里有一个简单地论证方法。假设堆(二叉树)呈现一个完整的“金字塔形”,假设最底层有2n个节点(可知最底层是第n+1层),那么以上n层的节点总和,根据等比数列求和,一共是2n-1个,所以如果k是节点总数的话,k/2号节点一定落在倒数第二层。这样在更下面(编号更大)的节点上运行了Max_heapify()函数,可以构建一个堆,是数列的最大值在A[1]中。
排序
原书160页
接下来就是排序了,
void Heapsort(int *A)
{
Build_max_heap(A);
for(int i=k;i>=2;i--)
{
int t=A[1];
A[1]=A[i];
A[i]=t;
//堆排序之后最大的A[1],放在最后的位置A[i]上面,相当于数字“归位了。
//长度减1,下面的函数就不会再排A[i]了;
length--;
Max_heapify(A,1);
}
}
至此结束。水平所限,欢迎指出纰漏。
来源:CSDN
作者:哈哈镜相机
链接:https://blog.csdn.net/weixin_45925418/article/details/104567475