堆排序
堆排序
1、堆的定义
堆是一个含有n个关键字{k1,k2,…,kn}的序列,且具有如下特性:
ki=k2i
且ki=k2i+1(1=i=n/2)(1)
或
ki=k2i
且ki=k2i+1(1=i=n/2)(2)
ki=k2i
满足式(1)的称为极小化堆,或极小堆,或小堆,满足式(2)的称为极大化堆,或极大堆,或大堆。本节以极小化堆为例子进行讲解。
堆与完全二叉树的关系:堆是n个元素(关键字)的序列,满足完全二叉树顺序存储中结点间的关系(双亲与子女序号间的关系)。
17,28,51,33,62,96,87,51是小顶堆
96,51,87,33,28,62,51,17是大顶堆
二叉堆
2、堆排序的基本问题
既然堆顶元素(关键字)是最小元素,所以它是排序序列的最小元素,输出后,将其它元素再调整成堆,新的堆顶元素是排序序列的第二个元素。如此下去,通过堆,可将一个无序序列变为一个有序序列。因此,堆排序的基本问题是:
(1)如何建堆
(2)如何调堆
3、如何调堆
将最后一个元素和堆顶元素交换(相当将堆顶元素输出)后,这时,从堆顶到倒数第二元素,除堆顶元素外,其余元素均符合堆的定义。下面采用筛选法,把包括堆顶元素在内的所有元素调成堆。大堆根
voidSift(RecTypeR[],inti,intm)
{‖假设R[i+1..m]中各元素满足堆的定义,本算法调整R[i]使序列
‖R[i..m]中各元素满足堆的性质
R[0]=R[i];‖暂存“根”记录R[i]
for(j=2*i;j=m;j*=2)‖j=m时,R[2i]是R[i]的左孩子
{if(jmR[j].keyR[j+l].key)j++;
‖若R[i]的右孩子存在,且关键字大于左孩子,j指向R[i]的右孩子
if(R[0].keyR[j].key)‖孩子结点关键字较大
{R[i]=R[j];‖将R[j]换到双亲位置上
i=j;‖修改当前被调整结点
}
elsebreak;‖调整完毕,退出循环
}‖for
R[i]=R[0];‖最初被调整结点放入正确位置
}‖Sift
4、如何建堆
具有n个结点的完全二叉树,其叶子结点被认为符合堆的定义,其最后一个非终端结点的编号是n/2,若从该结点开始,直到根结点,依次调用上面的筛选法,则可完成堆的建立。具体算法放在下面堆排序算法中。
5、堆排序算法
voidHeapSort(RecTypeR[],intn)
{‖对记录序列R[1..n]进行堆排序。
for(i=n/2;i0;i--)‖把R[1..n]建成大顶堆
Sift(R,i,n);
for(i=n;i1;i--)
{‖将堆顶记录和当前未经排序子序列R[1..i]中最后一个记录相互交换
R[1]←→R[i];
Sift(R,1,i-1);‖将R[1..i-1]重新调整为大顶堆
}‖for
}‖HeapSort
6、堆排序算法分析
时间复杂度为O(nlogn),只需要一个记录大小供交换用的辅助存储空间。