【JavaScript版数据结构与算法面向大厂面试】第十章 数据结构之“堆”

第十章 数据结构之“堆”

10.1 堆简介

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第K个最小元素
构建一个最大堆,步骤同上

10.2 JavaScript 实现:最小堆类

在这里插入图片描述

新建一个空堆类

class MinHeap{//新建一个类来存放堆数组
constructor() {
this.heap = [];//新建一个名为heap的数组,把它绑定到this上
}

}

在这里插入图片描述
插入元素代码:

class MinHeap {//1、新建一个类来存放堆数组
    constructor() {//2
        this.heap = [];//3、新建一个名为heap的数组,把它绑定到this上
    }
    swap(i1, i2) {//12、实现交换方法
        var temp = this.heap[i1];//声明一个临时变量来存i1的值
        this.heap[i1] = this.heap[i2];
        this.heap[i2] = temp;

    }
    getParentIndex(i) {//8、拿到这个值的父节点 
        return (i - 1) >> 1;//9、法一:二进制方法
        // return Math.floor((i - 1) / 2); 法二
    }
    shiftUp(index) {//7、实现shiftUp()方法
        //不停的和父节点交换,直到父节点小于它的子节点
        if (index == 0) { return; }//14、如果上移的值等于堆顶,那就不用上移了
        var parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex] > this.heap[index]) {//10、如果当前节点大于父节点,那么就需要交换
            this.swap(parentIndex, index);//11、交换下标
            this.shiftUp(parentIndex);//13、继续上移操作
        }
    }
    insert(value) {//4、插入方法:插入一个值
        this.heap.push(value);//5、把这个值推入heap数组的最后一位
        this.shiftUp(this.heap.length - 1);//6、封装一个shifUp(要上移的值的数组下标)方法,让值上移,
    }

}

var h = new MinHeap();//15、声明一个变量h它等于实例化的最小堆
h.insert(3);//16、插入值3
h.insert(2);
h.insert(1);

在这里插入图片描述
删除堆顶代码:

class MinHeap {//1、新建一个类来存放堆数组
    constructor() {//2
        this.heap = [];//3、新建一个名为heap的数组,把它绑定到this上
    }
    swap(i1, i2) {//12、实现交换方法
        var temp = this.heap[i1];//声明一个临时变量来存i1的值
        this.heap[i1] = this.heap[i2];
        this.heap[i2] = temp;

    }
    getParentIndex(i) {//8、拿到这个值的父节点 
        return (i - 1) >> 1;//9、法一:二进制方法
        // return Math.floor((i - 1) / 2); 法二
    }
    getLeftIndex(i) {//拿到当前节点的左节点 
        return i * 2 + 1;
    }
    getRightIndex(i) {//拿到当前节点的右节点 
        return i * 2 + 2;
    }

    shiftUp(index) {//7、实现shiftUp()方法
        //不停的和父节点交换,直到父节点小于它的子节点
        if (index == 0) { return; }//14、如果上移的值等于堆顶,那就不用上移了
        var parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex] > this.heap[index]) {//10、如果当前节点大于父节点,那么就需要交换
            this.swap(parentIndex, index);//11、交换下标
            this.shiftUp(parentIndex);//13、继续上移操作
        }
    }
    shiftDown(index) {//实现下移算法
        var leftIndex = this.getLeftIndex(index);
        var 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);
        }
    }
    insert(value) {//4、插入方法:插入一个值
        this.heap.push(value);//5、把这个值推入heap数组的最后一位
        this.shiftUp(this.heap.length - 1);//6、封装一个shifUp(要上移的值的数组下标)方法,让值上移,
    }
    pop() {//下移方法操作 
        this.heap[0] = this.heap.pop();//把数组的最后一位转移到数组的头部
        this.shiftDown(0);//this.shiftDown(“要下元素的数组下标“)把头部元素进行下移操作
    }

}

var h = new MinHeap();//15、声明一个变量h它等于实例化的最小堆
h.insert(3);//16、插入值3
h.insert(2);
h.insert(1);
h.pop();//调用移除方法

在这里插入图片描述

class MinHeap {//1、新建一个类来存放堆数组
    constructor() {//2
        this.heap = [];//3、新建一个名为heap的数组,把它绑定到this上
    }
    swap(i1, i2) {//12、实现交换方法
        var temp = this.heap[i1];//声明一个临时变量来存i1的值
        this.heap[i1] = this.heap[i2];
        this.heap[i2] = temp;

    }
    getParentIndex(i) {//8、拿到这个值的父节点 
        return (i - 1) >> 1;//9、法一:二进制方法
        // return Math.floor((i - 1) / 2); 法二
    }
    getLeftIndex(i) {//拿到当前节点的左节点 
        return i * 2 + 1;
    }
    getRightIndex(i) {//拿到当前节点的右节点 
        return i * 2 + 2;
    }

    shiftUp(index) {//7、实现shiftUp()方法
        //不停的和父节点交换,直到父节点小于它的子节点
        if (index == 0) { return; }//14、如果上移的值等于堆顶,那就不用上移了
        var parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex] > this.heap[index]) {//10、如果当前节点大于父节点,那么就需要交换
            this.swap(parentIndex, index);//11、交换下标
            this.shiftUp(parentIndex);//13、继续上移操作
        }
    }
    shiftDown(index) {//实现下移算法
        var leftIndex = this.getLeftIndex(index);
        var 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);
        }
    }
    insert(value) {//4、插入方法:插入一个值
        this.heap.push(value);//5、把这个值推入heap数组的最后一位
        this.shiftUp(this.heap.length - 1);//6、封装一个shifUp(要上移的值的数组下标)方法,让值上移,
    }
    pop() {//下移方法操作 
        this.heap[0] = this.heap.pop();//把数组的最后一位转移到数组的头部
        this.shiftDown(0);//this.shiftDown(“要下元素的数组下标“)把头部元素进行下移操作
    }
    peek(){//获取堆顶
        return this.heap[0];
    }
    size(){//获取堆的大小
        return this.heap.length;
    }

}

10.3 LeetCode:3215. 数组中的第K个最大元素

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现:

class MinHeap {//1、新建一个类来存放堆数组
    constructor() {//2
        this.heap = [];//3、新建一个名为heap的数组,把它绑定到this上
    }
    swap(i1, i2) {//12、实现交换方法
        var temp = this.heap[i1];//声明一个临时变量来存i1的值
        this.heap[i1] = this.heap[i2];
        this.heap[i2] = temp;

    }
    getParentIndex(i) {//8、拿到这个值的父节点 
        return (i - 1) >> 1;//9、法一:二进制方法
        // return Math.floor((i - 1) / 2); 法二
    }
    getLeftIndex(i) {//拿到当前节点的左节点 
        return i * 2 + 1;
    }
    getRightIndex(i) {//拿到当前节点的右节点 
        return i * 2 + 2;
    }

    shiftUp(index) {//7、实现shiftUp()方法
        //不停的和父节点交换,直到父节点小于它的子节点
        if (index == 0) { return; }//14、如果上移的值等于堆顶,那就不用上移了
        var parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex] > this.heap[index]) {//10、如果当前节点大于父节点,那么就需要交换
            this.swap(parentIndex, index);//11、交换下标
            this.shiftUp(parentIndex);//13、继续上移操作
        }
    }
    shiftDown(index) {//实现下移算法
        var leftIndex = this.getLeftIndex(index);
        var 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);
        }
    }
    insert(value) {//4、插入方法:插入一个值
        this.heap.push(value);//5、把这个值推入heap数组的最后一位
        this.shiftUp(this.heap.length - 1);//6、封装一个shifUp(要上移的值的数组下标)方法,让值上移,
    }
    pop() {//下移方法操作 
        this.heap[0] = this.heap.pop();//把数组的最后一位转移到数组的头部
        this.shiftDown(0);//this.shiftDown(“要下元素的数组下标“)把头部元素进行下移操作
    }
    peek(){
        return this.heap[0];
    }
    size(){
        return this.heap.length;
    }

}

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var findKthLargest = function(nums, k) {
    var h =new MinHeap();
    nums.forEach(n=>{//
        h.insert(n);
        if(h.size()> k){
            h.pop();
        }
    });
    return h.peek();
};

10.4 LeetCode:347. 前K个高频元素

在这里插入图片描述
代码实现:

class MinHeap {//1、新建一个类来存放堆数组
    constructor() {//2
        this.heap = [];//3、新建一个名为heap的数组,把它绑定到this上
    }
    swap(i1, i2) {//12、实现交换方法
        var temp = this.heap[i1];//声明一个临时变量来存i1的值
        this.heap[i1] = this.heap[i2];
        this.heap[i2] = temp;

    }
    getParentIndex(i) {//8、拿到这个值的父节点 
        return (i - 1) >> 1;//9、法一:二进制方法
        // return Math.floor((i - 1) / 2); 法二
    }
    getLeftIndex(i) {//拿到当前节点的左节点 
        return i * 2 + 1;
    }
    getRightIndex(i) {//拿到当前节点的右节点 
        return i * 2 + 2;
    }

    shiftUp(index) {//7、实现shiftUp()方法
        //不停的和父节点交换,直到父节点小于它的子节点
        if (index == 0) { return; }//14、如果上移的值等于堆顶,那就不用上移了
        var parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex]&& this.heap[parentIndex].value > this.heap[index].value) {
            this.swap(parentIndex, index);//11、交换下标
            this.shiftUp(parentIndex);//13、继续上移操作
        }
    }
    shiftDown(index) {//实现下移算法
        var leftIndex = this.getLeftIndex(index);
        var rightIndex = this.getRightIndex(index);
        if (this.heap[leftIndex] && this.heap[leftIndex].value<this.heap[index].value) {
            this.swap(leftIndex, index);
            this.shiftDown(leftIndex);
        }
        if (this.heap[rightIndex] && this.heap[rightIndex].value<this.heap[index].value) {
            this.swap(rightIndex, index);
            this.shiftDown(rightIndex);
        }
    }
    insert(value) {//4、插入方法:插入一个值
        this.heap.push(value);//5、把这个值推入heap数组的最后一位
        this.shiftUp(this.heap.length - 1);//6、封装一个shifUp(要上移的值的数组下标)方法,让值上移,
    }
    pop() {//下移方法操作 
        this.heap[0] = this.heap.pop();//把数组的最后一位转移到数组的头部
        this.shiftDown(0);//this.shiftDown(“要下元素的数组下标“)把头部元素进行下移操作
    }
    peek(){
        return this.heap[0];
    }
    size(){
        return this.heap.length;
    }

}

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var topKFrequent = function(nums, k) {
    var map = new Map();
    nums.forEach(n=>{
        map.set(n,map.has(n)?map.get(n)+1:1);
    });
    var h= new MinHeap();
    map.forEach((value,key)=>{
        h.insert({value,key});
        if(h.size()>k){
            h.pop();
        }
    });
    return h.heap.map(a=>a.key);
    };

10.5 LeetCode:23. 合并K个排序链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现:

class MinHeap {//1、新建一个类来存放堆数组
    constructor() {//2
        this.heap = [];//3、新建一个名为heap的数组,把它绑定到this上
    }
    swap(i1, i2) {//12、实现交换方法
        var temp = this.heap[i1];//声明一个临时变量来存i1的值
        this.heap[i1] = this.heap[i2];
        this.heap[i2] = temp;

    }
    getParentIndex(i) {//8、拿到这个值的父节点 
        return (i - 1) >> 1;//9、法一:二进制方法
        // return Math.floor((i - 1) / 2); 法二
    }
    getLeftIndex(i) {//拿到当前节点的左节点 
        return i * 2 + 1;
    }
    getRightIndex(i) {//拿到当前节点的右节点 
        return i * 2 + 2;
    }

    shiftUp(index) {//7、实现shiftUp()方法
        //不停的和父节点交换,直到父节点小于它的子节点
        if (index == 0) { return; }//14、如果上移的值等于堆顶,那就不用上移了
        var parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex] && this.heap[parentIndex].val > this.heap[index].val) {
            this.swap(parentIndex, index);//11、交换下标
            this.shiftUp(parentIndex);//13、继续上移操作
        }
    }
    shiftDown(index) {//实现下移算法
        var leftIndex = this.getLeftIndex(index);
        var 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) {//4、插入方法:插入一个值
        this.heap.push(value);//5、把这个值推入heap数组的最后一位
        this.shiftUp(this.heap.length - 1);//6、封装一个shifUp(要上移的值的数组下标)方法,让值上移,
    }
    pop() {//下移方法操作 
        this.heap[0] = this.heap.pop();//把数组的最后一位转移到数组的头部
        this.shiftDown(0);//this.shiftDown(“要下元素的数组下标“)把头部元素进行下移操作
    }
    peek(){
        if(this.size() === 1) return this.heap.shift();
        return this.heap[0];
        this.heap[0]=this.heap.pop();
        this.shiftDown(0);
        return top;
    }
    size(){
        return this.heap.length;
    }

}

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    var res = new ListNode(0);
    let p = res;
    var h = new MinHeap();
    lists.forEach(l => {
        if(l) h.insert(l);
    });
    while (h.size()){
        var n = h.pop();
        p.next = n;
        p = p.next;
        if(n.next) h.insert(n.next);
    }
    return res.next;

};

10.6 堆总结

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@ZGLi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值