数据结构-带优化的 prim

Posted by franki on October 5, 2022

带优化的 prim

从上文求出的最小生成树,可以发现有很多重复的选项被判断,而且最小堆里面存在很多相同的顶点不同边,没有及时判断出那条边是最小的情况,理应得到当前顶点相连的最小的边,其他的排除掉,减少执行的判断,减少复杂度。

代码实现

const IndexReverseMinHeap = require('./index-reverse-min-heap');

class PrimMST {
  constructor(g) {
    this.g = g; // 图的引用
    const compare = (parent, child) => {
      return parent && child && parent.weight > child.weight;
    };
    this.ipq = new IndexReverseMinHeap(compare); // 最小索引堆模拟优先队列
    this.edgeTo = new Array(g.V()).fill(null); // 访问的点对应的边
    this.marked = new Array(g.V()).fill(false); // 标记数组,标记节点是否被访问过
    this.mst = []; // 最小生成树包含的边
    this.mstWeight = 0; // 最小生成树的权值

    // prim
    this.visit(0);
    while (!this.ipq.isEmpty()) {
      // 使用最小索引堆找出已经访问的边中权值最小的边
      // 最小索引堆中存储的是点的索引,通过点的索引找到对应的边
      const v = this.ipq.extractMinIndex();
      this.mst.push(this.edgeTo[v]);
      this.visit(v);
    }

    for (const e of this.mst) {
      this.mstWeight += e.wt();
    }
  }

  visit(v) {
    if (this.marked[v]) return;

    this.marked[v] = true;
    for (const e of this.g.adj(v)) {
      const w = e.other(v);
      // 边的另一个端点没有被访问
      if (!this.marked[w]) {
        // 如果没有考虑过这个端点,直接将这个端点和与之相连的边加入索引堆
        if (!this.edgeTo[w]) {
          this.edgeTo[w] = e;
          this.ipq.insert(w, e.wt());
        } else if (e.wt() < this.edgeTo[w].wt()) {
          // 考虑过这个端点,但是现在这条边比之前考虑的更短,则进行替换
          this.edgeTo[w] = e;
          this.ipq.change(w, e.wt());
        }
      }
    }
  }

  mstEdges() {
    return this.mst;
  }

  result() {
    return this.mstWeight;
  }
}

module.exports = PrimMST;