Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >前端leetcde算法之--堆

前端leetcde算法之--堆

原创
作者头像
js2030code
发布于 2022-12-19 01:12:15
发布于 2022-12-19 01:12:15
24600
代码可运行
举报
文章被收录于专栏:js刷leetcodejs刷leetcode
运行总次数:0
代码可运行

正文 plus

堆是动态求极值的数据结构,所以当遇到需要不断取极值的时候,就可以考虑用堆了

温馨提示,建议每一道题都自己 new 一个堆,这样才能发现堆之美,其实就是不会再次遇到 topK 的时候只能用冒泡来做。

行文至此也该结束了,如果有 10 个关注,那就更新一下下一 part, dp 或者树吧, thx。

二叉堆的创建

分析 -- 小顶堆

  1. 这里是一个小顶堆,特点就是根节点的值比子节点的值都小,通常用作经典的前 K 大
  2. 主要有两个方法,
    • 一个是上升,这里主要用作构建堆的时候,整理堆用的
    • 一个是下沉,这里主要用于弹出堆顶元素后,置换了堆顶值之后使用的,这里用到 this.data0,是因为这个方法一般是构建完整的堆之后,才会使用
  3. 其他的方法不是不可以,只是为了保证灵活性,暂时就先做个简易版,后面再考虑,因为方法越多,其实兼容性就越差了
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MinHeap {
    constructor(len) {
      this.data = [];
      this.data[0] = len; // 第一个节点用来存放堆的大小 -- 某些特定环境比较好用
    }

    // 下浮
    down(index) {
      const size = this.data[0];
      while (index << 1 <= size) {
        let child = index << 1;

        if (child !== size && this.data[child] > this.data[child + 1]) {
          child += 1; //如果右节点更小,就右节点作为下一个接盘的节点
        }
        if (this.data[index] > this.data[child]) {
          //   交换一下
          [this.data[index], this.data[child]] = [
            this.data[child],
            this.data[index],
          ];
          index = child;
        } else {
          //   只要其中一次失效了,那么就没必要继续往下查找了
          break;
        }
      }
    }

    // 都是从最底层开始算的
    upper() {
      // 这里不用 this.data[0] 是因为当前构建的堆可能还没达到堆的最大值,所以不能使用  
      let index = this.data.length - 1;
      while (index >> 1 > 0) {
        let father = index >> 1;
        if (this.data[index] < this.data[father]) {
          // 子节点比父节点要小,则网上走
          [this.data[index], this.data[father]] = [
            this.data[father],
            this.data[index],
          ];
          index = father;
        } else {
          break;
        }
      }
    }
  }

分析 -- 大顶堆

  • 相对于初始版的小顶堆,这一版的大顶堆添加了两个方法, pop 和 add
  • add 方法可以写在使用堆的位置,但是为了让它和堆的第一个值匹配,所以写在了类方法里面,方便对 size 的添加
  • pop 是为了取出堆顶元素,堆是为了解决动态取极值而存在的数据结构,所以必然要取出整理的过程。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MaxHeap {
  constructor() {
    this.data = [];
    this.data[0] = 0; // 第一个值是当前堆的size
  }

  down(index) {
    const len = this.data.length; // 是下标极限
    while (index << 1 < len) {
      let child = index << 1;
      if (child !== len && this.data[child + 1] > this.data[child]) {
        child++;
      }
      if (this.data[child] > this.data[index]) {
        [this.data[child], this.data[index]] = [
          this.data[index],
          this.data[child],
        ];
        index = child;
      } else {
        break;
      }
    }
  }

  upper() {
    let index = this.data[0]; // 最大下标
    while (index >> 1 > 0) {
      const father = index >> 1;
      if (this.data[index] > this.data[father]) {
        [this.data[father], this.data[index]] = [
          this.data[index],
          this.data[father],
        ];
        index = father;
      } else {
        break;
      }
    }
  }

  add(value) {
    // 先加在最末尾
    this.data.push(value);
    this.data[0]++; // size 也加一下
    this.upper(); // 整理一下
  }

  // 弹出堆顶元素
  pop() {
    const last = this.data[0];
    [this.data[1], this.data[last]] = [this.data[last], this.data[1]]; //交换堆顶和堆尾的值
    const ret = this.data.pop();
    this.data[0]--;
    this.down(1); // 整理
    return ret;
  }
}

参考视频:传送门

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

分析

  • 这是一道炒鸡经典的题,可以用冒泡,快排,其中最经典的方法,莫过于小顶堆求值 -- 作为教材基本的题目
  • 这里求第 K 大,那么就是要维护一个 K 大的小顶堆,然后堆顶就是第 K 大
  • 然后遍历数组 nums,先初始化一个 K 大的小顶堆,然后剩下的值就和堆顶比较;
  • 只要是值大过堆顶值的,那么直接把堆顶值替换掉,但这时,堆顶值就不一定是小顶堆中的最小值了,所以需要向下 down 整理一下小顶堆
  • 遍历结束后就得到一个小顶堆了,然后直接去堆顶元素就是第 K 大了;
  • 时间复杂度: {O(N*L) -- 其中 N 是数组大小, L 是二叉堆的层高,L其实相对来说比较小,所以复杂度可以说接近线性
  • 空间复杂度: O(M) -- 其中 M 是就是 K 值,因为要维护一个 K 大堆
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 215. 数组中的第K个最大元素

// MinHeap 就是上面的那个类

var findKthLargest = function (nums, k) {
    // 创建一个 K 大的小顶堆
    const minHeap = new MinHeap(k);
    const len = nums.length;
    for (let i = 0; i < len; i++) {
      if (i < k) {
        // 初始化堆
        minHeap.data.push(nums[i]);
        minHeap.upper();
      } else {
        // 这个时候开始考虑是否要压到小顶堆中了
        // 因为维护的是一个 k 大的小顶堆,而目的是求第 k 大,所以只需要判断后面的值是否大于小顶堆的堆顶值,即可晓得是否需要取代
        if (nums[i] > minHeap.data[1]) {
          minHeap.data[1] = nums[i];
          minHeap.down(1);
        }
      }
    }
    return minHeap.data[1]
  };

1046. 最后一块石头的重量

分析 -- 大顶堆

  1. 按照题目已经,需要取出一组数组中的最大值和次大值,进行一定运算后,会将计算值返回给数组,然后循环操作,直到数组长度最大为 1 时结束
  2. 所以就是动态取极值,可以考虑用堆来处理
  3. 定义一个方法 pop,每次获取堆顶元素,并将大顶堆整理好,定义方法 add 为为堆加入元素
  4. 这样每一次取出两个元素,返回 1 或 0 个元素,一直到堆元素小于 2 时结束,返回堆中的元素或 0
  5. 空间复杂度就是维护堆,所以是 O(N), 时间复杂度 O(NlogN)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 1046. 最后一块石头的重量

var lastStoneWeight = function (stones) {
    // 维护一个大顶堆
    const heap = new MaxHeap();
    for (let i = 0; i < stones.length; i++) {
      heap.add(stones[i]);
    }

    while (heap.data[0] > 1) {
      // 1. 每次取出两个最大值
      const first = heap.pop();
      const second = heap.pop();
      // 2. 相减,再放回去
      const temp = first - second;
      if (temp) {
        heap.add(temp);
      }
    }
     return heap.data[0] ? heap.data[1]:0
  };

  class MaxHeap {
    constructor() {
      this.data = [];
      this.data[0] = 0; // 第一个值是当前堆的size
    }

    down(index) {
      const len = this.data.length; // 是下标极限
      while (index << 1 < len) {
        let child = index << 1;
        if (child !== len && this.data[child + 1] > this.data[child]) {
          child++;
        }
        if (this.data[child] > this.data[index]) {
          [this.data[child], this.data[index]] = [
            this.data[index],
            this.data[child],
          ];
          index = child;
        } else {
          break;
        }
      }
    }

    upper() {
      let index = this.data[0]; // 最大下标
      while (index >> 1 > 0) {
        const father = index >> 1;
        if (this.data[index] > this.data[father]) {
          [this.data[father], this.data[index]] = [
            this.data[index],
            this.data[father],
          ];
          index = father;
        } else {
          break;
        }
      }
    }

    add(value) {
      // 先加在最末尾
      this.data.push(value);
      this.data[0]++; // size 也加一下
      this.upper(); // 整理一下
    }

    // 弹出堆顶元素
    pop() {
        const last = this.data[0];
      [this.data[1], this.data[last]] = [
        this.data[last],
        this.data[1],
      ] //交换堆顶和堆尾的值
      const ret = this.data.pop();
      this.data[0]--;
      this.down(1); // 整理
      return ret
    }
  }

23. 合并K个升序链表

分析 -- 堆

  1. 这里主要是把链表当成是堆的一个元素,以链表头的值作为小顶堆创建的基点
  2. 这里要求得到是 K 个升序链表合并链表,我每次都获取 K 个链表中的最小值,然后放到我自己的链表中,最后得到的就是一个合并完的升序链表了
  3. 空间复杂度就是维护堆,所以是 O(N∗M), 其中 N 是 lists 的长度, M 是 链表的平均长度
  4. 时间复杂度: 取出值合并到新链表 -- O(N∗M)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 23. 合并K个升序链表

/** * @分析 * 1. 以链表的表头值作为判断元素,创建小顶堆 */
 var mergeKLists = function(lists) {
    let emptyNode  = new ListNode() // 自己创建一个
    // 构建一个堆
    const minHeap = new MinHeap()
    for (let i = 0; i < lists.length; i++) {
        const head = lists[i];
        if(head){
            minHeap.add(head)
        }
    }
    let cur = emptyNode;
    while(minHeap.data[0]){
        cur.next =new ListNode(minHeap.pop()) 
        cur = cur.next
    }
    return emptyNode.next
};

class MinHeap {
    constructor(){
        this.data = []
        this.data[0] = 0
    }

    down(index){
        const lastIndex = this.data[0]
        while(index<<1 <= lastIndex){
            let child = index<<1
            if(child!== lastIndex && this.data[child+1].val<this.data[child].val){
                child++
            }
            if(this.data[child].val<this.data[index].val){
                [this.data[child],this.data[index]] = [this.data[index],this.data[child]]
                index = child
            }else{
                break
            }
        }
    }

    upper(){
        let index = this.data[0]
        while(index>>1 > 0){
            let father = index>>1
            // 以表头值作为判断依据
            if(this.data[father].val>this.data[index].val){
                // 交换的是整个链表
                [this.data[father],this.data[index]] = [this.data[index],this.data[father]]
                index = father
            }else{
                break
            }
        }
    }

    add(head){
        // 添加的是一个排好序的链表,
        this.data.push(head);
        this.data[0]++
        this.upper()
    }

    // 将堆顶链表的头部值取出之后,重新整理
    pop(){
        const ret = this.data[1].val
        this.data[1] = this.data[1].next
        if(!this.data[1]){
            // 链表为 undefined 了
            [this.data[1],this.data[this.data[0]]] = [this.data[this.data[0]],this.data[1]]
            this.data.pop()
            this.data[0]--
        }
        this.down(1) //整理
        return ret // 返回的是一个值
    }
}

分析 -- 分治

  1. 多个排序链表很难处理,那么两个排序好的链表合并呢?
  2. 将两个有序链表合成一个,然后放进 list 继续走,最后合成一个即可
  3. 用分治,如果超出 2 个链表,就拆分了处理,mergeKLists(arr) 最后得到的也是一个排序好的链表,所以每次可以分开治,然后最后 merge 治;
  4. 合并两个链表的时间复杂度 O(N),分治处理 M 个链表的复杂度为 O(logM) 所以 O(NlogM) , 其中 N 是链表的长度, M 是链表的数量
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** * @分析 * 1. 多个排序链表很难处理,那么两个排序好的链表合并呢? * 2. 将两个有序链表合成一个,然后放进 list 继续走,最后合成一个即可 * 3. 用分治,如果超出 2 个链表,就拆分了处理,mergeKLists(arr) 最后得到的也是一个排序好的链表,所以每次可以分开治,然后最后 merge 治; * 4. 合并两个链表的时间复杂度 ${O(N)}$,分治处理 M 个链表的复杂度为 ${O(logM)}$ 所以 ${O(NlogM)}$ , 其中 N 是链表的长度, M 是链表的数量 */
 var mergeKLists = function(lists) {
     const len  =lists.length
     // return lists.reduce((prev,cur) => mergeTwoList(prev,cur)) // 直接 api 递推即可,但是复杂度更高
    // 用分治的方式可以将 N 的复杂度降低到 logN
    if(len<1) return null
    if(len === 1) return lists[0]
    if(len === 2) return mergeTwoList(lists[0],lists[1])
    const mid = len>>1
    return mergeTwoList(mergeKLists(lists.slice(0,mid)),mergeKLists(lists.slice(mid)))

};

// 合并两个有序链表
function mergeTwoList (list1,list2){
    const emptyNode = new ListNode()
    let cur =emptyNode
    while(list1 && list2){
        if(list1.val<list2.val){
            cur.next= list1
            list1 = list1.next
        }else{
            cur.next= list2
            list2 = list2.next
        }
        cur = cur.next 
        cur.next = null
    }
    if(list1) cur.next = list1
    if(list2) cur.next = list2
    return emptyNode.next
}

451. 根据字符出现频率排序

分析

  1. 既然是要按照出现评率进行新组装,所以先遍历一次字符串,用 map 将字符和出现频率作为一组 item 保存起来 -- 时间空间复杂度都是 O(N)
  2. 这个时候其实只要按照频率从到到小排列好,然后一一取出重装即可
  3. 这边是用堆排序,但是item 不再是简单的数字,而是一个数组 key,value,所以相应的方法微调即可
  4. 堆排是时间复杂度:O(NlogN), 最终的空间复杂度是 O(N)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 451. 根据字符出现频率排序

var frequencySort = function (s) {
    let ret = "";
    if (!s) return s;
    const map = new Map();
    const heap = new MaxHeap();
    for (let i = 0; i < s.length; i++) {
      const item = s[i];
      if (map.has(item)) {
        map.set(item, map.get(item) + 1);
      } else {
        map.set(item, 1);
      }
    }
    // 加入堆中,元素值是 [key,value], 要用 value 来进行比对
    for (let item of map.entries()) {
      heap.add(item);
    }
    while (heap.data[0]) {
      const item = heap.pop();
      ret += item[0].repeat(item[1]);
    }
    return ret;
  };

  class MaxHeap {
    constructor() {
      this.data = [];
      this.data[0] = 0;
    }

    down(index) {
      const lastIndex = this.data[0]; //最后一个值的下标值
      while (index << 1 <= lastIndex) {
        let child = index << 1;
        if (
          child !== lastIndex &&
          this.data[child + 1][1] > this.data[child][1]
        ) {
          child++;
        }
        if (this.data[child][1] > this.data[index][1]) {
          // 注意,item 是数组,所以用第二个值做比对,但是交换的是整个 item
          [this.data[child], this.data[index]] = [
            this.data[index],
            this.data[child],
          ];
          index = child;
        } else {
          break;
        }
      }
    }

    upper() {
      let index = this.data[0];
      while (index >> 1 > 0) {
        const father = index >> 1;
        if (this.data[father][1] < this.data[index][1]) {
          // 注意,item 是数组,所以用第二个值做比对,但是交换的是整个 item
          [this.data[father], this.data[index]] = [
            this.data[index],
            this.data[father],
          ];
          index = father;
        } else {
          break;
        }
      }
    }

    add(item) {
      this.data.push(item);
      this.data[0]++;
      this.upper();
    }

    pop() {
      [this.data[1], this.data[this.data[0]]] = [
        this.data[this.data[0]],
        this.data[1],
      ];
      this.data[0]--;
      const temp = this.data.pop();
      this.down(1)
      return temp
    }
  }

378. 有序矩阵中第 K 小的元素

分析

  1. 这里就是 item 为数组的 bottomK, 和正常的 top K 只是多了以数组作为元素的处理
  2. 使用堆排的时候,只需要整理函数 down 和 upper 比对的时候弄一下就好了
  3. 不过有一个区别就是,这个 K 有可能大于二维数组的数组长度 Len(matrix), 所以不能直接创建一个 K 大大顶堆取堆顶,反而要将二维数组所有元素都编入到 Len 大小的小顶堆中,然后再取 K 次
  4. 这也说明了 top K 这类题的两种堆解法,要不就是设置 K 大/小 的堆,然后不断用元素去替代,要不设置全部元素的堆,然后弹出 K 次值;
  5. 时间/空间复杂度 O(NlogN)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 378. 有序矩阵中第 K 小的元素

/** * @分析 -- 第 K 小 * 1. 这里给的排好序的矩阵,那么可以用小顶堆将矩阵中元素转移到小顶堆中,每次从堆顶取值后整理,取到第 K 个即可 */
var kthSmallest = function(matrix, k) {
    const minHeap = new MinHeap()
    for(let i = 0;i<matrix.length;i++){
        minHeap.add(matrix[i])
    }
    const ret = []
    while(--k){
        minHeap.pop()
    }
    return minHeap.pop()

};

class MinHeap {
    constructor(){
        this.data = []
        this.data[0] = 0
    }

    down(index){
        const lastIndex = this.data[0]
        while(index<<1 <= lastIndex){
            let child = index << 1
            if(child!==lastIndex && this.data[child+1][0]< this.data[child][0]){
                child++
            }
            if(this.data[child][0]< this.data[index][0]){
                [this.data[child], this.data[index]] = [this.data[index], this.data[child]]
                index = child
            }else {
                break
            }
        }
    }

    upper() {
        let index = this.data[0]
        while(index >>1 > 0){
            let father = index >> 1 
            if(this.data[father][0]> this.data[index][0]){
                [this.data[father], this.data[index]] = [this.data[index], this.data[father]]
                index = father
            }else {
                break
            }
        }
    }

    add(item){
        this.data.push(item)
        this.data[0]++
        this.upper()
    }

    // 这里不是直接弹出 item,而只是弹出堆顶的第一个字母,然后再整理
    pop(){
        const temp = this.data[1].shift()
        if(!this.data[1].length){
             // 数组空了
             this.data[1] = this.data[this.data[0]]
             this.data.pop()
             this.data[0]--
        }
        this.down(1)
        return temp
    }
}

const ret = kthSmallest([[1,5,9],[10,11,13],[12,13,15]],8)
console.log(ret)

1054. 距离相等的条形码

分析

  1. 为了保证两两不相等,那得保证数量最多的那个条码必须先排完,防止排完其他就省它自个儿了;
  2. 所以先用 map 存储所有条码值(key)和数量(value)
  3. 这个时候就和1046. 最后一块石头的重量很像了;
  4. 但是还有一点区别,就是每次你取出最大的两块,都只能取走一份进行排列,然后就要把剩下的放回去,保证每一次取两个值都是最大;这就好比,我们这道题是去磨石头,每次相互之间磨掉一层皮,每一次都要拿最后的石头去磨,而1046. 最后一块石头的重量 是直接两个一砸就结束了;
  5. map 存储时,时间复杂度和空间复杂度都是 O(N), N 是长度; 堆排,这个算不出来了,大概也是 O(NlogN) 吧
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 1054. 距离相等的条形码

var rearrangeBarcodes = function (barcodes) {
  // 1. 将条形码的值和数量用 map 存储起来
  const map = new Map();
  for (let i = 0; i < barcodes.length; i++) {
    const item = barcodes[i];
    if (map.has(item)) {
      map.set(item, map.get(item) + 1);
    } else {
      map.set(item, 1);
    }
  }
  // 2.创建最大堆,进行堆排
  const heap = new MaxHeap();
  for (let item of map.entries()) {
    heap.add(item); // [key,value]
  }

  // 3. 每次取出最大的两个 item 进行重写排列
  const ret = [];
  while (heap.data[0] > 1) {
    // 这里是默认是保证存在答案,所以即便最后还有item,那么对应的值也只有1个
    // 但是如果条件没有已知,那么就可以根据这个值进行判断是否成功了
    const first = heap.pop();
    const second = heap.pop();
    // Error:错误
    // while(second[1]--){
    //     ret.push(first[0])
    //     ret.push(second[0])
    //     first[1]--
    // }

    ret.push(first[0]);
    first[1]--;
    ret.push(second[0]);
    second[1]--;
    // 然后就要放回去了

    if (first[1]) {
      // 如果还有值,放回堆里再来
      heap.add(first);
    }
    if (second[1]) {
      // 如果还有值,放回堆里再来
      heap.add(second);
    }
  }
  if (heap.data[0]) {
    ret.push(heap.pop()[0]);
  }
  return ret;
};

class MaxHeap {
  constructor() {
    this.data = [];
    this.data[0] = 0;
  }

  down(index) {
    const lastIndex = this.data[0];
    while (index << 1 <= lastIndex) {
      let child = index << 1;
      if (
        child !== lastIndex &&
        this.data[child + 1][1] > this.data[child][1]
      ) {
        child++;
      }
      if (this.data[child][1] > this.data[index][1]) {
        [this.data[child], this.data[index]] = [
          this.data[index],
          this.data[child],
        ];
        index = child;
      } else {
        break;
      }
    }
  }

  upper() {
    let index = this.data[0];
    while (index >> 1 > 0) {
      let father = index >> 1;
      if (this.data[father][1] < this.data[index][1]) {
        [this.data[father], this.data[index]] = [
          this.data[index],
          this.data[father],
        ];
        index = father;
      } else {
        break;
      }
    }
  }

  add(item) {
    this.data.push(item);
    this.data[0]++;
    this.upper();
  }

  pop() {
    [this.data[1], this.data[this.data[0]]] = [
      this.data[this.data[0]],
      this.data[1],
    ];
    const item = this.data.pop();
    this.data[0]--;
    this.down(1);
    return item;
  }
}

console.log(rearrangeBarcodes([7, 7, 7, 8, 5, 7, 5, 5, 5, 8]));

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
搞定大厂算法面试之leetcode精讲18.队列
搞定大厂算法面试之leetcode精讲18.队列 视频讲解(高效学习):点击学习 目录: 1.开篇介绍 2.时间空间复杂度 3.动态规划 4.贪心 5.二分查找 6.深度优先&广度优先 7.双指针 8.滑动窗口 9.位运算 10.递归&分治 11剪枝&回溯 12.堆 13.单调栈 14.排序算法 15.链表 16.set&map 17.栈 18.队列 19.数组 20.字符串 21.树 22.字典树 23.并查集 24.其他类型题 队列的特点:先进先出(FIFO) 队列的时间复杂度:入队和出队O(1),查找
全栈潇晨
2021/12/03
6730
用javascript分类刷leetcode18.队列(图文视频讲解)1
队列的特点:先进先出(FIFO)队列的时间复杂度:入队和出队O(1),查找O(n)优先队列:priorityQueue,按优先级出队,实现 Heap(Binary,Fibonacci...)js里没有队列,但是可以用数组模拟图片347. 前 K 个高频元素 (medium)给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。示例 1:输入: nums = 1,1,1,2,2,3, k = 2输出: 1,2示例 2:输入: nums = 1, k
hellocoder2028
2023/01/03
7740
搞定大厂算法面试之leetcode精讲12.堆
满二叉树:除叶子节点外,所有的节点都有两个子节点,这类二叉树称作满二叉树(Full Binarry Tree),如下图:
全栈潇晨
2021/11/30
2370
前端算法系统练习: 栈和队列篇
这是前端算法系统练习系列的第二篇——栈和队列篇。关于为什么要练习算法和数据结构,请见上一篇前端算法系统练习: 链表篇完结。我一直秉承学习这件事情一定要系统的观念,因此每次更新都是一个完整的专题,虽然比较长,但是看下来结构会很清晰,收获的是一块完整的知识和方法论。希望对你能有帮助!
用户3806669
2021/03/11
4830
LeetCode 295.数据流的中位数 - JavaScript
题目描述:中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
心谭博客
2020/04/21
7330
可视化图解算法:合并k个已排序(升序)的链表
数据范围:节点总数满足 0≤n≤105,链表个数满足 1≤k≤105 ,每个链表的长度满足 1≤len≤200 ,每个节点的值满足∣val∣<=1000
用户11589437
2025/03/31
760
可视化图解算法:合并k个已排序(升序)的链表
搞定大厂算法面试之leetcode精讲13.单调栈
搞定大厂算法面试之leetcode精讲13.单调栈 视频讲解(高效学习):点击学习 目录: 1.开篇介绍 2.时间空间复杂度 3.动态规划 4.贪心 5.二分查找 6.深度优先&广度优先 7.双指针 8.滑动窗口 9.位运算 10.递归&分治 11剪枝&回溯 12.堆 13.单调栈 14.排序算法 15.链表 16.set&map 17.栈 18.队列 19.数组 20.字符串 21.树 22.字典树 23.并查集 24.其他类型题 239. 滑动窗口最大值 (hard) 方法1.优先队列 动画过大,点击查
全栈潇晨
2021/12/01
8200
测试面试 | Python 算法与数据结构面试题系列二(附答案)
⬆️ 关注 @霍格沃兹测试学院 公众号,回复「面试」,领取 BAT 大厂测试面试真题专辑。
霍格沃兹测试开发
2020/12/23
4930
数据结构与算法(4)——优先队列和堆什么是优先队列?堆和二叉堆LeetCode相关题目整理
听这个名字就能知道,优先队列也是一种队列,只不过不同的是,优先队列的出队顺序是按照优先级来的;在有些情况下,可能需要找到元素集合中的最小或者最大元素,可以利用优先队列ADT来完成操作,优先队列ADT是一种数据结构,它支持插入和删除最小值操作(返回并删除最小元素)或删除最大值操作(返回并删除最大元素);
我没有三颗心脏
2018/07/24
1.3K0
数据结构与算法(4)——优先队列和堆什么是优先队列?堆和二叉堆LeetCode相关题目整理
用javascript分类刷leetcode-排序算法(图文视频讲解)
归并排序:时间复杂度O(nlogn),分的时间复杂度O(logn),合并的过程的复杂度是O(n)
hellocoder2028
2023/01/02
4690
数据结构--堆 Heap
堆排序代码见:https://blog.csdn.net/qq_21201267/article/details/80993672#t7
Michael阿明
2021/02/20
3110
数据结构--堆 Heap
前端学数据结构与算法(七): 从零实现优先队列-堆及其应用
为什么说树结构是01世界里最重要的数据结构,因为只要调整一下节点的存储顺序或枝杈多少,解决问题的类型就可以完全不同。本章介绍的堆也是二叉树的一种,与二叉搜索树想比,只是改变了节点存放值的规则,它遵循的规则就是每个父节点的值必须大于或等于孩子节点的值,这种数据结构是二叉堆,也可以叫它优先队列。
飞跃疯人院
2020/10/07
3500
文心一言 VS 讯飞星火 VS chatgpt (241)-- 算法导论17.3 7题
七、为动态整数多重集 S (允许包含重复值)设计一种数据结构,支持如下两个操作:① INSERT(S,x) 将 x 插入 S 中;② DELETE-LARGER-HALF(S) 将最大的 ⌈|S|/2⌉ 个元素从S中删除。解释如何实现这种数据结构,使得任意 m 个 INSERT 和 DELETE-LARGER-HAIF 操作的序列能在 O(m) 时间内完成。还要实现一个能在 O(|S|) 时间内输出所有元素的操作。如果要写代码,请用go语言。
福大大架构师每日一题
2024/04/25
1330
文心一言 VS 讯飞星火 VS chatgpt (241)-- 算法导论17.3 7题
搞定大厂算法面试之leetcode精讲14.排序算法
归并排序:时间复杂度O(nlogn),分的时间复杂度O(logn),合并的过程的复杂度是O(n)
全栈潇晨
2021/12/01
2890
用javascript分类刷leetcode13.单调栈(图文视频讲解)
我们怎样加速嵌套的这层循环呢,其实可以预先计算从左往右和从右往左的最大高度数组,在循环数组的时候,可以直接拿到该位置左右两边的最大高度,当前位置的接水量就是左右两边高度的较小者减去当前位置柱子的高度
js2030code
2023/01/05
6110
JavaScript刷LeetCode--高频链表题
无论是插入,删除,还是翻转等等操作,先把 next 指针用临时变量保存起来,这可以解决 90% 重组链表中指向出错的问题,
hellocoder2028
2022/12/12
5470
【数据结构与算法】堆
,总的交换次数为 $$ \begin{aligned} & 4 * 0 + 2 * 1 + 1 * 2 \
程序员波特
2024/09/27
900
【数据结构与算法】堆
文心一言 VS 讯飞星火 VS chatgpt (70)-- 算法导论6.5 9题
要设计一个时间复杂度为 O(n log k) 的算法,将 k 个有序链表合并为一个有序链表,可以使用最小堆来实现 k 路归并。
福大大架构师每日一题
2023/08/29
1610
文心一言 VS 讯飞星火 VS chatgpt (70)-- 算法导论6.5 9题
支招 | 机器学习算法之KNN
本文为 AI 研习社社区用户 @BBuf 的博客内容,欢迎扫描底部社区名片访问 @BBuf 的主页,查看更多内容。
AI研习社
2019/10/08
3950
支招 | 机器学习算法之KNN
Python 算法基础篇:堆和优先队列的实现与应用
堆和优先队列是常用的数据结构,它们在算法和程序设计中有着广泛的应用。本篇博客将重点介绍堆和优先队列的原理、实现以及它们在不同场景下的应用。我们将使用 Python 来演示堆和优先队列的实现,并通过实例展示每一行代码的运行过程。
小蓝枣
2023/07/25
4790
推荐阅读
相关推荐
搞定大厂算法面试之leetcode精讲18.队列
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验