图论之最小生成树

最小生成树(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;
          }
      }

    }

相关推荐
庞轩px21 小时前
HotSpot详解——符号引用、句柄池、直接指针的终极解密
java·jvm·设计模式·内存·虚拟机·引用·klass
IT猿手21 小时前
基于强化学习Q-learning算法的无人机三维路径规划算法原理与实现,MATLAB代码
算法·matlab·无人机·路径规划·动态路径规划
qq_4176950521 小时前
C++中的解释器模式
开发语言·c++·算法
y = xⁿ21 小时前
【LeetCodehot100】T108:将有序数组转换为二叉搜索树 T98:验证搜索二叉树
数据结构·算法·leetcode
難釋懷21 小时前
初识Caffeine
java·缓存
程序员小崔日记21 小时前
一道KMP统考真题彻底讲透:nextval与滑动距离的本质
算法·408·王道计算机考研
xiaoye-duck21 小时前
《算法题讲解指南:动态规划算法--路径问题》--9.最小路径和,10.地下城游戏
c++·算法·动态规划
big_rabbit050221 小时前
java面试题整理
java·开发语言
渡过晚枫1 天前
[第十四届蓝桥杯/java/算法]国赛A——跑步计划
算法
hanlin031 天前
刷题笔记:力扣第17题-电话号码的字母组合
笔记·算法·leetcode