堆排序算法是选择排序的一种,该算法只是通过堆,最大堆、或者最小堆选择出一个待排序序列中的最大值,或者最小值。要想实现堆排序算法,就需要构建什么堆,这里也最小堆为例。说明什么是堆,怎么构建一个堆。
假设待排序序列为A[n] 为一个数组。数组的长度为n 数组下标为 0,1,2,…i,….2i,2i+1….n-1;
堆是一个有序的二叉树。一个二叉树满足如下条件就是一个堆。下面以最小堆为例:
l 树中的非叶节点的数据小于或者等于左右孩子节点的数据。
在队中,没有规定节点的左右孩子的大小,只规定了父节点和子节点数据之间必须满足的条件。当从小到大的顺序输出数据时,要求根节点是最大值。
下面以按照从小到大的顺序排序为例,介绍堆排序的相关内容。
堆排序有连个阶段:
1. 将无序的数据A[n]构成堆,(即用无序的数据生成满足堆定义的完全二叉树)。
2. 利用堆排序(利用上一步生成的堆输出有序的数据)。
下面说明如何构成堆:
获取最后一个非叶节点,和左右孩子节点比较将最大值移动到父节点。比如:n/2 就是最后一个非叶节点。假如此时一个非叶节点的序号为 k 则 k >0 并且 k< n
上一个非叶节点 k -1 , 和 2(k-1) 左节点 、2k右节点中较大的进行比较,如果大于跟节点就和跟节点进行交换,否则不交换。如果交换,加入k -1 和 2(k)交换的,则需要将2k作为父节点进行比较检测,因为k-1 和 2k进行了交换说明k-1小于 2k 而 2k 处的值是以2k为父节点的子树的最大值,一旦交换,则不能保证还是最大值。
如A[n] = {69,65,90,37,92,6,28,54} 将其放在二叉树中,如下图所示:
图中,黄色背景表示:进行比较的根节点。将该节点和左右子树比较,是否交换要看交换结果。第一次交换结果、第二次交换如下图:
本次交换,90 比6,28都大,本次比较不需要交换。
下一次比较和交换:如下图:
下一次:
本次交换完成,但是因为将较小的数放在了节点2 处,一次要遍历 以 2 为根节点的所有子树,以保证,2 节点也是一个符合 左右孩子节点的值都比父节点的值小。 因此将 2 号所在未知的节点重新遍历。如下:
最后结果:
这个整个二叉树就成为一个最大堆。
以上描述的是堆的形成过程。堆排序就是基于这样的过程的。
待排序序列构成一个最大堆,然后将最大的数放在待排序序列的最后,将出去最后序列的剩余数据生成最大堆,一次循环,就可以将待排序的数据变成有序序列。
有以上可知,堆排序的过程中生成最大堆是堆排序的难点。在生成最大堆以后,很容易进行堆排序了,以下是堆排序中生成堆的流程图:
将如当前排序的序列是A[n], 最大非叶节点是 s 序列的长度为n
对排序的整体实现:C++
#include<iostream>
using namespace std;
void GengrateHeap(int array[],int s,int n);
void HeapSort(int array[],int n);
void OutPut(int array[],int n);
int main()
{
int A[8] = {10,9,4,11,3,13,6,5};
OutPut(A,8);
HeapSort(A,8);
OutPut(A,8);
system("pause");
return 0;
}
//输出整个排序序列
void OutPut(int array[],int n)
{
for(int i =0;i<n;i++)
{
cout<<array[i]<<" , ";
}
cout<<endl;
}
//将s为根节点生成一个堆
void GengrateHeap(int array[],int s,int n)
{
int j =0;
int temp = 0;
while(2*s+1<n)
{
j = 2*s+1;
if((j+1)<n)
{
if(array[j]<array[j+1])
{
j++;
}
}
if(array[s]<array[j])
{
temp = array[s];
array[s]=array[j];
array[j]=temp;
s=j;
}
else
{
break;
}
}
}
//堆排序的实现
void HeapSort(int array[],int n)
{
int temp=0;
for(int i =n/2-1;i>=0;i--)
{
GengrateHeap(array,i,n);
}
for(int j =n-1;j>0;j--)
{
temp = array[0];
array[0]=array[j];
array[j]=temp;
GengrateHeap(array,0,j);
}
}
本程序在VC++6.0中能够正确运行。