堆排序算法
追随大师思想,提升自我素养。
堆排序算法
Any noun can be verbed.
—Alan J.Perlis
〇、关于
0.1 堆排序算法是由J.Williams发明的,构造堆的线性时间算法是由Robert W.Floyd发明的。Floyd还提出了先下沉后上浮的优化方法。
一、二叉堆概念
1.1 结构性:
二叉堆是一个数组,它可以被看成是一个完全二叉树,树上的每一个结点对应数组中的一个元素,除了最底层,该树是完全充满的。最底层的元素由从左向右填入。
表示堆的数组A包含两个属性:
A.Length(通常)给出数组元素的个数;
A.Heapsize表示有多少个堆元素存储在堆的有效数组;
一般0≤A.Heapsize≤A.Length。
1.2 堆序性:
在最大堆中,最大堆性质是指除了根以外的所有结点i都要满足:
A[Parent[i]]≥A[i]
即某个结点的值至多与其父结点一样大,堆中的最大元素存放在根结点中;并且,在任一子树中,该子树所包含的所有结点的值都不大于该子树根结点的值。
最小堆性质是指除了根以外的所有结点i都有
A[Parent[i]]≤A[i]
最小堆中的最小元素存放在根结点中。
在堆排序算法中,我们使用最大堆,最小堆通常用于构造优先队列。
二、二叉堆算法
2.1 维护堆的性质 2.1.1 由下而上的对有序化(上浮)
如果堆的有序状态因为某个结点变得比它的父结点更大而被打破,就通过交换它和它的父结点来修复堆。交换后,这个结点比它的两个子结点都大(一个是曾经的父结点,另一个比它小,因为它是曾经的父结点的子结点),但这个结点仍然可能比它现在的父节点更大,我们可以一遍遍地用同样的办法恢复秩序,将这个结点不断向上移动直到我们遇到一个更大的父节点。
2.1.2 由上至下的堆有序化(下沉)
如果堆的有序状态因为某个结点变得比它的两个子结点或是其中之一而被打破了,那么可以通过交换它和它的两个子结点中的较大者来修复堆。交换可能会在子结点处继续打破堆的有序状态,因此需要不断地用相同的方式将其修复,将结点向下移动直到它的子结点都比它更小或是到达堆的底部。
2.1.3 应用
插入元素:将新元素加到数组末尾,增加堆的大小(heapsize)并让这个新元素上浮到合适的位置。
删除最大元素:从数组顶端删去最大的元素并将元素的最后一个元素放到顶端,减小堆的大小并让这个元素下沉到合适的位置。
2.2 建堆
通过使用自底向上的方法对0到⌊A.Length/2⌋的元素利用上浮过程,可以把一个大小为n=A.Length的数组A[0…n]转换为最大堆。子数组A[⌊n/2⌋+1…n]中的元素都是树的叶结点。每个叶结点都可以看成只包含一个元素的堆。
BuildMaxHeap(A)
1 A.heapsize=A.Length
2 for i =⌊A.Length/2⌋downto 0
3 Swim(i)
建堆算法的时间复杂度:O(n)。
2.3 堆排序算法
初始时候,堆排序算法利用BuildMaxHeap将输入数组A[0…n]建成最大堆,其中n=A.Length。因为数组中的最大算法总在根结点A[0]中,通过将它与A[n]进行交换,可以让该元素到正确的位置,这时候,如果从堆中去掉结点n(减小A.heapsize的值),剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根结点可能会违背最大堆的性质,为了维护最大堆的性质,调用下沉过程修复堆,从而在A[0…n-1]上构造一个新的最大堆。堆排序算法会不断重复这一过程,直到堆的大小从n-1降到1。
三、实现(C#)
3.1 细节
因为数组的起始下标为0,需要注意其子节点与父节点的实现方式与CLRS中伪代码的实现方式有所不同;
3.2 泛型实现
类声明
public class MaxBinaryHeap : IEnumerable where T : IComparable{
///Implements
}
通过使用 IEnumerable泛型接口,可实现堆中元素的遍历;
通过对泛型参数使用IComparable约束,可对基础类型和实现了该接口的自定义类型调用该方法。
3.3 容器数组
容器数组可实现为可变数组或使用List。
3.4 实现(C#)
略—
四、大师
4.1 Robert W.Floyd是1978年ACM图灵奖的获得者,是一位从文学转行计算机的自学成才的计算机科学家。
4.2 勤勉致知。
五、资料引用
4.1 《算法导论》
4.2 《算法》Robert Sedgewick,Kevin Wayne.
4.3 《数据结构与算法分析》Mark Allen Weiss.