堆排序
堆
逻辑定义
n个关键字序列
{k1,k2...ki...kn}
,当且仅当满足下列关系时称之为堆:
(最小堆:
ki<=k2i,ki<=k2i+1
)或者(最大堆:
ki>=k2i,ki>=k2i+1
)
若将此序列所存储的向量R[1…n]看作是一颗完全二叉树的存储结构,则堆实际上是满足如下性质的完全二叉树:树中任一非叶节点的关键字均不大于(或不小于)其左右孩子(若存在)节点的关键字
性质
- 任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)
- 堆总是一颗完全树。即除了最底层,其它层的节点都被关键字填满,且最底层尽可能地从左到右填入
- 每个节点的左子树或右子树都是一个堆
堆排序
特点
- 堆排序是一种树形选择排序
- 在排序过程中,将R[1..n]看成一颗完全二叉树的顺序存储结构们利用完全二叉树中双亲节点和孩子节点之间内在的关系,在当前无序中选择关键字最大(或最小)的记录
与直接选择排序区别
直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,又许多比较课能在前面的n-1次比较中已经做过,但由于前一趟排序未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
堆排序可以通过树形结构保存部分比较结果,可减少比较次数。
基本思想(最大堆)
- 先将向量R[1..n]建成一个大根堆,此堆为初始的无序区
- 将关键字最大的记录R[1](堆顶)和无序区的最后一个记录交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys ≤ R[n].key
- 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]与该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys ≤ R[n-1..n].keys,同样要将R[1..n-2]调整为堆
……
直到无序区只有一个元素为止。
基本操作
- 初始化操作:将R[1..n]构造为初始堆
- 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称为重建堆)
算法实现
python
def minChildIndex(heapList, i, right):
leftIndex = i * 2
rightIndex = leftIndex + 1
if leftIndex > right:
return None
elif rightIndex > right:
return leftIndex
elif heapList[leftIndex] > heapList[rightIndex]:
return leftIndex
else:
return rightIndex
def percDown(heapList, i, right):
if (heapList is None) or (i < 0) or right < i:
raise Exception("Illegal arguments")
while i * 2 <= right:
mc = minChildIndex(heapList, i, right)
if mc is None:
return
if heapList[i] < heapList[mc]:
heapList[i],heapList[mc] = heapList[mc], heapList[i]
i = mc
def buildHeap(heapList, right=None):
if right is None:
right = len(heapList) - 1
i = right+1 // 2
while i > 0:
percDown(heapList, i, right)
i = i - 1
def heapsort(data):
heapList = [0] + data[:]
right = len(heapList) - 1
buildHeap(heapList, right)
while right > 1:
heapList[1], heapList[right] = heapList[right], heapList[1]
nextRight = right - 1
print("%s"%heapList)
buildHeap(heapList, nextRight)
right = nextRight
return heapList[1:]
def runTest():
heapList = list(range(0,100,1))
print("before build heap:%s"%heapList)
buildHeap(heapList)
print("after build heap:%s"%heapList)
data = heapList[1:]
sortedData = heapsort(data)
print("result: %s"%sortedData)
if __name__ == "__main__":
runTest()
JavaScript
"use strict";
function minChildIndex(heapList, i, right){
var leftIndex = i * 2, rightIndex = leftIndex + 1;
if(leftIndex > right){
return;
}else if(rightIndex > right){
return leftIndex;
}else if(heapList[leftIndex] > heapList[rightIndex]){
return leftIndex;
}else{
return rightIndex;
}
}
function percDown(heapList, i, right){
while(i * 2 <= right){
var mc = minChildIndex(heapList, i, right);
if(!mc){
return;
}else if(heapList[i] < heapList[mc]){
[heapList[i],heapList[mc]] = [heapList[mc], heapList[i]];
}
i = mc;
}
}
function buildHeap(heapList, right){
if(typeof right !== "number"){
right = heapList.length - 1;
}
var i = Math.floor((right + 1)/2);
while(i > 0){
percDown(heapList, i, right);
i--;
}
}
function heapsort(data){
var heapList = [0];
Array.prototype.push.apply(heapList, data);
var right = heapList.length - 1;
buildHeap(heapList, right);
while(right > 1){
[heapList[1], heapList[right]] =[heapList[right], heapList[1]];
var nextRight = right - 1;
buildHeap(heapList, nextRight);
right = nextRight;
}
return heapList.slice(1);
}
function runTest(){
var heapList = [...Array(10).keys()];
buildHeap(heapList);
var data = heapList.slice(0);
var sortedData = heapsort(data);
console.info(sortedData);
}
runTest();