算法学习笔记--堆排序

堆排序

逻辑定义

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次比较中已经做过,但由于前一趟排序未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
堆排序可以通过树形结构保存部分比较结果,可减少比较次数。

基本思想(最大堆)

  1. 先将向量R[1..n]建成一个大根堆,此堆为初始的无序区
  2. 将关键字最大的记录R[1](堆顶)和无序区的最后一个记录交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys ≤ R[n].key
  3. 由于交换后新的根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]调整为堆
    ……
    直到无序区只有一个元素为止。

基本操作

  1. 初始化操作:将R[1..n]构造为初始堆
  2. 每一趟排序的基本操作:将当前无序区的堆顶记录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();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值