图 Kruskal最小生成树算法

110 阅读2分钟

Kruskal最小生成树算法

6AECE57D-0375-4E69-B9EA-3A4F383710C7.png

image.png 在一个无向图中,获取一个可以连通的且权重和最小的图,可以连通指每个节点都被连接,如上图右边部分

  • 最小生成图:一定是没有环的,权重和最低,意味着要排序从最小边开始建图
  • 以第二个图的节点距离:首先为每个节点创建一个集合,初始情况下每个集合都只包含自身节点,如{A},{B},{C},...
  • 从边开始创建最小图,从最小的边开始如上图的A-C,判断两个节点是否在一个集合中,A和C不在,则A、C的节点集合合并,{A,C}
  • 然后是C-D,CD不在一个集合中,则合并集合,{A,C,D}
  • 然后是A-B,BC不在一个集合中,则合并集合,{A,B,C,D}
  • 然后是A-D,AD在一个集合中,跳过
  • 然后是B-D,BD在一个集合中,跳过
  • 然后是B-E,B-E不在一个集合中,则合并集合,{A,B,C,D,E},则最终形成的图的节点为{A,B,C,D,E}
function Kruskal(graph) {
  let map = new Map();
  let result = [];
  //获取图中所有的节点
  graph.nodes.values().forEach((node) => {
    let list = [];
    list.push(node);
    //维护每个节点的集合
    map.set(node, list);
  });

  //根据边的权重排序(从大到小)
  let edgeArr = graph.edges;
  edgeArr = edgeArr.sort((a, b) => b.priority - a.priority);

  //从最小边开始放
  while (edgeArr.length) {
    let edge = edgeArr.pop();
    //判断边的两个节点是否在一个集合中(是否形成环)
    if (!isInSameList(edge.from, edge.to)) {
      result.push(edge);
      //不在一个集合中,进行合并集合的操作
      union(edge.from, edge.to);
    }
  }

  function isInSameList(from, to) {
    let fromList = map.get(from);
    let toList = map.get(to);
    //通过内存地址来判断是否是同一个集合(是否会形成环)
    if (fromList === toList) {
      return true;
    }
    return false;
  }
  //合并集合
  function union(from, to) {
    let fromList = map.get(from);
    let toList = map.get(to);
    //将要合并的to节点的集合都加到from节点的集合中
    toList.forEach((node) => {
      fromList.push(node);
      //将to节点的集合修改成fromList,这样两个节点的集合就是同一引用
      map.set(node, fromList);
    });
  }
}