首页 > 其他分享 >最小生成树

最小生成树

时间:2023-05-17 21:12:34浏览次数:43  
标签:int 最小 生成 算法 权值 节点

最小生成树

最小生成树(Minimum Spanning Tree,简称 MST)是连接所有节点的边的集合中,权值最小的连通子图的边集合, 即一个加权连通图中,找到一棵生成树,使得所有边的权值之和最小。最小生成树的求解算法主要有两种:


Kruskal 算法

Kruskal 算法也是一种贪心算法,将所有边按照权值`从小到大排序,逐个加入到已有的边集合中,直到所有节点都被覆盖。具体步骤如下:

  1. 将所有边按照权值从小到大排序。
  2. 依次将每条边加入到已有的边集合中,如果加入该边后形成了环,则舍弃该边。
  3. 重复步骤(2),直到所有节点都被覆盖。
  • 需要注意的是,无向图的最小生成树可能不唯一,但最小生成树的边数是固定的,即节点数减一。

时间复杂度分析

O(ElogE)或者O(ElogV),其中E代表图中的边的数目,V代表图中的顶点数目。对图中的边按照非降序排列需要O(ElogE)的时间。排序后遍历所有的边并判断添加边是否构成环,判断添加一条边是否构成环最坏情况下需要O(logV),关于这个复杂度等到景禹给你们谈并查集的时候再分析;因此,总的时间复杂度为O(ElogE + ElogV),其中E的值最大为V(V-1)/2,因此O(logV)等于 O(logE)。因此,总的时间复杂度为O(ElogE) 或者O(ElogV)

代码

  • C++
#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
int fa[maxn];

struct Edge{
    int u, v, w;
    bool operator<(const Edge &other) const{ //重载操作符
        return w < other.w;
    }
}e[maxn];

int find(int x){
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

int main(){
    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= m; i++){
        cin >> e[i].u >> e[i].v >> e[i].w;
    }

    sort(e + 1, e + m + 1);

    for (int i = 1; i <= n; i++){
        fa[i] = i;
    }

    int ans = 0, cnt = 0;
    for (int i = 1; i <= m; i++){
        int fu = find(e[i].u), fv = find(e[i].v);
        if (fu != fv){
            fa[fu] = fv;
            ans += e[i].w;
            cnt++;
        }
        if (cnt == n - 1){
            break;
        }
    }

    cout << ans << endl;

    // 测试代码
    /*
    for (int i = 1; i <= n; i++) {
        cout << find(i) << " ";
    }
    cout << endl;
    */

    return 0;
}

Prim 算法

Prim 算法是一种贪心算法,它的基本思想是先将图中的所有边按照权值从小到大排序,然后依次选择边,如果该边的两个端点不在同一个集合中,则将该边加入生成树中。最终生成的树即为最小生成树。从任意一个节点开始,每次选择权值最小的一条边,加入到已有的边集合中,直到所有节点都被覆盖。具体步骤如下:

  1. 从任意一个节点开始,将其加入到已有的节点集合中。
  2. 选择与已有节点集合距离最小的一条边,将其加入到已有的边集合中。
  3. 将该边的另一个节点加入到已有的节点集合中。
  4. 重复步骤(2)和(3),直到所有节点都被覆盖。
  • 不同于Kruskal算法,Prim算法每次只考虑一个节点和生成树之间的边,因此在稠密图中效率更高。

时间复杂度分析

上面的代码中,当 i == 1的时候,内层的 while 与 for 循环中 j 的取值范围是从 1 到 n-1,内循环一共计算了 2(n-1) 次,其中n为图中的顶点个数; 当 i == 2 的时候,内层循环还是一共计算 2(n-1)次; 以此类推...... i 取最大值 n -1,内层循环还是一共计算2(n-1)次; 所以,整体的执行次数就是(n-1) \* 2(n-1),Prim算法的复杂度是 O(n2)级别的。

代码

  • C++
#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;

int n, m;
int dis[maxn], vis[maxn];
int g[maxn][maxn];

int prim() {
    memset(dis, INF, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    dis[1] = 0;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int u = 0;
        for (int v = 1; v <= n; v++) {
            if (!vis[v] && (u == 0 || dis[v] < dis[u])) {
                u = v;
            }
        }
        vis[u] = 1;
        ans += dis[u];
        for (int v = 1; v <= n; v++) {
            if (!vis[v] && g[u][v] < dis[v]) {
                dis[v] = g[u][v];
            }
        }
    }
    return ans;
}

int main() {
    cin >> n >> m;
    memset(g, INF, sizeof(g));
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        g[u][v] = g[v][u] = w;
    }
    int ans = prim();
    cout << ans << endl;
    return 0;
}

优化

如果要进一步提高Prim算法的效率,可以使用堆优化,将待检查的节点按照权值从小到大加入堆中,这样每次取出堆顶元素时都可以保证该元素是当前权值最小的节点。


参考文献:

图解:什么是最小生成树? - 知乎 (zhihu.com)

最小生成树(Kruskal算法和Prim算法) - 知乎 (zhihu.com)

标签:int,最小,生成,算法,权值,节点
From: https://www.cnblogs.com/EraYes/p/17410274.html

相关文章

  • 最小公倍数
    一问题描述输入两个数求出他们的最小公倍数。二设计思路从一开始查看这两个数是否是因子,将最小的数输出。三程序流程图 四伪代码实现#include<iostream>usingnamespacestd;intmain(){ intm,n; for(intj=0;j<2;j++){ cin>>m; cin>>n; for(inti=1;i<=m*n;i++){ if(i......
  • blog-博客美化-生成目录索引
    博客美化-生成目录索引注意:需要先申请开通JS接口!!今天帮朋友设置后台代码,发现怎么都没有效果,看了下忽略了JS接口;因为插入的代码大多有JS功能,不申请开通JS功能自然无法支持JS效果。网上看了很多博客也都没提及这点,感觉是个坑,So,需要的朋友可以留意下。对了、在编辑页面是显示不出......
  • 使用Rust编写的程序,可以使用快捷键启动、最小化、最大化和关闭窗口
     以下是一个使用Rust编写的程序,可以使用快捷键启动、最小化、最大化和关闭窗口: usegtk::{prelude::*,Application,ApplicationWindow,WindowPosition};usegdk::enums::key;fnmain(){letapplication=Application::new(Some("com.example"),Default::defau......
  • Oracle客户端导出服务端数据(数据泵)生成DMP文件并导入
    1.首先了解下EXPDP和EXP的区别   1)EXP和IMP是客户端工具程序,它们既可以在可以客户端使用,也可以在服务端使用。   2)EXPDP和IMPDP是服务端的工具程序,他们只能在ORACLEQ服务端使用,不能在客户端使用   3)IMP只适用于EXP导出文件,不适用于EXPDP导出文件......
  • PG 生成c#实体类的函数
    赠人玫瑰手有余香 CREATEORREPLACEFUNCTION"public"."fun_Generate_Entity"(class_nametext,tables_nametext)RETURNS"pg_catalog"."text"AS$BODY$DECLAREstr_Resulttext;s_cNamevarchar(64);s_ctypevarchar(64);s_c......
  • 图片生成网站
    以下是一些可以从文本描述生成图片的工具:Dezgo:可以从文本提示生成图片。AIInput:基于文本输入快速创建图片。Lightricks:为个性化和独特的视觉效果生成图片。TexttoImageEditor:为设计和插图生成文本图片。IMGCreator:为图形和插图生成图片。以下是一些可以生成矢量卡通风......
  • 3:闭包,装饰器,生成器,迭代器
    一:什么是闭包1:必须有一个内部函数2:外部函数返回值内部函数3:内部函数一定要调用外部函数的变量 二:什么是装饰器1:装饰器和闭包的区别闭包传递的是变量,装饰器传递的是函数,可以说装饰器是闭包的一种,它只是传递函数的闭包装饰器本质是一种函数,在原函数上增加新的功能。比......
  • Eclipse使用mybatis generator自动生成代码
    一、写在前面           Mybatis属于半自动ORM,在使用这个框架中,工作量最大的就是书写Mapping的映射文件,由于手动书写很容易出错,我们可以利用Mybatis-Generator来帮我们自动生成文件。通过在Eclipse中集成mybatis-generater插件,自动生成Mybatis相关的model、dao、Mapping......
  • 最小公倍数
    一、问题描述:  二、设计思路:   三、程序流程图: 四、代码实现:#include<stdio.h>intmain(){intx,y;printf("请输入两个数字:");scanf("%d%d",&x,&y);intmax=x;if(y>max)max=y;for(inti=max;;i++)......
  • 概率生成函数
    概率生成函数最近联测打到了两道能用概率生成函数直接秒的题。但是我不会概率生成函数。概率生成函数.gb对于非负整数范围内的随机变量\(X\),令\(p_i\)表示\(X=i\)的概率,那么我们定义\(X\)的概率生成函数\(P\)为\(p\)的普通生成函数,即\(P=\sum_zp_iz^i\)。我们对......