【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal)

这篇具有很好参考价值的文章主要介绍了【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最小生成树介绍

最小生成树
有关树的定义

生成子图:生成子图是从原图中选取部分节点以及这些节点之间的边所组成的图。生成子图中的所有节点和边都必须在原图中存在。

生成树:一个连通无向图生成子图,同时要求是树。也即在图的边集中选择 n - 1 条,将所有顶点连通。

我们定义无向连通图的 最小生成树(Minimum Spanning Tree,MST)为边权和最小的生成树

注意:只有连通图才有生成树,而对于非连通图,只存在生成森林。

【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal),算法,算法,图论,最小生成树,生成树,Prim,Kruskal

朴素Prim算法

算法思路⭐

算法流程和 Dijkstra 算法非常相似。

Dijkstra 算法是用 t 更新其它点到起点的距离,而 Prim 用 t 更新其它点到 集合 的距离。

【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal),算法,算法,图论,最小生成树,生成树,Prim,Kruskal

初始时各个点到集合的距离设置为 INF

循环 n 次,每次循环找到当前没在集合(集合就是最小生成树中节点的集合)且距离集合最近的节点。

将当前最近的节点 t 加入最小生成树中,然后使用 t 更新其它所有点(1~n)到集合之间的距离。

时间复杂度是: O ( n 2 + m ) O(n^2 + m) O(n2+m)

例题:858. Prim算法求最小生成树

https://www.acwing.com/problem/content/description/860/

【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal),算法,算法,图论,最小生成树,生成树,Prim,Kruskal

注意:图中可能存在重边和自环。
重边是指连接同一对顶点的多条边。在处理重边的时候,我们应当只保留权重最小的那条边,其他的边应当被忽略。
自环是指从一个顶点指向自身的边。在最小生成树中,自环是没有意义的,因为我们可以直接忽略这样的边。实际上,对于 Prim 算法,我们应当在初始化阶段,忽略这些自环,即将其赋予无穷大的权重。

另外注意图是无向图,因此在建图时应当同时更新 g [ u ] [ v ] = g [ v ] [ u ] = M a t h . m i n ( g [ u ] [ v ] , w ) ; g[u][v] = g[v][u] = Math.min(g[u][v], w); g[u][v]=g[v][u]=Math.min(g[u][v],w);

import java.util.*;

public class Main {
    static final int INF = 0x3f3f3f3f;

    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt();
        // 边数很多,所以使用邻接矩阵来存储
        int[][] g = new int[n + 1][n + 1];
        for (int i = 1; i <= n; ++i) {
            Arrays.fill(g[i], INF);     // 对于自环也应该把距离设置成INF
        }
        for (int i = 0; i < m; ++i) {
            int u = scanner.nextInt(), v = scanner.nextInt(), w = scanner.nextInt();
            g[u][v] = g[v][u] = Math.min(g[u][v], w);   // 是无向图,所以g[u][v] = g[v][u]都要更新
        }

        // 求最小生成树的树边权重之和
        int sum = prim(g);
        System.out.println(sum > INF / 2? "impossible": sum);
    }

    static int prim(int[][] g) {
        int n = g.length - 1, res = 0;
        boolean[] st = new boolean[n + 1];      // 存储每个点是否已经在生成树中了
        int[] dis = new int[n + 1];             // 存储其它点到当前最小生成树的距离
        Arrays.fill(dis, 0x3f3f3f3f);       // 初始时距离都是 INF

        for (int i = 0; i < n; ++i) {
            // 找到当前和集合最近且不在集合中的节点t
            int t = -1;
            for (int j = 1; j <= n; ++j) {
                if (!st[j] && (t == -1 || dis[t] > dis[j])) t = j;
            }

            if (i != 0 && dis[t] == INF) return INF;    // 如果第一个节点没有把任何节点到最小生成树的距离变小

            // 将 t 加入最小生成树中去
            if (i != 0) res += dis[t];                  // 注意判断树中的第一个节点是不会贡献边权和的
            st[t] = true;

            // 使用 t 到各个节点的距离 更新 各个节点到当前最小生成树的距离
            for (int j = 1; j <= n; ++j) {
                dis[j] = Math.min(dis[j], g[t][j]);
            }
        }

        return res;
    }
}

Kruskal算法

算法思路⭐

  1. 先将所有边按权重从小到大排序
  2. 枚举每条边 a ,b , w。如果 a, b 不连通就把这条边加入集合,即加入最小生成树。

【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal),算法,算法,图论,最小生成树,生成树,Prim,Kruskal

如果使用 O ( m log ⁡ m ) O(m\log m) O(mlogm) 的排序算法,并且使用 O ( m α ( m , n ) ) O(m\alpha(m, n)) O(mα(m,n)) O ( m log ⁡ n ) O(m\log n) O(mlogn) 的并查集,就可以得到时间复杂度为 O ( m log ⁡ m ) O(m\log m) O(mlogm) 的 Kruskal 算法。

例题:859. Kruskal算法求最小生成树

https://www.acwing.com/activity/content/problem/content/925/
【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal),算法,算法,图论,最小生成树,生成树,Prim,Kruskal文章来源地址https://www.toymoban.com/news/detail-600542.html

import java.util.*;

public class Main {
    static final int INF = 0x3f3f3f3f, N = 100005;
    static int[] p = new int[N];
    static int n;

    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        int m = scanner.nextInt();

        // 记录所有的 m 个边
        int[][] edges = new int[m][3];
        for (int i = 0; i < m; ++i) {
            edges[i][0] = scanner.nextInt();
            edges[i][1] = scanner.nextInt();
            edges[i][2] = scanner.nextInt();
        }

        int res = kruskal(edges);
        System.out.println(res == INF? "impossible": res);
    }

    static int find(int x) {
        if (x != p[x]) p[x] = find(p[x]);
        return p[x];
    }

    static int kruskal(int[][] edges) {
        // 按照权重升序排序
        Arrays.sort(edges, (a, b) -> a[2] - b[2]);
        Arrays.setAll(p, e -> e);       // 初始化并查集

        int res = 0, cnt = 0;
        for (int[] edge: edges) {
            int x = edge[0], y = edge[1];
            if (find(x) != find(y)) {
                p[find(x)] = find(y);
                res += edge[2];
                cnt++;
            }
        }

        if (cnt < n - 1) return INF;
        return res;
    }
}

到了这里,关于【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 图论13-最小生成树-Kruskal算法+Prim算法

    基本思想:按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路 具体做法:首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中 不产生 回路,直至森林变成一棵树为止。 2.2.1 如果图不联通,直接返回空,该

    2024年02月01日
    浏览(23)
  • 算法基础课-搜索与图论

    题目链接:842. 排列数字 - AcWing题库 思路:写的很好的题解AcWing 842. 排列数字--深度优先遍历代码+注释 - AcWing 也可以考虑使用c++自带的next_permutation函数直接秒了: 题目链接:844. 走迷宫 - AcWing题库 思路:由于bfs是一层一层扩展,所以能保证走到终点时,走过的距离最短,所

    2024年04月15日
    浏览(32)
  • 搜索与图论(acwing算法基础)

    排列数字 n皇后 走迷宫 单链表 点击跳转至例题 idx存的是指针 树与图的深度优先搜索 树的重心 每个节点都是一个单链表 模拟队列 hh = 0 , tt = -1 有向图的拓扑序列 都是从前指向后,即有向无环图(不能有环) 所有入度为0的点,都能排在前面的位置 删掉t-j的边,仅仅是j的入度

    2024年02月08日
    浏览(24)
  • 【算法基础:搜索与图论】3.3 拓扑排序

    https://oi-wiki.org/graph/topo/ 本文主要学习拓扑排序相关知识。 拓扑排序的英文名是 Topological sorting。 拓扑排序要解决的问题是给一个 有向无环图 的 所有节点排序 。 我们可以拿大学每学期排课的例子来描述这个过程,比如学习大学课程中有:程序设计,算法语言,高等数学,

    2024年02月16日
    浏览(26)
  • acwing算法基础之搜索与图论--kruskal算法

    kruskal算法的关键步骤为: 将所有边按照权重从小到大排序。 定义集合S,表示生成树。 枚举每条边(a,b,c),起点a,终点b,边长c。如果结点a和结点b不连通(用并查集来维护),则将这条边加入到集合S中。 kruskal算法的时间复杂度为O(mlogm),它用来解决稀疏图的最小生成树问题

    2024年02月05日
    浏览(26)
  • Acwing-基础算法课笔记之搜索与图论

    bellman-ford算法适用于负权边的图,求 1 到 n 的最多经过k条边的最短距离。 如图所示: 1 2 3 dist 0 ∞ infty ∞ ∞ infty ∞ ⇓ Downarrow ⇓ 1 2 3 dist 0 1 ∞ infty ∞ ⇓ Downarrow ⇓ 1 2 3 dist 0 1 2 此过程中出现了串联的结果,所以是错误的,此时需要进行备份操作。 备份操作如下: 为了

    2024年01月20日
    浏览(30)
  • 【算法基础:搜索与图论】3.2 树与图的dfs和bfs

    要学会建树、建图的通用方法。 dfs 和 bfs 的代码框架。 https://www.acwing.com/problem/content/848/ 在 dfs 的过程中,统计各个节点作为断点时的连通块最大值。 https://www.acwing.com/problem/content/849/ 看到最短距离就可以想到使用宽搜。 注意! :题目中说明了 a 和 b 表示存在一条从 a 走到

    2024年02月16日
    浏览(21)
  • acwing算法基础课(第三讲 搜索与图论)

    void dfs(int u){ if(n == u){ for(int i = 0;i n;i++) puts(g[i]); puts(“”); return; } for(int i = 0;i n;i++){ if(!col[i] !dg[u+i] !udg[n - u + i]){ g[u][i] = ‘Q’; col[i] = dg[u+i] = udg[n - u + i] = true; dfs(u+1); col[i] = dg[u+i] = udg[n - u + i] = false; g[u][i] = ‘.’; } } } int main(){ scanf(“%d”,n); for(int i = 0;i n;i++){ for(int j = 0;j

    2024年04月10日
    浏览(30)
  • 【AcWing算法基础课】第三章 搜索与图论

    本专栏文章为本人AcWing算法基础课的学习笔记,课程地址在这。如有侵权,立即删除。 特点 :尽可能先向 纵深方向 搜索。使用 stack 实现。所需空间 O(h) (h为深度)。不具有“最短性”。 题目链接 :842. 排列数字 1.1题目描述 给定一个整数 n,将数字 1∼n 排成一排,将会有

    2024年02月12日
    浏览(40)
  • ✔ ★ 算法基础笔记(Acwing)(三)—— 搜索与图论(17道题)【java版本】

    1. 排列数字(3分钟) 每次遍历dfs参数是 遍历的坑位 原题链接 2. n-皇后问题 原题链接 方法 1. 按行遍历(过程中有回溯、剪枝) 思想: 每次递归中,遍历一行的元素,如果可以放皇后,就递归到下一行,下一行中不行了,就返回来,回溯, 方法2. 按每个元素遍历(没有减枝)

    2024年02月05日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包