优先队列"笨笨的"解题方案

129 阅读3分钟

其实刷到这种类型题的时候,我刚开始确实不知道怎么才是一样比较快的解决方案。在刷完题之后才会知道一种优先队列的方案,可以做我的刷题方案。那么这里对于队列和优先队列不在赘述,分享一篇介绍的不错的文章

这里只说两个核心的操作:

  • insert: 即将元素插入到优先队列中(入队)
  • deleteMin(删除最小者) ,它的作用是找出、删除优先队列中的最小的元素(出队),我下面会定义为 pop

优先队列(堆)的基本实现

最开始的实现:

class MinHeap {
    constructor() { 
        this.heap = []; 
    }
}

交换节点位置

swap(i1, i2) {
        [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
    }

寻找父节点的位置

 //父节点index
getFatherIndex(index) {
    return (index - 1) >> 1
}

寻找左子树节点的位置

// 左子树index
getLeftIndex(index) {
    return (index * 2) + 1;
}

寻找右子树节点的位置

// 右子树
getRightIndex(index) {
    return (index * 2) + 2;
}

上浮: 这里就是一种比较方式, 我们需要对插入底部的元素依次比较重新建堆

shiftUp(index) {
    // 首先会判断一下 边界条件
    if (index === 0) return;
    // 拿到父节点
    let fatherIndex = this.getFatherIndex(index);

    if (this.heap[fatherIndex] && this.heap[fatherIndex].val > this.heap[index].val) {
        this.swap(fatherIndex, index);  // 可以画图解决
        // 继续
        this.shiftUp(fatherIndex);
    }
}

下沉: 每次在堆顶取走一个元素之后, 需要从尾部拿来一个元素重新比较建堆

shiftDown(index) {
    let leftIndex = this.getLeftIndex(index);
    let rightIndex = this.getRightIndex(index);

    // 左
    if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
        this.swap(leftIndex, index);
        // 继续
        this.shiftDown(leftIndex)
    }

    // 右
    if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
        this.swap(rightIndex, index);
        // 继续
        this.shiftDown(rightIndex)
    }
}

插入元素:


insert(value) {
    this.heap.push(value);
    // this.shiftUp(this.heap[this.heap.length-1]) 填索引
    this.shiftUp(this.heap.length-1);
}

删除元素:

pop() {
    // 特殊情况处理 直接优化了
    if (this.size() === 1) return this.heap.shift();

    let top = this.heap[0];
    // 这里是 需要重新调整 忘记了
    this.heap[0] = this.heap.pop()
    this.shiftDown(0);
    return top;
}

取堆顶元素:

peek() {
   return  this.heap[0]
}

获取堆的长度

size() {
    return this.heap.length;
}

这里完整的组合起来就是我们想要的小根堆,这里是依据链表结构建的堆,使用需要做一些修改

// 这里小根堆为例

class MinHeap {
    constructor() {
        this.heap = [];
    }

    swap(i1, i2) {
        [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
    }

    //父节点index
    getFatherIndex(index) {
        return (index - 1) >> 1
    }

    // 左子树index
    getLeftIndex(index) {
        return (index * 2) + 1;
    }

    // 右子树
    getRightIndex(index) {
        return (index * 2) + 2;
    }

    // 上浮
    shiftUp(index) {
        // 首先会判断一下 边界条件
        if (index === 0) return;
        // 拿到父节点
        let fatherIndex = this.getFatherIndex(index);
        
        if (this.heap[fatherIndex] && this.heap[fatherIndex].val > this.heap[index].val) {
            this.swap(fatherIndex, index);  // 可以画图解决
            // 继续
            this.shiftUp(fatherIndex);
        }
    }

    // 下沉
    shiftDown(index) {
        let leftIndex = this.getLeftIndex(index);
        let rightIndex = this.getRightIndex(index);

        // 左
        if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
            this.swap(leftIndex, index);
            // 继续
            this.shiftDown(leftIndex)
        }

        // 右
        if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
            swap(rightIndex, index);
            // 继续
            this.shiftDown(rightIndex)
        }
    }

    // 插入
    insert(value) {
        this.heap.push(value);
        // this.shiftUp(this.heap[this.heap.length-1]) 填索引
        this.shiftUp(this.heap.length-1);

    }

    // 删除
    pop() {
        // 特殊情况处理 直接优化了
        if (this.size() === 1) return this.heap.shift();

        let top = this.heap[0];
        // 这里是 需要重新调整 忘记了
        this.heap[0] = this.heap.pop()
        this.shiftDown(0);
        return top;
    }

    peek() {
       return  this.heap[0]
    }

    size() {
        return this.heap.length;
    }
}

703. 数据流中的第 k 大个元素

image.png

代码实现:

class MinHeap {
    constructor() {
        this.heap = [];
    }

    getFatherIndex(index) {
        return (index - 1) >> 1;
    }

    getLeftIndex(index) {
        return index * 2 + 1;
    }

    getRightIndex(index) {
        return index * 2 + 2;
    }

    swap(i1, i2) {
        [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
    }

    // 上浮
    shiftUp(index) {
        // 找 父节点
        if (index === 0) return;

        let fatherIndex = this.getFatherIndex(index);
        if (this.heap[fatherIndex] > this.heap[index]) {
            this.swap(fatherIndex, index);
            // 继续
            this.shiftUp(fatherIndex);
        }
    }

    // 下沉
    shiftDown(index) {
        let leftIndex = this.getLeftIndex(index);
        let rightIndex = this.getRightIndex(index);

        // 左边
        if (this.heap[leftIndex] < this.heap[index]) {
            this.swap(leftIndex, index);
            this.shiftDown(leftIndex);
        }

        // 右边
        if (this.heap[rightIndex] < this.heap[index]) {
            this.swap(rightIndex, index);
            this.shiftDown(rightIndex);
        }
    }

    // 删除
    pop() {
        // let top = this.heap[0];
        this.heap[0] = this.heap.pop();
        // 从头交换
        this.shiftDown(0);
        // return top;
    }

    // 插入
    insert(value) {
        this.heap.push(value);
        // 重新整理 从尾部
        this.shiftUp(this.heap.length-1);
    }

    // top
    peek() {
        return this.heap[0];
    }

    // size
    size() {
        return this.heap.length;
    }
}

var KthLargest = function(k, nums) {
    this.k = k;
    this.minHeap = new MinHeap();
    nums.forEach((item) => {
         
    })
}

KthLargest.prototype.add = function(val) {
    this.minHeap.insert(val);
    // 多了拿走
    if (this.minHeap.size() > this.k) {
        this.minHeap.pop();
    }
    // 此时拿到的就是 第K大的元素
    return this.minHeap.peek();
};

215. 数组中的第K个最大元素

image.png

代码实现:

class MinHeap {
    // construcrtor() {
    //     this.heap = [];
    // }  拼错了
    constructor() {
        this.heap = [];
    }

    // 寻找父节点 index
    getFather(index) {
        let fatherIndex = (index - 1) >> 1;
        return fatherIndex
    }

    // 寻找 左子树 index
    getLeftIndex(index) {
        let leftIndex = index * 2 + 1;
        return  leftIndex;
    }

    // 寻找 右子树 节点index
    getRightIndex(index) {
        let rightIndex = 2 * index + 2;
        return  rightIndex;
    }

    swap(i1, i2) {
        [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]]; // 这里写错了 l1
    }

    // 上浮
    shiftUp(index) {
        // 临界点的判断
        if (index === 0) return;
        // 先拿到 父节点
        let fatherIndex = this.getFather(index);
        // 移动
        if (this.heap[fatherIndex] > this.heap[index]) {
            this.swap(fatherIndex, index);
            // 继续移动
            this.shiftUp(fatherIndex);
        }
    }

    // 下沉
    shiftDown(index) {
        // 找左右 
        let leftIndex = this.getLeftIndex(index);
        let rightIndex = this.getRightIndex(index);

        // 分别比较左右节点  error 在于这数组不是链表
        if (this.heap[leftIndex] < this.heap[index]) {
            this.swap(leftIndex, index);
            // 还没完
            this.shiftDown(leftIndex);
        }

        // 右
        if (this.heap[rightIndex] < this.heap[index]) {
            this.swap(rightIndex, index);
            // 还没完
            this.shiftDown(rightIndex);
        }
    }

    // 插入节点
    insert(value) {
        console.log(value)
        this.heap.push(value)
        // 上浮  填索引
        this.shiftUp(this.heap.length - 1);
    }

    // 少了个删除 堆顶元素 没记住
    pop() {
        // if (this.size() === 1) return this.heap.shift();
        // let top = this.heap[0];
        // 此时 要拿最后一个 扔到第一个
        this.heap[0] = this.heap.pop();
        // 调整从堆顶开始重新排序 index = 0;
        this.shiftDown(0)
        // return top;
    }

    peek() {
        // 没有 return
        return this.heap[0];
    }

    size() {
        return this.heap.length;
    }
}

var findKthLargest = function(nums, k) {
    // 把所有元素放到堆里面
    
    let heap = new MinHeap();
    for (let i of nums) {
        if (heap.size() > k) {
            heap.pop()
        }
    }
    
    return heap.peek()
}
23. 合并k个升序链表

image.png

实现思路:

代码实现:

class MinHeap {
    constructor() {
        this.heap = [];
    }

    swap(i1, i2) {
        [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
    }

    //父节点index
    getFatherIndex(index) {
        return (index - 1) >> 1
    }

    // 左子树index
    getLeftIndex(index) {
        return (index * 2) + 1;
    }

    // 右子树
    getRightIndex(index) {
        return (index * 2) + 2;
    }

    // 上浮
    shiftUp(index) {
        // 首先会判断一下 边界条件
        if (index === 0) return;
        // 拿到父节点
        let fatherIndex = this.getFatherIndex(index);
        
        if (this.heap[fatherIndex] && this.heap[fatherIndex].val > this.heap[index].val) {
            this.swap(fatherIndex, index);  // 可以画图解决
            // 继续
            this.shiftUp(fatherIndex);
        }
    }

    // 下沉
    shiftDown(index) {
        let leftIndex = this.getLeftIndex(index);
        let rightIndex = this.getRightIndex(index);

        // 左
        if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
            this.swap(leftIndex, index);
            // 继续
            this.shiftDown(leftIndex)
        }

        // 右
        if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
            this.swap(rightIndex, index);
            // 继续
            this.shiftDown(rightIndex)
        }
    }

    // 插入
    insert(value) {
        this.heap.push(value);
        // this.shiftUp(this.heap[this.heap.length-1]) 填索引
        this.shiftUp(this.heap.length-1);

    }

    // 删除
    pop() {
        // 特殊情况处理 直接优化了
        if (this.size() === 1) return this.heap.shift();

        let top = this.heap[0];
        // 这里是 需要重新调整 忘记了
        this.heap[0] = this.heap.pop()
        this.shiftDown(0);
        return top;
    }

    peek() {
       return  this.heap[0]
    }

    size() {
        return this.heap.length;
    }
}

var mergeKLists = function(lists) {
    // 对于链表还是一样的说法
    const heap = new MinHeap();
    
    const dummy = new ListNode(0);
    
    let p = dummy;
    // 插入k个升序链表的头部节点
    lists.forEach(item => {
        heap.insert(item);
    })
   
    //不断的比较最小堆中k个节点的大小
    while(heap.size()) {
       let temp = heap.pop(); // 取头 向下调整
       
       p.next = temp; // 更新next ---> 节点
       p = p.next; // p 指向下一个节点
       
       // 如果 temp 的 下个节点存在 继续插入重新调整
       // 向上调整
       if (temp.next) heap.insert(temp.next)
    }
    // 返回头节点
    return dummy.next;
}