堆和堆排序

来源:互联网 发布:局域网流量控制软件 编辑:程序博客网 时间:2024/05/16 23:41

堆heap与内存分配中的堆是完全不同的两个概念,只是由于历史的原因才有区别。堆是一个很形象的概念,好比一堆石子,大的堆在底下,小的堆在顶上,这样才能稳定。也即堆是有序的结构,有序体现在顶层的node比底层的node在某种比较条件下小,或者说parent要比child小。而堆的无序体现在sibling之间是没有顺序的。可以用完全二叉树的线性表示来表示堆。


堆的操作有两个核心的问题,一是对于一个建好的堆,堆顶元素弹出后如何保持堆的性质,而是如何由一个无序的数组构建堆?
1.堆顶元素弹出后保持堆的性质的调整方法:交换下沉法

首先将最后一个元素放在堆顶的位置,注意,此时该堆顶元素的左右子树都是有序的堆。
然后判断两个孩子哪个小,取较小者与之交换,即将堆顶这个大石头下沉到右子树中。这时左子树仍然保持堆的结构,且堆顶元素比左右子树都小。
大石头到了右子树,一直下沉下去直到该大石头不会破会当前层的堆的性质为止。

2.由初始的无序线性结构构建堆的过程(堆排序heap sort 的过程:原地排序,仅需要交换用的一个记录的辅助空间)

设堆中有n个元素,则该元素必为叶子节点,其parent为floor(n/2). floor(n/2)后面的元素都是叶子节点,这是由完全二叉树的性质决定的。floor(n/2)后面的元素可以是无序的,因为堆的性质决定了同一层的sibling不需要有序,下面的层也不需要有序。
因此初始建堆的不需要考虑floor(n/2)后面的元素。从floor(n/2)元素开始,现在底层构建出堆的性质,然后从floor(n/2)-1,floor(n/2)-2,floor(n/2)-3的顺序依次建堆。注意,这里的思想是在考虑第i个元素时,其底层已经是建好的堆,因此问题又回到了第一个问题,即在左右子树有序的条件堆顶元素太大,应该下沉调整为有序。可见初始建堆的过程还是比较代价大,但是一旦建好了后面操作的代价就小了。而且可以初始时用较少的元素建堆,逐渐添加元素。


3.其他操作:插入元素

该操作非常简单,先将要插入的元素附在末尾,因为除了该元素,数组的其他部分已经有了堆的性质,因此只要将该元素上浮即可,一直找到一层合适的位置(即不必其parent大),而在上浮的过程中,堆的其他部分的性质没有受到影响。


4.算法复杂度分析
当n较小时堆排序的性能不佳(堆排序渐进优化,n小时低阶项起作用大),但是当n很大时可以。设完全二叉树的深度为k,设在位置h处下沉,则最坏需要下沉h-1次。若数据个数为n,则h=logn,故下降logn次。
建堆的过程自底层开始向上走,树根处得调整要下降log(n-1)次,第二层下降log(n-2)次,
因此下降的次数为log(n-1)+log(n-2)+log(n-3)+……+log2<=nlogn
因此最坏情况下也是nlogn这一点比quicksort性能好,quicksort只是平均要nlogn。

原创粉丝点击