读前声明:本人所写帖子主要为了记录本人学习的一个过程,无他想法,由于内容比较肤浅,如有雷同,非常正常!!!
本文内容:
堆排序,堆排序像合并排序而不像插入排序,堆排序的运行时间是O(nlgn),但是又像插入排序而不像合并排序,这是因为它是一种原地(in place)排序算法:在任何时候,数组中只有常数个元素存储在输入数组以外,这样,堆排序就将前面提到的两种排序算法的优点结合起来。
堆排序还引入一种数据结构——堆,下面对堆简单介绍:
如果有一个关键码的集合K={k0,k1,k2,...,kn-1},把它的所有元素按照完全二叉树的顺序存储方式存放在一个一维数组中。并且满足
ki<=k2i+1且ki<=k2i+2(或者ki>=k2i+1且ki>=k2i+2) i=0,1,2,...,[(n-2)/2]
则称这个集合为最小堆(最大堆)。
上图所示即为堆的组成,由于在编程应用过程中,堆存储在下标从0开始计数的数组中,因此,在堆中给定下标为i的结点时:
1)如果i=0,结点i为根节点,无父节点;否则结点i的父节点为结点[(i-1)/2];
2)如果2i+1>n-1,则结点i无左子女;否则结点i的左子女为结点2i+1;
3)如果2i+2>n-1,则结点i无右子女;否则结点i的右子女为结点2i+2;
代码如下:
//计算父节点位置
int Parent(int i)
{
if (i==0)
{
return 0;
cout<<"当前为根节点"<<endl;
}
else return (i-1)/2;
}
//计算左子结点位置
int Left(int i)
{
return 2*i+1;
}
//计算右子节点位置
int Right(int i)
{
return 2*i+2;
}
保持堆的性质
当堆中出现某个结点不符合堆的性质时,需要对堆进行调整,下面的Max_HeapIfy是对最大堆操作的重要子程序,当Max_HeapIfy被调用时,我们假定当前下标i的左子女和右子女为根的两棵二叉树都是最大堆,但这时a[i]可能小于其子女,违反了最大堆性质。Max_HeapIfy让a[i]在最大堆中下降,使以i为根的子树成为最大堆。
//维持最大堆算法
void Max_HeapIfy(int a[],int i,int heapsize)
{
heapsize = heapsize - 1;
int l = Left(i);
int r = Right(i);
int largest;
if (l<=heapsize&&a[l]>a[i])
{
largest = l;
}
else
{
largest = i;
}
if (r<=heapsize&&a[r]>a[largest])
{
largest = r;
}
if (largest!=i)
{
int temp = a[largest];
a[largest] = a[i];
a[i] = temp;
Max_HeapIfy(a,largest,heapsize);
}
}
建立堆
我们可以自底向上地用Max_HeapIfy将一个数组a[0,...,n-1]变成一个最大堆
//建立最大堆
void BuildMaxheap(int a[],int length)
{
int hs = length-1;
for (int i=(length/2);i>=0;i--)
{
Max_HeapIfy(a,i,hs);
}
}
堆排序
利用堆及其运算,可以很容易选择排序的思路,可分为两个步骤:第一步,根据初始输入数据,利用堆的调整方法形成初始堆;第二步,通过一系列的元素交换和重新调整堆进行排序,其代码如下:
//堆排序
void HeapSort(int a[],int length)
{
BuildMaxheap(a,length);
for (int i=(length-1);i>0;i--)
{
int temp = a[i];
a[i] = a[0];
a[0] = temp;
length--;
Max_HeapIfy(a,0,length);
}
}