算法基础系列第三章——图论之最小生成树问题(1)

简介: 算法基础系列第三章——图论之最小生成树问题(1)

最小生成树算法大纲

微信图片_20221018122542.png

最小生成树的基本概念


自由树和生成树


自由树(树):

1、自由树就是一个无回路的连通图(没有确定根)

2、n个顶点就一定有n-1条边


生成树:

1、包含全部顶点

2、n-1条边全部在图中

图的生成树不惟一。从不同的顶点出发进行遍历,可以得到不同的生成树。

微信图片_20221018122725.jpg

最小生成树


如果图G是一个连通图,G上的一棵各边权值之和最小的带权生成树,称为G的最小生成树。


普利姆算法(prim)——模板题


通俗演示微信图片_20221018122725.jpg

例题描述微信图片_20221018123006.jpg

因为这是用来做分析的模板题,题目要求里就很直接明了的指出,要咱们求最小生成树的树边权重之和。现在就可以直接去观察数据范围,可以看出是稠密图,那么我们可爱的Prim算法就可以掏出来啦~


参考代码(C++版本)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 510 , INF = 0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
    //初始化距离数组
    memset(dist,0x3f,sizeof dist);
    //res中存放最小生成树的树边权重之和
    int res = 0;        
    for(int i = 0; i < n;i++)
    {
        int t = -1;
        for(int j = 1; j <= n;j++)
            if(!st[j] && (t == -1 || dist[t] > dist[j]))
            t = j;
            //如果不是第一个点以及距离最小的点距离是正无穷,说明当前距离最近的点,到集合的距离都是正无穷,即当前图不连通
            if(i && dist[t] == INF) return INF;
            if(i) res += dist[t];
            //用t去更新其他点
            for(int j = 1; j <= n;j++)
                dist[j] = min(dist[j],g[t][j]);//注意这里是g[t][j],Dijkstra中是dist[t]+g[t][j]
                st[t] = true;
    }
    return res;
}
int main()
{
    //输入
    scanf("%d%d",&n,&m);
    //初始化邻接矩阵
    memset(g,0x3f,sizeof g);
    //建图
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        //因为是无向图,所以得建a 到 b 和 b 到 a的
        g[a][b] = g[b][a] = min(g[a][b],c);
    }
    int t = prim();
    if(t == INF ) puts("impossible");
    else printf("%d\n",t);
    return 0;
}

算法模板


Prim算法实现的流程图如下

微信图片_20221018123157.png

Prim算法实现的代码描述:

  int n;        // n表示点数
  int g[N][N];    // 邻接矩阵,存储所有边
  int dist[N];    // 存储其他点到当前最小生成树的距离
  bool st[N];     // 存储每个点是否已经在生成树中
  // 如果图不连通,则返回INF(值是0x3f3f3f3f), 否则返回最小生成树的树边权重之和
  int prim()
  {
    memset(dist, 0x3f, sizeof dist);
    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
      int t = -1;
      for (int j = 1; j <= n; j ++ )
        if (!st[j] && (t == -1 || dist[t] > dist[j]))
          t = j;
      if (i && dist[t] == INF) return INF;
      if (i) res += dist[t];
      st[t] = true;
      for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }
    return res;
  }

疑难杂症剖析


一、建图

    g[a][b] = g[b][a] = min(g[a][b],c);

因为是无向图,所以需要建立从a 到 b的边 以及 从b 到 a的边

二、计算最小权值之和

        if(i && dist[t] == INF) return INF;
        if(i) res += dist[t];

计算最小的权值之和需要在保证当前这个点能和已经存在的最小生成树集合之间存在最小距离,倘若是距离是正无穷,说明无法与已经存在的最小生成树连通

三、更新

      for(int j = 1; j <= n;j++)
         dist[j] = min(dist[j],g[t][j]);

将算法模板的代码实现看完的小伙伴可能发现,Prim算法和朴素版Dijkstra算法好像呀

相似,又不完全相似

Dijkstra算法中,dist数组维护的是1号点到当前点的距离

Prim算法中,dist数组维护的是当前点到已经存在的最小生成树集合的距离

因此

Dijkstra算法的更新是将t作为中介,将从1号点到j的距离dist[j] 和 1号点到t,再从t到j的距离dist[t]+g[t][j]作比较,找到最小的权值

Prim算法的更新则是,t是作为已经确实的最小生成树集合的代表


/

微信图片_20221018123528.jpg

克鲁斯卡尔算法(Kruskal)——模板题


通俗演示微信图片_20221018123655.jpg

Kruskal算法在理解上是比Prim更舒服的


例题描述微信图片_20221018123929.jpg

参考代码(C++版本)

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200010;
int n,m;
int p[N];//并查集
int cnt,res;//cnt存当前加入多少条边 ; res存放的是最小生成树中所有树边的权重之和
//kruskal算法可以不用邻接表存,只要把点和点到点的边存下来就好
//就不用使用复杂的数据结构,直接结构体搞了,只是要注意重载小于符号,让sort的时候可以根据权重来比较
struct Edge
{
    int a,b,w;
    bool operator < (const Edge &W)const
    {
        return w < W.w;
    }
}edges[N];
//并查集的find函数
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void kruskal()
{
    //对存放的边按照权重排序
    sort(edges,edges+m);
    //初始化并查集
    for(int i = 1; i <= n;i++) p[i] = i;
    //从小到大枚举所以边
    for(int i = 0; i < m; i++) 
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;
        //找到a的祖宗结点
        a = find(a),b = find(b);
        //如果a 和 b 不在一个连通块中
        if(a !=  b)
        {
            //将a连通到b上
            p[a] = b;
            res += w;
            cnt ++;
        }
    }
}
int main()
{
    //输入
    scanf("%d%d",&n,&m);
    //建图
    for(int i = 0; i < m;i++)
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        edges[i] = {a,b,w};
    }
    kruskal();
    //输出 :输出一个整数,表示最小生成树的树边权重之和
    //n个点,因为成最小生成树只能有n-1边
    if(cnt < n-1) puts("impossible");
    else printf("%d\n",res);
    return 0;
}

算法模板


Kruskal算法的执行流程图如下;

微信图片_20221018124040.jpg

Kruskal算法代码实现:

int n, m;   // n是点数,m是边数
  int p[N];   // 并查集的父节点数组
  struct Edge
  {
      int a,b,w;
  }edges[N];
  //自定义比较的方式,待会放置到sort函数中进行比较
  bool cmp(Edge a,Edge b)
  {
      return a.w < b.w;
  }
  int find(int x)   // 并查集核心操作
  {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
  }
  int kruskal()
  {
    sort(edges, edges + m);
    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集
    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
      int a = edges[i].a, b = edges[i].b, w = edges[i].w;
      a = find(a), b = find(b);
      if (a != b)   // 如果两个连通块不连通,则将这两个连通块合并
      {
        p[a] = b;
        res += w;
        cnt ++ ;
      }
    }
    if (cnt < n - 1) return INF;
    return res;
  }

疑难杂症剖析


一、存储边

Kruskal算法和Bellman-Ford算法挺相似的,都是随便大方的乖算法,只要能够获取到存储的信息就好。因此算法模板中可以使用最简单的结构体存储数据。

唯一需要注意的是,要重新制定比较的逻辑,让比较的逻辑是根据权重w来比较的。


二、并查集的使用

2.1、初始化并查集——让每个结点做自己的父结点

2.2、并查集的find函数的编写

相关文章
|
8月前
|
机器学习/深度学习 安全 算法
【图论】【割点】【C++算法】928. 尽量减少恶意软件的传播 II
【图论】【割点】【C++算法】928. 尽量减少恶意软件的传播 II
|
2月前
|
JSON 算法 数据挖掘
基于图论算法有向图PageRank与无向图Louvain算法构建指令的方式方法 用于支撑qwen agent中的统计相关组件
利用图序列进行数据解读,主要包括节点序列分析、边序列分析以及结合节点和边序列的综合分析。节点序列分析涉及节点度分析(如入度、出度、度中心性)、节点属性分析(如品牌、价格等属性的分布与聚类)、节点标签分析(如不同标签的分布及标签间的关联)。边序列分析则关注边的权重分析(如关联强度)、边的类型分析(如管理、协作等关系)及路径分析(如最短路径计算)。结合节点和边序列的分析,如子图挖掘和图的动态分析,可以帮助深入理解图的结构和功能。例如,通过子图挖掘可以发现具有特定结构的子图,而图的动态分析则能揭示图随时间的变化趋势。这些分析方法结合使用,能够从多个角度全面解读图谱数据,为决策提供有力支持。
120 0
|
3月前
|
算法 决策智能
基于prim算法求出网络最小生成树实现网络社团划分和规划
该程序使用MATLAB 2022a版实现路线规划,通过排序节点权值并运用Prim算法生成最小生成树完成网络规划。程序基于TSP问题,采用遗传算法与粒子群优化算法进行路径优化。遗传算法通过编码、选择、交叉及变异操作迭代寻优;粒子群优化算法则通过模拟鸟群觅食行为,更新粒子速度和位置以寻找最优解。
|
6月前
|
存储 传感器 算法
|
7月前
|
算法 Java
Java数据结构与算法:贪心算法之最小生成树
Java数据结构与算法:贪心算法之最小生成树
|
8月前
|
算法 Python
Python中实现图论算法
Python中实现图论算法 “【5月更文挑战第20天】”
63 3
|
7月前
|
算法 数据挖掘 定位技术
算法必备数学基础:图论方法由浅入深实践与应用
算法必备数学基础:图论方法由浅入深实践与应用
|
7月前
|
算法 C语言
数据结构与算法——最小生成树问题(什么是最小生成树、Prim算法、Kruskal算法)
数据结构与算法——最小生成树问题(什么是最小生成树、Prim算法、Kruskal算法)
46 0
|
8月前
|
存储 机器学习/深度学习 算法
上机实验三 图的最小生成树算法设计 西安石油大学数据结构
上机实验三 图的最小生成树算法设计 西安石油大学数据结构
106 1
|
8月前
|
人工智能 算法
一些算法的复习(最短路径、最小生成树、dp)
一些算法的复习(最短路径、最小生成树、dp)