算法学习记录(五)

104 阅读2分钟

堆排序

问:

  1. 堆结构

  2. 堆排序

  3. 已知存在一个数组,它的每一个元素最多移动不超过K个位置就能到正确的位置。如何排序 解:

  4. 将一个数组按顺序看作完全二叉树,就是一个堆结构。根节点的关键字既大于或等于左子树的关键字值,又大于或等于右子树的关键字值,称为大根堆

  5. 将数组看作堆结构。先将这个长度为heapSize的数组转为大根堆,把根节点与最后一个节点交换位置,我们就将把堆的最大值放到了正确的位置,然后将heapSize--。现在将换到根节点的值与它的左右子节点(如果存在的话)中较大的比较,若根节点较小,就把大的子节点换上来并且递归这个操作,节点的下标不得超出heapSize。当递归结束,就会再次得到大根堆。

    const arr = [3,21,31,235,435,43,3,4,12,]

    // 转为大根堆结构
    function heapInsert(idx) {
        while (arr[idx] > arr[Math.floor((idx - 1) / 2)]) {
            // 若当前值大于自己的父节点就和父节点交换位置
            [arr[idx],arr[Math.floor((idx - 1) / 2)]] = [arr[Math.floor((idx - 1) / 2)],arr[idx]]
            idx = Math.floor((idx - 1) / 2)
        }
    }
    function heapfy(idx, heapSize) { // 找出子节点中较大的一个与当前节点比较,若小于子节点就交换位置
        let targetIdx = idx
        let leftNodeIdx = 2 * idx + 1
        // 从三个节点中,选出最大节点
        if (leftNodeIdx < heapSize && arr[leftNodeIdx] > arr[idx]) targetIdx = leftNodeIdx
        if (leftNodeIdx + 1 < heapSize && arr[leftNodeIdx + 1] > arr[targetIdx]) targetIdx = leftNodeIdx + 1
        // 交换位置
        if (idx !== targetIdx) {
            [arr[idx], arr[targetIdx]] = [arr[targetIdx], arr[idx]]
            heapfy(targetIdx, heapSize)
        }
    }
    function heapSort() {
        let heapSize = arr.length -1
        // 遍历数组,转为大根堆
        for (let i =0; i< arr.length; i++) {
            heapInsert(i);
        }
        // 将大根堆根节点与最后一位交换位置,然后将这个最大值移出堆结构
        while (heapSize > 0) {
            [arr[0], arr[heapSize]] = [arr[heapSize], arr[0]]
            heapSize--
            heapfy(0, heapSize)
        }
    }
    heapSort()
  1. 把原数组从0开始截取K+1长度,转为小根堆,然后根节点就是最小值,将根节点放入结果数组,将原数组的下一位纳入堆解构转为小根堆。
    const arr = [3,7,6,5,2,4,6,8,5]
    const k = 5
    function heapInsert(idx, arr) {
        while (arr[idx] < arr[Math.floor((idx - 1) / 2)]) {
            // 若当前值小于自己的父节点就和父节点交换位置
            [arr[idx],arr[Math.floor((idx - 1) / 2)]] = [arr[Math.floor((idx - 1) / 2)],arr[idx]]
            idx = Math.floor((idx - 1) / 2)
        }
    }
    function heapSort (arr, k) {
        // 先把数组的前k位截取出来
        const heapArr = arr.splice(0, k)
        // 结果数组
        const res = []
        // 遍历数组,转为小根堆
        while (heapArr.length > 0) {
            for (let i = 0; i < heapArr.length; i++) {
                heapInsert(i, heapArr);
            }
            // 小根堆根节点放入结果数组
            res.push(heapArr.shift())
            // 将原数组的第一位再加入堆
            arr.length && heapArr.push(arr.shift())
        }
        return res
    }
    heapSort(arr, k)