堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]
即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。
堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。
堆排序的思想
其基本思想为(大顶堆):
1)将初始待排序关键字序列[R0,R2....R(n-1)]构建成大顶堆,此堆为初始的无序区;
2)将堆顶元素R[0]与最后一个元素R[n-1]交换,此时得到新的无序区[R0,R2,......R(n-2)]和新的有序区[R(n-1)],且满足R[0,1...n-2]<=R[n-1];
3)由于交换后新的堆顶R[0]可能违反堆的性质,因此需要对当前无序区(R0,R1,......Rn-2)调整为新堆,然后再次将R[0]与无序区最后一个元素交换,得到新的无序区(R0,R1....Rn-3)和新的有序区(Rn-2,Rn-1)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
操作过程如下:
1)初始化堆:将R[0..n-1]构造为堆;
要将初始文件R[0..n-1]调整为一个大根堆,就必须将它所对应的完全二叉树中以每一结点为根的子树都调整为堆。显然只有一个结点的树是堆,而在完全二叉树中,所有序号 i>[n/2]-1的结点都是叶子,因此以这些结点为根的子树均已是堆。这样,我们只需依次将以序号为
-1,…,0的结点作为根的子树都调整为堆即可。
2)将当前无序区的堆顶元素R[0]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。
因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。
下面举例说明:
给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。
首先根据该数组元素构建一个完全二叉树,得到
20和16交换后导致16不满足堆的性质,因此需重新调整
这样就得到了初始堆。
此时3位于堆顶不满堆的性质,则需调整继续调整
using namespace std;
void Build_heap(int *a,int size);
void Heap_adjust(int *a,int i,int size);
void Heap_sort(int *a,int size);
int main()
{
int a[100],i,size;
cout<<"Please enter the length of array:";
cin>>size;
cout<<"Please input "<<size<<" numbers: ";
for(i=0;i<size;i++)
cin>>a[i];
Heap_sort(a,size);
cout<<"The resorted array: ";
for(i=0;i<size;i++)
cout<<a[i]<<' ';
}
void Heap_adjust(int *a,int i,int size)
{
int max=i,temp;
int left_child=2*i+1; //i的左子节点序号
int right_child=2*i+2;//i的右子节点序号
if(i<size/2)//如果i是叶子就不需要进行调整
{
if(left_child<size&&a[left_child]>a[max]) max=left_child;
if(right_child<size&&a[right_child]>a[max]) max=right_child;
}
if(max!=i)
{
temp=a[max];
a[max]=a[i];
a[i]=temp;
Heap_adjust(a,max,size);//避免调整之后以max为父节点的子树不是堆
}
}
void Build_heap(int *a,int size)//建立堆
{
int i;
for(i=size/2-1;i>=0;i--)//非叶节点最大序号值为size/2 -1
Heap_adjust(a,i,size);
}
void Heap_sort(int *a,int size)//堆排序
{
int i,temp;
Build_heap(a,size);
for(i=size-1;i>=0;i--)
{
temp=a[i];//交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面
a[i]=a[0];
a[0]=temp;
Heap_adjust(a,0,i);//重新调整堆顶节点成为大顶堆
}
}