数据结构和算法之路---堆和堆排序

1.是一种完全二叉树,第N层至少有 2 ^(n-1)个结点
2.大顶堆:父节点 大于左右子结点;小顶堆:父节点小于左右子结点
3.用一维数组来表示
4.最后一个非叶结点的下标 = length / 2 -1
   parent = floor( ( (i -1) / 2 ) i > 1
   left = i * 2+1;
   right = i * 2 + 2;

堆的用途

  • 构建优先队列
  • 快速找到一个集合的最大值或者最小值
  • 堆排序
  • 斐波那契数列,Huffman数
  • 朋友面前装逼

堆的主要方法

1.shiftUp( i ):跟父结点比较,将第i个结点往上冒,将结点在数组中的更靠前,主要用于insert
2.shiftDown( i , n) : n代表当前参与构建堆的结点数量,跟左右子结点比较,与shiftUp操作相反,主要用于remove,又称为 堆化(heapify)
shiftUp 或者 shiftDown 是一个递归的过程,所以它的时间复杂度是 O(log n),这两个是堆的基本方法,其他都是基于它们产生的
3.insert( value ):将value添加到堆尾,然后调用shiftUp调整堆
4.remove( ):移除堆顶结点,然后将堆尾结点放在堆顶,调用shiftDown调整堆

// shiftUp 
/**
 * 往上冒 (小顶堆)
 * @param arr 堆数组
 * @param i   往上冒的结点下标
 */
function shiftUp(arr, i) {
    if (i <= 0 || !arr) {
        return;
    }
    // 为交换腾出坑
    let tmpValue = arr[i];
    while (i === 1 || Math.floor((i - 1) / 2) >= 0) {
        let parent = i === 1 ? 0 : Math.floor((i - 1) / 2);
        if (arr[parent] > tmpValue) {
            arr[i] = arr[parent];
            i = parent;
        } else {
            break;
        }
    }
    arr[i] = tmpValue;
}

堆排序

1.基本思想:
   a.先将无序数组构建成大顶堆(小顶堆)— 从最后的一个非叶结点开始,从下往上循环调整每个小二叉树,使其形成局部最优解
   b.将根结点与叶结点交换位置 — 为了下次调整堆得时候,将最大(最小)的结点排除
   c.循环的重新构建一次堆 — 因为已经构成了局部最优解,这次从上往下调整,最多调整一个分支
2.选择排序,所以是不稳定的
3.O(nlogn) — 第一次构建堆,用一次for循环为O(n),以后每次循环调整 最后再遍历一个分支,即O(logn)
4.注:堆排序的主要应用并不是全排(快速排序更方便)更适用最优N解得问题,或者动态的数据;
          对于已经构建好的堆,主要有两个操作,插入和删除
          插入:插入到堆尾,然后从下往上调整堆
          删除:将堆顶移动到堆尾,然后从上往下调整堆

// 堆排序
function heapSort(arr) {
    if (!arr || typeof arr !== "object" || arr.constructor !== Array) {
        return;
    }
    // 最后一个非叶节点的下标
    let i = Math.floor(arr.length / 2) - 1;
    // 构建大堆序
    for (; i >= 0; i--) {
        adjustHeap(arr, i, arr.length);
    }
    // 调换堆顶和结点位置,再从上往下调整堆
    for (let j = arr.length - 1; j > 0; j--) {
        let temp = arr[0];
        arr[0] = arr[j];
        arr[j] = temp;
        adjustHeap(arr, 0, j)
    }
}

/**
 * 调整堆结构
 * 从左往右,从下到上
 */
function adjustHeap(arr, i, length) {
    let temp = arr[i];
    for (let k = 2 * i + 1; k < length; k = 2 * k + 1) {
        if (k + 1 < length && arr[k] > arr[k + 1]) {
            k++;
        }
        if (arr[k] < temp) {
            arr[i] = arr[k];
            i = k;
        } else {
            break;
        }
    }
    arr[i] = temp;

}

let arr = [12, 34, 2, 34, 5, 1, 0, 2, 5, 8, 9, 6, 3, 2, 33, 3, 3, 3, 5, 6, 8];
heapSort(arr);
console.log(arr);

堆代码实现

用JS 手动实现了堆结构,以及主要方法

/**
 * 堆结构(小顶堆为例)
 */
class Heap {
    constructor(arr) {
        this.arr = arr;
    }

    get size() {
        if (!this.arr || typeof this.arr !== "object" || this.arr.constructor !== Array) {
            return 0;
        }
        return this.arr.length;
    }

    /**
     * 将i位置的元素往上冒
     * @param i
     */
    shiftUp(i) {
        if (this.size < 0) {
            return;
        }
        let tempValue = this.arr[i];
        while (i === 1 || Math.floor((i - 1) / 2) >= 0) {
            let parent = i === 1 ? 0 : Math.floor((i - 1) / 2);
            if (this.arr[parent] > tempValue) {
                this.arr[i] = parent;
                i = parent;
            } else {
                break;
            }
        }
        this.arr[i] = tempValue;
    }

    /**
     * 往下沉  (堆化heapify)
     * @param i
     */
    shiftDown(i, size) {
        let length = size || this.size;
        if (i < 0) {
            return;
        }
        let tempValue = this.arr[i];
        let k = i * 2 + 1;
        while (k < length) {
            if (k + 1 < length && this.arr[k] > this.arr[k + 1]) {
                k++;
            }
            if (this.arr[k] < tempValue) {
                this.arr[i] = this.arr[k];
                i = k;
                k = 2 * k + 1;
            } else {
                break;
            }
        }
        this.arr[i] = tempValue;
    }

    /**
     * 插入新元素
     * 放到堆尾,然后往上冒
     * @param value
     */
    insert(value) {
        this.arr.push(value);
        this.shiftUp(this.size - 1);
    }

    /**
     * 删除堆顶元素
     * 1.移除堆顶
     * 2.将堆尾的结点移到堆顶
     * 3.往下沉
     */
    remove() {
        if (this.size <= 0) {
            return;
        }
        if (this.size === 1) {
            return this.arr.splice(0, 1);
        } else {
            let firstValue = this.arr[0];
            this.arr[0] = this.arr[this.size - 1];
            this.arr.splice(this.size - 1, 1);
            this.shiftDown(0);
            return firstValue;
        }
    }

    /**
     * 堆排序
     * 1.先构建堆
     * 2.再循环remove堆顶
     * @param heap
     */
    sort() {
        Heap.createHeap(this);
        for (let j = this.size - 1; j > 0; j--) {
            let temp = this.arr[0];
            this.arr[0] = this.arr[j];
            this.arr[j] = temp;
            this.shiftDown(0, j);
        }

    }

    /**
     * 构建堆
     *
     * @param heap
     */
    static createHeap(heap) {
        let arr = heap.arr;
        if (!arr || typeof arr !== "object" || arr.constructor !== Array) {
            return false;
        }
        //从最后一个非叶结点开上往上堆化
        let node = Math.floor(arr.length / 2) - 1;
        while (node >= 0) {
            heap.shiftDown(node);
            node--;
        }
        return true;
    }
}

let arr = [12, 34, 2, 34, 5, 1, 0, 2, 5, 8, 9, 6, 3, 2, 33, 3, 3, 3, 5, 6, 8];

let heap = new Heap(arr);
Heap.createHeap(heap);
// heap.sort();
console.log(heap.arr);
// heap.remove();
heap.insert(-1);
console.log(heap.arr);

最优队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值