【路飞】堆数据结构

401 阅读1分钟

  • 堆是一种特殊的完全二叉树;
  • js 中通常用数组表示堆;

特点

  • 所以的节点都大于等于(最大堆)或小于等于(最小堆)它的子节点;
  • 左侧节点的位置是 2*index + 1;
  • 右侧节点的位置是 2*index + 2;
  • 父节点位置是(index - 1)/2; image.png

示例

我们以数组[1,3,6,5,8,9]为例,看一下最大堆是怎么生成的。

iShot2021-11-07 10.06.39.gif

应用

  • 堆能高效、快速的找出最大值和最小值,事件复杂度 O(1);
  • 找出第 k 个最大(小)的元素;

最小堆的实现

class MinHeap {
  constructor(maxSize) {
    this.heap = [];
    this.maxSize = maxSize;
  }
  insert(val) {
    this.heap.push(val);
    this.siftUp(this.heap.length - 1);
    if (this.size() > this.maxSize) {
      this.pop();
    }
  }
  getParentIndex(index) {
    return (index - 1) >> 1; // 二进制右移一位
  }
  getLeftIndex(index) {
    return index * 2 + 1;
  }
  getRightIndex(index) {
    return index * 2 + 2;
  }
  siftUp(index) {
    if (index === 0) return;
    let parentIndex = this.getParentIndex(index);
    if (this.heap[index] < this.heap[parentIndex]) {
      this.swap(index, parentIndex);
      this.siftUp(parentIndex);
    }
  }
  pop() {
    this.heap[0] = this.heap.pop();
    this.siftDown(0);
  }
  swap(index1, index2) {
    [this.heap[index1], this.heap[index2]] = [
      this.heap[index2],
      this.heap[index1],
    ];
  }
  siftDown(index) {
    let leftIndex = this.getLeftIndex(index);
    let rightIndex = this.getRightIndex(index);
    if (this.heap[leftIndex] < this.heap[index]) {
      this.swap(leftIndex, index);
      this.siftDown(leftIndex);
    }
    if (this.heap[rightIndex] < this.heap[index]) {
      this.swap(rightIndex, index);
      this.siftDown(rightIndex);
    }
  }
  // 获取堆顶元素
  peak() {
    return this.heap[0];
  }
  size() {
    return this.heap.length;
  }
}

力扣刷题

题目:数据流中的第 K 大元素

解析:

  1. 创建一个最小堆,用来保存前 k 个元素
  2. 每次添加一个元素,如果大于堆顶元素,则替换堆顶元素,并且重新调整堆

代码

class MinHeap {
  constructor(maxSize) {
    this.heap = [];
    this.maxSize = maxSize;
  }
  insert(val) {
    this.heap.push(val);
    this.siftUp(this.heap.length - 1);
    if (this.size() > this.maxSize) {
      this.pop();
    }
  }
  getParentIndex(index) {
    return (index - 1) >> 1; // 二进制右移一位
  }
  getLeftIndex(index) {
    return index * 2 + 1;
  }
  getRightIndex(index) {
    return index * 2 + 2;
  }
  siftUp(index) {
    if (index === 0) return;
    let parentIndex = this.getParentIndex(index);
    if (this.heap[index] < this.heap[parentIndex]) {
      this.swap(index, parentIndex);
      this.siftUp(parentIndex);
    }
  }
  pop() {
    this.heap[0] = this.heap.pop();
    this.siftDown(0);
  }
  swap(index1, index2) {
    [this.heap[index1], this.heap[index2]] = [
      this.heap[index2],
      this.heap[index1],
    ];
  }
  siftDown(index) {
    let leftIndex = this.getLeftIndex(index);
    let rightIndex = this.getRightIndex(index);
    if (this.heap[leftIndex] < this.heap[index]) {
      this.swap(leftIndex, index);
      this.siftDown(leftIndex);
    }
    if (this.heap[rightIndex] < this.heap[index]) {
      this.swap(rightIndex, index);
      this.siftDown(rightIndex);
    }
  }
  // 获取堆顶元素
  peak() {
    return this.heap[0];
  }
  size() {
    return this.heap.length;
  }
}

/**
 * @param {number} k
 * @param {number[]} nums
 */
var KthLargest = function (k, nums) {
  this.minHead = new MinHeap(k);
  nums.forEach((m) => {
    this.minHead.insert(m);
  });
};

/**
 * @param {number} val
 * @return {number}
 */
KthLargest.prototype.add = function (val) {
  this.minHead.insert(val);
  return this.minHead.peak();
};

写在最后

  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞关注