堆排序

堆排序

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),只需要一个记录大小供交换用的辅助存储空间。