最近在学习算法导论的时候,对于堆排序这部分,在CSDN上找了一篇文章,感觉代码写的比较复杂,不是很有逻辑,因此把书好好的看了一篇,按书中的思想自己完成了堆排序的代码,看起来逻辑比较清晰一些。
分首先排序分为最大堆和最小堆,最大堆是指每个父节点的值都不小于它孩子节点的值,最小堆是指每个父节点的值都不大于它孩子节点的值,我们这里以最大堆为例进行讨论。PS:需要用到的几个小知识,编号为i的节点(树的节点都从1开始),左孩子为2i,右孩子为2i+1,父节点为i/2。
堆排序的核心步骤是如何保证每颗树满足最大堆的特性,比如现在A[LEFT],A[RIGHT]都是最大堆,但是他们2个的父节点A[I]不一定都比这2个孩子大,因此就需要调整,使这颗树成为最大堆。代码如下:
void max_heapify(int arrary[],int i,int m)
{
if (arrary==NULL||i<=0)
return ;
if (i>m)
return;
int left,right,largest;
left=2*i;
right=2*i+1;
if (left<=m&&arrary[left-1]>arrary[i-1])
{
largest=left;
}
else
largest=i;
if (right<=m&&arrary[right-1]>arrary[largest-1])
{
largest=right;
}
if (largest!=i)
{
int temp;
temp=arrary[i-1];
arrary[i-1]=arrary[largest-1];
arrary[largest-1]=temp;
max_heapify(arrary,largest,m);
}
}
传入的参数为1个数组,i为需要调整为最大堆的节点编号,m为数组元素的个数。通过比较i和他的左右子数的大小,来调整i为最大堆,此时调整了位置的largest节点可能违反了最大堆的性质,因此再调整largest这个节点为最大堆直至i下面的子树都变成最大堆。
另外一个步骤就是进行建堆,如何把数组和完全二叉树联系起来。代码如下:
void build_max_haep(int array[],int l)
{
int m=l/2;
for (int k=m;k!=0;--k)
{
max_heapify(array,k,l);
}
}
一颗有l个节点的二叉树,从l/2+1开始都是叶子节点,因此从1到l/2都是有孩子的父节点,从l/2开始依次调整每颗树为最大堆,这样这步完成后,整个树就变成了最大堆。
通过上面的建堆,已经变成了最大堆,那么A[0]就是数组中最大的节点啦,现在要排序应该怎么办呢?
void heap_sort(int array[],int length)
{
build_max_haep(array,length);
for (int i=length;i!=1;--i)
{
int temp;
temp=array[0];
array[0]=array[i-1];
array[i-1]=temp;
--length;
max_heapify(array,1,length);
}
}
这个for循环,就是把A[0]和数组中最后1个元素交换,那么最大的值就到A[i-1]中去了,此时在把1到i-1的元素继续调整为最大堆,得到最大的值放到A[i-2]中,这样最后A[]数组就变成从小到的的数组了。
void main()
{
int a[]={8,2,4,7,1,14,9,10,3,16};
heap_sort(a,sizeof(a)/sizeof(a[0]));
for (size_t ix=0;ix!=sizeof(a)/sizeof(a[0]);++ix)
{
cout << a[ix] <<endl;
}
}
下面是运行结果: