刷题前最好先看看数据结构:JS数据结构基本操作「下」图与堆| 豆包MarsCode AI 刷题

125 阅读3分钟

Hi,这里是正在参加青训营🪖JustHappy,最近不是正好在使用MarsCode AI刷题,正好趁着这个机会再整理一遍在JavaScript中的数据结构的基本操作,争取做到最简单,多一行代码不行、少一行代码不跑那种!这是个人的学习笔记,写的很简单,如果大家想要比较详细的数据结构介绍,推荐去 Hello算法 瞅瞅

本篇主要记录了图和堆

  • JavaScript 中没有图,但是可以用 Object 和 Array 构建图
  • 图的表示法: 邻接矩阵、邻接表、关联矩阵

大概就长下面这样

这是邻接矩阵

这是邻接表

图的深度优先遍历:尽可能深的搜索图的分支

  1. 访问根节点
  2. 对根节点的没有访问过的相邻节点挨个进行深度优先遍历
const visited = new Set();
const dfs = (n) => {
  console.log(n);
  visited.add(n);
  graph[n].forEach((c) => {
    if (!visited.has(c)) {
      dfs(c);
    }
  });
};

图的广度优先遍历:先访问离根节点最近的节点

  1. 新建一个队列,把根节点入队
  2. 把队头出队并访问
  3. 把队头没有访问过的相邻节点入队
  4. 重复第二、第三步,直到队列为空
const visited = new Set();
const q = [2]; //起点
while (q.length) {
  const n = q.shift();
  console.log(n);
  visited.add(n);
  graph[n].forEach((c) => {
    if (!visited.has(c)) {
      q.push(c);
      visited.add(c);
    }
  });
}

  • 任意节点的左侧子节点的位置是 2 * index + 1
  • 任意节点的右侧子节点的位置是 2 * index + 2
  • 父节点位置是(index - 1)/ 2

堆的应用

  • 堆可以高效、快速地找出最大值和最小值

其时间复杂度为 O(1)

  • 找出第 K 个 最大 / 最小 元素:
    • 构建一个最小堆,并将元素依次插入堆中
    • 当堆的容量超过 K,就删除堆顶
    • 插入结束后,堆顶就是第 K 个最大元素

JavaScript 实现一个最小堆类

插入
  • 将值插入到堆的底部,即数组的尾部
  • 执行上移操作:将这个值和它的父节进行交换,直到父节点小于等于这个插入的值
删除堆顶
  • 用数组尾部元素替换堆顶(直接删除堆顶元素会破坏堆结构)
  • 然后下移:将新堆顶和它的子节点进行交换,直到子节点大于等于这个新的堆顶
  • 大小为 k 的堆中删除堆顶的时间复杂度为 O(log k)
获取堆顶和堆的大小
  • 获取堆顶:返回数组的头部
  • 获取堆的大小:返回数组的长度

有关堆类的代码如下

class MinHeap {
  constructor() {
    this.heap = [];
  }
  getParentIndex(i) {
    return (i - 1) >> 1; // 右移一位,相当于除以2
  }
  swap(i, j) {
    const temp = this.heap[i];
    this.heap[i] = this.heap[j];
    this.heap[j] = temp;
  }
  shiftUp(index) {
    if (index === 0) return;
    const parentIndex = this.getParentIndex(index);
    if (this.heap[parentIndex] > this.heap[index]) {
      this.swap(parentIndex, index);
      this.shiftUp(parentIndex);
    }
  }
  insert(value) {
    // 将值插入到堆的底部,即数组的尾部
    this.heap.push(value);
    // 执行上移操作:将这个值和它的父节进行交换,直到父节点小于等于这个插入的值
    this.shiftUp(this.heap.length - 1);
  }
  getLeftIndex(i) {
    return i * 2 + 1;
  }
  getRightIndex(i) {
    return i * 2 + 2;
  }

  shiftDown(index) {
    const leftIndex = this.getLeftIndex(index);
    const 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() {
    this.heap[0] = this.heap.pop();
    this.shiftDown(0);
  }
  peek() {
    return this.heap[0];
  }
  size() {
    return this.heap.length;
  }
}