堆排序
堆
堆是一个数组,可以被近似的看成一个完全二叉树。树上的每一个节点对应数组中的一个元素。除了最底层外该树是完全被充满的且是从左向右填充的。存储堆的数组A应具有两个属性A.length即数组元素的个数和A.heap-size即A中堆元素的个数,事实上在数组A中只有A[1…heap-size]是堆的有效元素而参数A.heap-size的取值范围在1与A.length之间。树的根节点为A[1]。对于堆中元素A[i]可以计算出其父节点与左右孩子节点的位置
Parent(i)
return i/2
Left(i)
return 2*i
Right(i)
return 2*i+1
堆的性质
二叉堆可以分最大堆和最小堆两种。在两种堆中节点的值都要满足堆的性质,而在细节上有一些差别。
-
最大堆
最大堆的性质是除根结点外的所有节点都要满足父节点的值不小于子节点的值的特点
A[Parent(i)]>=A[i]
所以堆中最大元素存储在根节点中
-
最小堆
最小堆的性质是除了根节点外所有节点都满足父节点不大于子节点
A[Parent(i)]<=A[i]
最小堆根节点中存储的是堆中最小的元素
通常的将最大堆用于排序序列,最小堆用于优先队列算法。
堆的性质的维护
在排序或优先队列中,我们总会在过程中将堆的根节点的元素从堆中取出。此时堆的性质遭到破坏,所以我们在每一次取出根元素后要对堆的性质进行维护。
Max-Heapify(A,i)
l=Left(i) 确认i节点的孩子节点
r=Right(i)
if l<=A.heap-size && A[l]>A[i] 在i与其孩子节点中找到最大的节点
largest = l
else largest = i
if r<=A.heap-size && A[r]>A[largest]
largest = r
if largest != i
exchange A[i] with A[largest] 交换i与其孩子中最大的节点的值
Max-Heapify(A,largest) 在交换后以largest为根节点的子树有可能破坏堆的性质
建堆
可以使用自底向上的方法利用过程 Max-Heapify把一个长度为n=A.length的数组转换为最大堆
Build-Max-Heap(A)
A.heap-size = A.length 将数组A转换成一个最大堆,堆的有效元素即为这个数组的全部元素
for i = A.length/2 downto 1 因为n/2+1..n都为叶子节点所以迭代从n/2向下自减
Max-Heapify(A,i)
经过该算法将数组A塑造成一个最大堆
堆排序
堆排序算法,先将数组A塑造成一个最大堆,根据最大堆的性质根节点A[1]中为堆中最大的值,将A[1]取出在伪代码中采取将A[1]与A[n]交换再将A.heap-size减一的操作来实现。在对新的堆进行性质的维护。堆新的最大堆重复之前的操作排序完后会形成一个从小到大的有序数组
HeapSort(A)
Build-Max-Heap(A) 建堆,将数组A转换成一个最大堆
for i = A.length downto 2 每一次循环都会取走最大的一个元素当循环A.length-2次后只有两个元素之后仅需一次 循环即可完成排序
将根节点元素取出
exchange A[1] with A[i]
A.heap-size = A.heap-size - 1
维护新堆的性质
Max-Heapiyf(A,1)