最近在复习数据结构和算法的的内容,栈和队列的思想是比较深刻,借于许多高级语言都有相应的框架实现了栈和队列链表等,所以对于这一类,我们只需要了解其思想,在真正操作时,也会显得比较简单。但是还有一类数据结构是稍显复杂的,在高级语言的程序里面并没有相应的框架,比如树和图。树一般可用节点结构体来封装一个节点,但是图,图的话就不容易表示了,因为图是无序的,每个节点与其他节点都有任意的连通性。但是基于使用图的操作目的而言,一般有:搜索(遍历)、最小生成树、寻找节点之间的最小路径等。其目的都是为了存储点对之间的连通性,以及通路的代价,为此,我们可以根据我们的使用目的对其进行抽象为:邻接表、邻接矩阵、十字链表。

连通图的最小生成树

  最小生成树其实在计算机网络里面也有应用:在有线Lan中,为避免交换机之间的连线形成环路,而最终会导致“兜圈子”,从而引起“广播风暴”的现象,Lan中交换机的配置就采用了最小生成树的算法,来避免形成环路。下面介绍两种连通图的最小生成树算法,普里姆算法(Prim)和克鲁斯卡算法(Kruskal),他们在时空消耗上面,各有优劣。但是这里也顺便说,Prim和Kruskal算法都是具是贪心算法的类比,都是从局部最优最后到全局最优的。

(Prim)普里姆算法

  其思想是:

1.有两个集合V,S  .  S代表已经被识别的最小生成树路径上的节点集合,V代表所有节点的集合,V-S 就是剩余未被识别的节点的集合。

2.程序开始时,指定v0 加入S中,使得{v0} = S .

3.在V-S 集合中寻找到下一个节点vi,使得vi 到 S的距离最短。(vi到S的距离是指,vi到S集合中任意一点的距离;当两点直接相连时为连通,否则距离为无穷)。将vi 加入到集合S中。

4.不断运行步骤三,直到S集合包含了所有节点。

  由上就是普里姆算法,其思想非常简单,每次都是去取寻找离已识别集合最短的路径,这样局部最优导致全局最优。该算法的时间复杂度为O(n2).

  下面给出完整的C++代码实现:

  

#include <iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<string.h>
#define N 6
#define MAX_INT 999999
using namespace std;// 边的结构体
typedef struct{int x;int y;int cost;
} Tpath;//连通图的邻接矩阵
int g [N][N] = {{-1,6,1,5,-1,-1},{6,-1,5,-1,3,-1},{1,5,-1,5,6,4},{5,-1,5,-1,-1,2},{-1,3,6,-1,-1,6},{-1,-1,4,2,6,-1}};void gprim(); //main
int main(int argc, char** argv) {gprim();return 0;
}
//运行prim算法
void gprim(){vector<Tpath> p;   //记录边vector<int>u;      //集合Su.push_back(0);    //将V0加入到S中int node1= 0,node2 = 0,cost = 0;int i = 0;vector<int>::iterator  it;for(u.size(); u.size() < N ;){// get the lowcost pathnode1 = -1;node2 = -1;cost = MAX_INT ;for(it = u.begin() ;it != u.end() ; it++){      // 从V-S集合里面寻找到离S集合最lowcost的节点和对应的边。将其记录下来为 为cost,node1,node2 int k = (*it);for(i = 0; i < N ;i++){if(i == (*it))continue;if(g[k][i] >= 0 && (find(u.begin(),u.end(),i) == u.end()) && g[k][i] < cost){node1 = k;node2 = i;cost = g[k][i];}}}// 将该节点加入到S中 并记录下路径pathTpath path;path.cost = cost;path.x = node1;path.y = node2;p.push_back(path);u.push_back(node2);     }//输出vector<Tpath>::iterator itO;for(itO = p.begin() ; itO != p.end() ;itO++){printf("(%d ,%d) cost: %d\n",itO->x+1,itO->y+1,itO->cost);}
}

  

(Kruskal)克鲁斯卡算法

  其思想是:

1.引入节点的连通分量的概念:即一个节点与其他哪些节点相连通。

2.程序开始时,每个节点的连通分量就是自己。有集合E,SE,S。E为图中边的集合,SE为图中已经被识别的边的集合。SE开始为{},S为已识别点的集合。

3.从E-SE中选择一条边(vi,vj),其边的两个顶点时是vi,vj:该边的距离是所有E-S中距离最短的。同时,vi的连通分量中不包含vj,vj的连通分量中不包含vi。将(vi,vj)加入到SE中,将vi,vj

加入到S中,同时将vi的连通分量加入vj中,将vj的连通分量加入到vi中。

4.持续运行步骤3,直到S集合包含了所有节点。

  由上就是克鲁斯卡算法。分析其算法可知,其时间复杂度度为n(logn) , n 为连通图中边的个数。为什么是O(n(logn))呢?其实很简单,克鲁斯卡的算法中每次都是找的E-SE中最短的边,这里可以使用排序算法对所有的边进行排序(O(nlogn)),然后再执行算法步骤2-4时,就可以依次取出来(O(n))。而这里最大的时间消耗是排序,所以是O(nlogn)。

  Kruskal的个人实现:

#include <iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<string.h>
#define N 6
#define MAX_INT 999999
using namespace std;// 边的结构体
typedef struct{int x;int y;int cost;
} Tpath;//连通图的邻接矩阵
int g [N][N] = {{-1,6,1,5,-1,-1},{6,-1,5,-1,3,-1},{1,5,-1,5,6,4},{5,-1,5,-1,-1,2},{-1,3,6,-1,-1,6},{-1,-1,4,2,6,-1}};
//increase sort
bool cmp(const Tpath &p1, const Tpath &p2){return p1.cost < p2.cost;
}void gkruskal();//main
int main(int argc, char** argv) {gkruskal();return 0;
}void gkruskal(){vector<Tpath> t;int i = 0 ,j = 0;
//将所有的边生成一个一个的结构体节点for(i ; i < N ; i++){for(j = i+1;j <N ;j++){if(g[i][j] < 0) continue;Tpath p ;p.x = i;p.y = j;p.cost = g[i][j];t.push_back(p);}}
//按边的距离升序排序sort(t.begin(),t.end() ,cmp);vector<Tpath>::iterator it;//为每个节点Vi设置连通分量vector< set<int> > sets;for(i = 0; i < N ;i++){set<int> v;v.insert(i);sets.push_back(v);}vector<Tpath> p;i = 0;
//执行算法,扫描升序边集合for(;p.size() < N -1 ; ){set<int> x  = sets[t[i].x];set<int> y =  sets[t[i].y];set<int>::iterator it;//如果该边的两个顶点Vi ,Vj 各自的连通分量不包含对方,就将改变加入到路径集合SE中if(x.find(t[i].y) == x.end()){p.push_back(t[i]);set<int>::iterator xi ;//同时将Vj的连通分量加入到Vi的连通分量重for(xi = sets[t[i].x].begin() ; xi != sets[t[i].x].end(); xi++){if((*xi) == t[i].x)continue;sets[(*xi)].insert(y.begin(),y.end());}//同时将Vi的连通分量加入到Vj的连通分量重for(xi = sets[t[i].y].begin() ; xi != sets[t[i].y].end(); xi++){if((*xi) == t[i].y)continue;sets[(*xi)].insert(x.begin(),x.end());}sets[t[i].x].insert(y.begin(),y.end());sets[t[i].y].insert(x.begin(),x.end());}++i;   //扫描下一条边}//输出最小生成树的 边对for(it = p.begin() ; it != p.end();it++){cout<<"("<< it->x +1 <<","<<it->y + 1<<")"<<"cost :"<<it->cost<<endl;}}

  

  

  上面就是两个比较简单,但是比较经典的连通图最小生成树的算法。Prim算法时间复杂度略高,但是空间消耗较少;而Kruskal的算法呢,时间复杂度低,但需要为每个节点设置连通分量的存储空间,因此空间复杂度略高。总之看了这些算法之后,总是对计算机的算法设计有股莫名的倾佩和向往啊!。。。

参考书本:

数据结构(c语言版) 清华大学出版社

计算机算法设计与分析

转载于:https://www.cnblogs.com/compilers/p/5450087.html

个人总结---连通图的最小生成树算法相关推荐

  1. 生成随机数放入整型数组怎么判断有没有重复_图的应用(1)-连通图的最小生成树(Prim算法和Kruskal算法)...

    连通图的生成树: 是一个极小的连通图,它含有图中全部的N个顶点,但是只足以构成一颗树的N-1条边. 必须满足三个条件: 图是连通图: 图中包含了N个结点 图中边的数量等于N-1条. 连通图生成树的判断 ...

  2. 最小生成树算法MST_kruskal算法

    每日一贴,今天的内容症结字为最小生成树算法 MST(minimum spanning tree)即最小生成树算法,经典的有两个,这里分析一下kruskal算法.关于另外的一个prim算法,本blog也 ...

  3. 最小生成树算法详解(prim+kruskal)

    最小生成树概念: 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边. 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里 ...

  4. 最小生成树算法(两个方法实现)

    何为最小生成树算法呢?(记得前提是该数是无向树)(在保证一个图连通的情况下,权值最小的边的集合) 科普一下图的相关定义: 关于图的几个概念定义: 连通图:在无向图中,若任意两个顶点vivi与vjvj都 ...

  5. 2021年SWPUACM暑假集训day3最小生成树算法

    前言 视频链接 视频连接:https://www.bilibili.com/video/BV1wV411s7Pe 练习题单 SWPUOJ题单:http://acm.mangata.ltd/traini ...

  6. 最小生成树算法-克鲁斯卡尔和普利姆

    http://www.cnblogs.com/qianbixin/p/5005161.html http://www.cnblogs.com/yoke/p/6697013.html 学习最小生成树算法 ...

  7. 如何在 Java 中实现最小生成树算法

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/cou ...

  8. 基于C++的带权无向图的实现 (三)- Prim最小生成树算法

    该系列文章是本人整理的有关带权无向图的数据结构和算法的分析与实现,若要查看源码可以访问我的github仓库,如有问题或者建议欢迎各位指出. 目录 基于C++的带权无向图的实现 (一)- 数据结构 基于 ...

  9. Kruskal(克鲁斯卡尔) 最小生成树 算法详解+模板

    最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总和 ...

最新文章

  1. js改变iframe链接
  2. atitit.软件开发方法总结O6
  3. 详解布隆过滤器的原理、使用场景和注意事项
  4. 二叉搜索树的算法实现
  5. 【干货】工作邮件高段位写法
  6. QT如何让窗口放置在屏幕正中间
  7. mybatis中的智能标签之二
  8. python打印日历代码_带tkinter的日历(打印所选日期)
  9. 什么网了解c语言,什么是c语言?
  10. 2月14 大数据处理的基本算法
  11. 如何在Mac OSX上装妥node-camera
  12. 并发编程常见面试题总结一
  13. 简单的Java商城项目记录
  14. bbsmax mysql_mysql 数据库 备份 还原
  15. 雅思备考-口语词组积累-第二章
  16. Itunes 制作苹果手机铃声
  17. SparkMllib模型选择与优化-网格搜索和交叉验证
  18. SaaS 公司融资的「22条军规」
  19. CC2530串口命令控制LED灯开关
  20. GIF 斗图警告!GitHub 标星 5.5k+,Sorry 会编程就是可以 为所欲为!

热门文章

  1. 十进制与二进制快速互转换计算心得
  2. python输入三个整数、输出最大的数_题目:使用Python编程,输入三个整数x,y,z,请把这三个数由小到大输出...
  3. 软考路:2021年系统架构设计师之考试
  4. 解决linux系统WIFI无法使用5GHz频率的问题
  5. Thymeleaf的入门(一)
  6. 【kafka】Group coordinator xx is unavailable or invalid, will attempt rediscovery
  7. 【janio】janio ClassBodyEvaluator 的使用
  8. 【ElasticSearch】Es 源码之 Exporters 源码解读
  9. 【Elasticsearch】zen discovery集群发现机制
  10. 【docker】docker Portainer容器可视化管理工具使用文档