排序之堆排序 | 刷题打卡

435 阅读2分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

一、题目描述

使用堆排序对一个非有序数组进行升序的排序。

示例:

输入:[5, 8, 6, 3, 4, 7, 1, 2]
输出:[1, 2, 3, 4, 5, 6, 7, 8]

二、思路分析

堆排序实际上就是利用,最大堆和最小堆的特性,来对数组进行排序。

最大堆: 即代表堆顶的元属是该堆里面值最大的元属

最小堆: 即代表堆顶的元属是该堆里面值最小的元属

实际上我们只需要利用最大最小堆的特性,就能轻易地对数组进行排序。

           5          2         4         1        3         2        1
          / \        / \       / \       / \      / \       / \      / \
         3   4  ->  3   4 ->  3   2 ->  3   2 -> 1   2 ->  1   3 -> 2   3
        /  \       / \       / \       / \      / \       / \      / \
       1   2      1   5     1   5     4   5    4   5     4   5    4   5

三、AC 代码

Array.prototype.heapSort = function () {
    /**
     * 下沉调整
     * @param { Array } array 待调整的堆
     * @param { Number } parentIndex 要 下沉 的父节点下标
     * @param { Number } length 堆的有效大小 
    */
    function downAdjust(array, parentIndex, length) {
        // 保存父节点用于最后的赋值
        const temp = array[parentIndex]
        
        // 获取子节点的下标 - 左子节点
        let childIndex = 2 * parentIndex + 1
           
        // 边界判断
        while(childIndex < length) {
        
            // 判断右子节点的值是否大于左子节点。
            // 如果是,子节点的下标转换成右子节点 
            if(childIndex + 1 < length && array[childIndex + 1] > array[childIndex]) {
                childIndex++
            }
            
            // 如果父节点大于子节点,则直接跳出
            if(temp >= array[childIndex]) {
                break;
            }
            
            // 将子节点的值移交给父节点,继续向下,下沉
            array[parentIndex] = array[childIndex]
            parentIndex = childIndex
            childIndex = 2 * childIndex + 1
        }
        
        // 赋值
        array[parentIndex] = temp
    }

    /**
     * 堆排序 - 升序
     * @param { Array } array 待调整的堆
    */
    function heapSort(array) {
        // 构建最大堆
        for(let i = (array.length - 2) / 2; i >= 0; i--) {
            downAdjust(array, i, array.length)
        }
        
        // 循环删除堆顶的元素,移到集合尾部,调整堆产生新的堆顶
        for (let i = array.length - 1; i > 0; i--) {
            // '最后'的元素 和 堆顶的元素进行交换
            const temp = array[i]
            array[i] = array[0]
            array[0] = temp
            
            // 下沉
            downAdjust(array, 0, i)
        }
    }
    
    heapSort(this)
}

const arr = [5, 8, 6, 3, 4, 7, 2, 1]
arr.heapSort()
console.log(arr) // [ 1, 2, 3, 4, 5, 6, 7, 8 ]

四、总结

排序在项目当中是非常常见的场景,所以排序算法是每个程序员都必须掌握并且熟练运用的算法之一。

首先需要把无序数组构建成二叉堆(数组表示法)。 需要从小到大排序(升序), 则构建成最大堆; 需要从大到小排序(降序),则构建成最小堆;

循环删除堆顶的元素,替换到二叉堆的末尾,调整堆产生新的堆顶即可。

需要注意的是,如果在数组原有基础上修改,在下沉的时候,要手动设置堆的末尾边界,来防止对转移下来的元素重新进入二叉堆,如果用额外的数组来存储,则可以不需要并且改用最小堆(使用 push),当然最大堆(使用 unshift)也是可以的。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情