• 使用并查集实现查找无向图的连通分量和求解有向图的强连通分量


    目录

    1.无向图的连通分量

    2.求解连通分量算法的实现

    3.有向图的强连通分量

    4.求解有向图的强连通分量


    使用C语言实现并查集

    有向图和无向图

    1.无向图的连通分量

    无向图G中,如果存在从顶点v1到顶点v2有路径,那么v1和v2是连通的。

    如果对于无向图中的任意两个顶点vi和vj之间都是连通的,那么无向图G是连通图。

    连通分量:是无向图中的极大连通子图。

    如图一所示为连通图:

    如图二所示不是连通图(有是两个连通分量):

    注:由于图二中存在v6,v7和v1,v2,v3,v4,v5是不完全连通的,所以属于两个不同的连通分量,这对于无向图来说是这样,而对于有向图来说的话,判断更加的复杂。

    2.求解连通分量算法的实现

    思路:首先读者需要对并查集 进行了解,其中主要是查找和合并操作以及路径压缩的方法。由于开始的时候对parent数组初始化都为节点自身,使用rank数组记录每一个集合中合并成的树的高度,将高度矮的合并到高度高的树上;判断无向图的连通分量,就是通过并查集的查找操作,查找到一个节点的根节点,判断这两个节点的根节点是否相同,不同的话表示属于两个集合,也就是两个不同的连通分量。最后就是输出每一个连通分量的集合。

    1. #include<stdio.h>
    2. #include<stdlib.h>
    3. #define MAXSIZE 15
    4. typedef int ElemType;
    5. int parent[MAXSIZE];
    6. int rank[MAXSIZE];
    7. void init(){
    8. for(int i=0;i<MAXSIZE;i++){
    9. parent[i]=i;
    10. rank[i]=0;
    11. }
    12. }
    13. //并查集查找操作(包含路径压缩)
    14. int find(int x){
    15. if(parent[x]==x){
    16. return x;
    17. }
    18. return parent[x]=find(parent[x]);
    19. }
    20. //合并操作
    21. void Union(int x,int y){
    22. int fx=find(x);
    23. int fy=find(y);
    24. //当x和y属于不同的集合时,进行合并操作
    25. if(fx!=fy){
    26. if(rank[fx]>rank[fy]){
    27. parent[fy]=fx;
    28. }else{
    29. parent[fx]=fy;
    30. if(rank[fx]==rank[fy]){
    31. rank[fx]++;
    32. }
    33. }
    34. }
    35. }
    36. int main(){
    37. int n,m;
    38. init();
    39. printf("请输入无向图的顶点数: ");
    40. scanf("%d",&n);
    41. printf("请输入无向图的边数: ");
    42. scanf("%d",&m);
    43. printf("请输入无向图的边: \n");
    44. for(int i=1;i<=m;i++){
    45. int u,v;
    46. scanf("%d %d",&u,&v);
    47. Union(u,v);
    48. }
    49. int count=0;
    50. //note用来记录每个连通分量的根节点
    51. int note[MAXSIZE];
    52. for(int i=1;i<=n;i++){
    53. int fx=find(i);
    54. if(fx==i){
    55. count++;
    56. note[count]=fx;
    57. }
    58. }
    59. printf("无向连通图的连通分量: %d\n",count);
    60. for(int i=1;i<=count;i++){
    61. printf("连通分量[%d]: ",i);
    62. for(int j=1;j<=n;j++){
    63. int fx=find(j);
    64. if(fx==note[i]){
    65. printf("%d\t",j);
    66. }
    67. }
    68. printf("\n");
    69. }
    70. return 0;
    71. }
    72. /*
    73. 7 6
    74. 1 2
    75. 1 4
    76. 2 3
    77. 3 4
    78. 2 5
    79. 6 7
    80. */

    3.有向图的强连通分量

    有向图G中,如果对于每一对vi,vj\epsilon V and vi!=vj,从vi到vj和vj到vi都存在路径,则图G为强连通图。

    强连通分量:有向图中的极大连通子图。

    如下图所示,虽然原图不是一个强连通图,但是有两个强连通分量:

    4.求解有向图的强连通分量

    关于深度优先搜索和广度优先搜索请看上面给出的链接。 

    思路:首先是进行深度优先遍历,并将遍历的结果存储在finish数组中;正向遍历完成之后就是进行逆向深度优先遍历,正向边使用edge二维数组存储,逆向边使用redge二维数组存储;逆向深度优先遍历的时候使用正向遍历的结果finish,从finish[n]到finish[1]进行遍历,并且使用k来记录强连通分量的个数,Set数组记录强连通分量的集合。

    1. #include<stdio.h>
    2. #include<stdlib.h>
    3. #define MAXSIZE 15
    4. typedef int ElemType;
    5. //用于记录深度优先搜索过程中访问的节点
    6. int finish[MAXSIZE];
    7. //用于记录节点是否被访问
    8. int visit[MAXSIZE];
    9. //记录正向边
    10. int edge[MAXSIZE][MAXSIZE];
    11. //记录反向边
    12. int redge[MAXSIZE][MAXSIZE];
    13. //图的顶点数和边数
    14. int n,m;
    15. //记录强连通分量的集合
    16. int Set[MAXSIZE];
    17. //k用来记录强连通分量的个数
    18. int k=0;
    19. //记录访问的顶点数
    20. int cnt=0;
    21. //初始化
    22. void init(){
    23. for(int i=0;i<MAXSIZE;i++){
    24. visit[i]=0;
    25. finish[i]=0;
    26. Set[i]=0;
    27. for(int j=0;j<MAXSIZE;j++){
    28. edge[i][j]=0;
    29. redge[i][j]=0;
    30. }
    31. }
    32. }
    33. //深度优先搜索
    34. void dfs(int v){
    35. visit[v]=1;
    36. for(int i=1;i<=n;i++){
    37. if(visit[i]==0&&edge[v][i]==1){
    38. dfs(i);
    39. }
    40. }
    41. //记录访问的节点
    42. finish[++cnt]=v;
    43. }
    44. //反向深度优先搜索
    45. void rdfs(int v,int k){
    46. visit[v]=1;
    47. Set[v]=k;
    48. for(int i=1;i<=n;i++){
    49. if(visit[i]==0&&redge[v][i]==1){
    50. rdfs(i,k);
    51. }
    52. }
    53. }
    54. //求解强连通分量
    55. void Solve(){
    56. //首先进行正向的深度优先搜索
    57. for(int i=1;i<=n;i++){
    58. if(visit[i]==0){
    59. dfs(i);
    60. }
    61. }
    62. //对visit重新进行初始化
    63. for(int i=0;i<MAXSIZE;i++){
    64. visit[i]=0;
    65. }
    66. //进行逆向的深度优先搜索
    67. for(int i=n;i>=1;i--){
    68. int v=finish[i];
    69. if(visit[v]==0){
    70. k++;
    71. rdfs(v,k);
    72. }
    73. }
    74. }
    75. //输出强连通分量集合
    76. void display(){
    77. //打印深度优先访问的结果
    78. printf("cnt = %d\n",cnt);
    79. for(int i=1;i<=cnt;i++){
    80. printf("%d\t",finish[i]);
    81. }
    82. printf("\n");
    83. printf("强连通分量的个数: %d\n",k);
    84. for(int i=1;i<=k;i++){
    85. printf("强连通分量[%d]: ",i);
    86. for(int j=1;j<=n;j++){
    87. if(Set[j]==i){
    88. printf("%d\t",j);
    89. }
    90. }
    91. printf("\n");
    92. }
    93. }
    94. int main(){
    95. init();
    96. printf("请输入无向图的顶点数: ");
    97. scanf("%d",&n);
    98. printf("请输入无向图的边数: ");
    99. scanf("%d",&m);
    100. printf("请输入无向图的边: \n");
    101. for(int i=1;i<=m;i++){
    102. int u,v;
    103. scanf("%d %d",&u,&v);
    104. edge[u][v]=1;
    105. redge[v][u]=1;
    106. }
    107. Solve();
    108. display();
    109. return 0;
    110. }
    111. /*
    112. 4 4
    113. 1 3
    114. 1 2
    115. 3 4
    116. 4 1
    117. 4 7
    118. 1 2
    119. 1 3
    120. 3 1
    121. 3 4
    122. 4 3
    123. 4 2
    124. 4 1
    125. 7 9
    126. 1 2
    127. 1 3
    128. 1 4
    129. 2 1
    130. 2 4
    131. 5 2
    132. 4 5
    133. 6 7
    134. 7 6
    135. */

  • 相关阅读:
    python基于OCR深度学习实现商品配料表识别
    第二十三章 多线程(二)
    数据库压力测试方法小结
    爬虫三大库
    消息队列的选择与应用
    【阅读笔记】多任务学习之PLE(含代码实现)
    重学Java8新特性(四) : 日期时间API、LocalDateTime、DateTimeFormatter、开发中时间工具类(常用)
    Java反射机制清空字符串导致业务异常分析
    在PostgreSQL中如何实现递归查询,例如使用WITH RECURSIVE构建层次结构数据?
    脱发秘籍:前端Chrome调试技巧汇总
  • 原文地址:https://blog.csdn.net/Keep_Trying_Go/article/details/126797661