图论之最小生成树

最小生成树(MST)

朴素Prim算法(普利姆)
  • 无向图,适合于稠密图, 时间复杂度O(n^2)

  • 第一步,选距离生成树最近节点

  • 第二步,最近节点加入生成树

  • 第三步,更新非生成树节点到生成树的距离(即更新minDist数组)

  • 一般做法是把合适的点放入集合,每次都遍历点作为顶点。

  • 代码随想录给出的方案是minDist存储,这样每次只需要遍历一次顶点,把最小边放入数组,而无需遍历所有集合内的点作为顶点。

    输入:
    7 11
    1 2 1
    1 3 1
    1 5 2
    2 6 1
    2 4 2
    2 3 2
    3 4 1
    4 5 1
    5 6 2
    5 7 1
    6 7 1

    import java.util.Arrays;
    import java.util.Scanner;
    public class Main {

    复制代码
      public static void main(String[] args) {
          Scanner in = new Scanner(System.in);
          int V = in.nextInt();
          int E = in.nextInt();
    
          // 初始化一个二维数组来存储图的边权重,初始化为最大值。
          int[][] grid = new int[V + 1][V + 1];
          for (int i = 0; i <= V; i++) {
              Arrays.fill(grid[i], Integer.MAX_VALUE);
          }
          //更新非生成树节点到生成树的距离
          int[] minDist = new int[V + 1];
          Arrays.fill(minDist, Integer.MAX_VALUE);
    
    
          //V是节点的数量,E是边的数量
          for (int i = 1; i <= E; i++) {
              int v1 = in.nextInt();
              int v2 = in.nextInt();
              int val = in.nextInt();
              grid[v1][v2] = val;// 更新边的权重
              grid[v2][v1] = val;
          }
          boolean[] visited = new boolean[V + 1]; // 用于标记节点是否被访问过
    
          minDist[1] = 0; //起始节点1,到自身的距离为0
          //遍历每一个节点
          for(int i = 1; i <= V; i++) {
              int nextNode = -1;
              //先遍历minDist找到未访问的节点中距离起始节点最近的节点
              int minVal = Integer.MAX_VALUE;
              for(int k=1; k <= V; k++) {
                  // 找到未访问的节点中距离起始节点最近的节点
                  if (!visited[k] && minVal> minDist[k]) {
                      minVal = minDist[k];
                      nextNode = k;
                  }
              }
              visited[nextNode] = true; // 标记下一个节点为已访问
    
              //首先先更新当前节点到其他节点的距离
              for(int j = 1 ;j<=V ;j++){
                  int val = grid[nextNode][j];
                  // 如果节点j已经被访问过,跳过
                  // 如果边的权重是最大值,表示没有边相连,跳过
                  if (visited[j] || val == Integer.MAX_VALUE) continue;
                  // 如果当前节点到其他节点的距离小于已知最小距离,则更新最小距离
                  if (minDist[j] > val){
                      minDist[j] = val; // 更新最小距离
                  }
              }
          }
          // 统计结果
          int result = 0;
          for (int i = 2; i <= V; i++) {
              result += minDist[i];
          }
          System.out.println(result);
      }

    }

Kruskal算法(克鲁斯卡尔)
  • 无向图,适合于稀疏图,时间复杂度 O (E logE)。

  • 思路:( 排序边+并查集 ),边放入集合中排序,遍历边,并用并查集判断边是否是成环的边,如果是就跳过。当边达到n-1时可以提前结束

    输入:
    7 11
    1 2 1
    1 3 1
    1 5 2
    2 6 1
    2 4 2
    2 3 2
    3 4 1
    4 5 1
    5 6 2
    5 7 1
    6 7 1

    import java.util.*;
    public class Main{

    复制代码
      public static void main(String[] args) {
          Scanner scanner = new Scanner(System.in);
          int V = scanner.nextInt();
          int E = scanner.nextInt();
          List<Edge> edges = new ArrayList<>();
          int result_val = 0;
    
          for (int i = 0; i < E; i++) {
              int v1 = scanner.nextInt();
              int v2 = scanner.nextInt();
              int val = scanner.nextInt();
              edges.add(new Edge(v1, v2, val));
          }
    
          edges.sort(Comparator.comparingInt(e -> e.val)); // 按边的权重排序
          UnionFind uf = new UnionFind(V);
          int num =0;
          for(Edge edge : edges) {
              // 如果已经选取的边数等于V-1,则停止
              if(num==V-1)break;
              int rootS = uf.find(edge.s);
              int rootT = uf.find(edge.t);
              if (rootS == rootT) {
                  continue; // 如果两个节点已经在同一集合中,则跳过
              }
              uf.union(rootS, rootT); // 合并两个节点的集合
              result_val += edge.val; // 累加边的权重
              num++;
          }
          System.out.println(result_val);
    
      }
      static class Edge{
          int s; // 起点
          int t; // 终点
          int val; // 边的权重
          public Edge(int s, int t, int val) {
              this.s = s;
              this.t = t;
              this.val = val;
          }
      }
      static class UnionFind{
          int[] parent;
          public UnionFind(int size) {
              parent = new int[size + 1];
              for (int i = 1; i <= size; i++) {
                  parent[i] = i; // 初始化每个节点的父节点为自己
              }
          }
    
          public int find(int x) {
              if (parent[x] != x) {
                  parent[x] = find(parent[x]); // 路径压缩
              }
              return parent[x];
          }
    
          public void union(int x, int y) {
              int rootX = find(x);
              int rootY = find(y);
              if (rootX == rootY) return; // 已经在同一组
              parent[rootY] = rootX;
          }
      }

    }

相关推荐
脑海科技实验室2 小时前
Ageing Res Rev:绘制阿尔茨海默病分期进展图:一项利用静息态fMRI和图论的综合性横断面及纵向研究
图论·fmri·阿尔茨海默病
闻缺陷则喜何志丹2 小时前
【图论 拓扑排序 贪心 临项交换】P5603 小 C 与桌游 题解|普及+
c++·算法·图论·贪心·拓扑排序·洛谷·临项交换
闻缺陷则喜何志丹2 小时前
【图论 BFS染色 并集查找 】P3663 [USACO17FEB] Why Did the Cow Cross the Road III S|普及+
c++·算法·图论·染色法·宽度优先·并集查找
月明长歌2 小时前
Java数据结构:PriorityQueue堆与优先级队列:从概念到手写大根堆
java·数据结构·python·leetcode·
青山如墨雨如画2 小时前
【北邮-研-图论】网络最大流的标号算法V1.0
网络·算法·图论·北邮
chao1898442 小时前
基于MATLAB实现NSGA-II算法
开发语言·算法·matlab
lalala_Zou2 小时前
小米日常实习一面
java·后端·面试
mmz12072 小时前
差分数组(c++)
c++·算法
xu_yule2 小时前
算法基础-图论基础
数据结构·c++·算法·图论·dfs·bfs·最小生成树