图论之最小生成树

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

    }

相关推荐
燃于AC之乐34 分钟前
我的算法修炼之路--4 ———我和算法的爱恨情仇
算法·前缀和·贪心算法·背包问题·洛谷
Boilermaker19926 小时前
[Java 并发编程] Synchronized 锁升级
java·开发语言
Cherry的跨界思维6 小时前
28、AI测试环境搭建与全栈工具实战:从本地到云平台的完整指南
java·人工智能·vue3·ai测试·ai全栈·测试全栈·ai测试全栈
MM_MS6 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
独自破碎E6 小时前
【二分法】寻找峰值
算法
alonewolf_997 小时前
JDK17新特性全面解析:从语法革新到模块化革命
java·开发语言·jvm·jdk
一嘴一个橘子7 小时前
spring-aop 的 基础使用(啥是增强类、切点、切面)- 2
java
sheji34167 小时前
【开题答辩全过程】以 中医药文化科普系统为例,包含答辩的问题和答案
java
mit6.8247 小时前
位运算|拆分贪心
算法
ghie90907 小时前
基于MATLAB的TLBO算法优化实现与改进
开发语言·算法·matlab