经典排序算法之堆排序

经典排序算法之堆排序

若以升序排序说明,把数组转换成最大堆积(Max-Heap Heap),这是一种满足最大堆积性质(Max-Heap Property)的二叉树:对于除了根之外的每个节点i, A[parent(i)] ≥ A[i]。

重复从最大堆积取出数值最大的结点(把根结点和最后一个结点交换,把交换后的最后一个结点移出堆),并让残余的堆积维持最大堆积性质。

最大堆积即:父节点总是大于子节点的完全二叉树
完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

若将和此序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有的非叶子节点的值均不大于(或不小于)其左右孩子节点的值。

例如:序列{96,83,27,38,11,09} 对应完全二叉树如下:

在实现堆排序前我们需要解决两个问题:

(1):如何将一个无序序列建成一个堆?

(2):如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?

function heapSort(arr) {
    function swap(x, y) {
        let tmp = arr[x];
        arr[x] = arr[y];
        arr[y] = tmp;
    }

    function maxHeap(start, end) {
        let dad = start;
        let son = dad * 2 + 1;
        // 如果有子节点则一直调整下去
        while (son <= end){
            // 找出比较大的子节点
            if (son+1 <= end && arr[son] < arr[son + 1]){
                son ++;
            }
            // 和父节点比较大小
            if (arr[dad] > arr[son]){
                return;
            } else{
                // 如果父节点小于子节点则要对整个子树进行调整
                // 交换父子节点,继续递归下一层
                swap(dad, son);
                dad = son;
                son = dad * 2 + 1;
            }
        }
    }

    let len = arr.length;
    // 建堆,从最后一个非叶子节点开始调整
    // 子节点 c = 父节点f*2 + 1 / f*2 + 2
    // 数组长度为n,最后一个节点则为n-1,则最后一个父节点为n/2 - 1
    for (let i = (len >> 1) - 1; i >= 0; i--) {
        maxHeap(i, len-1);
    }

    // 先将根节点和最后一个节点交换,然后取走根(此时为数组最后一个节点),重新建堆
    for (let i = len - 1; i > 0; i--){
        swap(0, i);
        maxHeap(0, i - 1);
    }

    return arr;
}

堆排序wiki

堆排序详解

posted @ 2019-02-27 17:26  随风的博客  阅读(209)  评论(0编辑  收藏  举报