当前位置:   article > 正文

数据结构——图(Graph)_图结构数据

图结构数据

目录

图的概念

概括

基本术语

图的存储结构

邻接矩阵

邻接矩阵的基本结构运行算法代码

邻接矩阵代码实现

邻接表

图的基本运算在邻接表中的实现

邻接表存储结构 

输出图邻接表

邻接表计算无向图某个顶点的度

邻接表计算有向图某个顶点的出度和入度

图的遍历

 深度优先遍历

深度优先遍历算法(邻接表)

深度优先遍历算法(邻接矩阵)

广度优先遍历

广度优先遍历算法(邻接表)

广度优先遍历算法(邻接矩阵)

生成树和最小生成树

 两种常用的构造最小生成树的算法:

Prim算法

kruskal算法


图的概念

概括

图都由顶点(Vertex)和边(Edge)有限集合而成
图分为无向图和有向图
无向图:代表边的顶点对(或序偶)是无序的,则称为无向图。代表边的无序顶点对通常用圆括号括起来,以表示一条无向边。(i,j)
有向图:表示边的顶点对(或序偶)是有序的,则称为有向图。代表边的无序顶点对通常用尖括号括起来,以表示一条有向边。<i,j>

图1:无向图和有向图的示意图

基本术语

1、端点和邻接点
在一个无向图中,若存在一条边(i,j),则称顶点“i”和顶点“j“为该边的两个端点,并称它们互为邻接点,即顶点”i“是顶点”j“的一个邻接点,顶点j也是顶点i的一个邻接点。
在一个有向图中,若存在一条边<i,j>,则称此边是顶点”i“的一条出边,同时也是顶点”j“的一条入边;称”i“和”j“分别为此边的起始端点(简称为起点)和终止端点(简称为终点),并称顶点”j“是”i“的出边邻点,顶点”i“是”j“的入边邻接点。
2、顶点的度、入度和出度
在无向图中, 顶点所关联的边的数目称为该顶点的度。在有向图中,顶点”i“的度又分为入度和出度,以顶点”i“为终点的入边的数目称为该顶点的入度,以顶点”i“为起点的出边的数目称为该顶点的出度。一个顶点的入度与出度的和为该顶点的度。一个图中所有顶点的度之和等于边数的两倍。因为图中的每条边分别作为两个邻接点的度各计一次。
3、完全图
若无向图中的每两个顶点之间都存在一条边 ,有向图中的每两个顶点之间都存在方向相反的两条边,则称此图为完全图。显然,含有n个顶点的完全无向图有n(n-1)/2条边,含有n个顶点的完全有向图有n(n-1)条边。
4、连通、连通图和连通分量
在无向图G中,若从顶点”i“到顶点”j”有路径,则称顶点“i”和顶点“j”是连通的。若图G中的任意两个顶点都连通,则称G为连通图,否则称为非连通图。无向图G中的极大连通子图称为G的连通分量。显然,任何连通图的连通分量只有一个,即它本身,而非连通图有多个连通分量。
4、强连通图和强连通分量
在有向图G中,若从顶点“i”到顶点“j”有路径,则称从顶点“i”到顶点“j”连通的。若图G中的任意两个顶点“i”和“j”都连通,即从顶点“i”到顶点“j”和从顶点“j”到顶点“i’都存在路径,则称图G是强连通图。有向图G中的极大强连通子图称为G的强连通分量。显然,强连通图只有一个强连通分量,即它本身,非强连通图有多个强连通分量。一般地,单个顶点自身就是一个强连通分量。
5、权和网
图中的每一条边都可以附有一个对应的数值。 这种与边相关的数值称为权。权可以表示从图中顶点到另一个顶点的距商或花费的代价。边上带有权的图称为带权图。也称作网。


图的存储结构

邻接矩阵

邻接矩阵是表示顶点之间邻接关系的矩阵。设G=(V ,E)是含有n(设n>0)个顶占的图,各顶点的编号为0~n-1,则G的邻接矩阵数组A是n阶方阵,其定义如下
如果G是不带权图(或无权图),则:

如果G是带权图(或有权图),则:

 图2:2个邻接矩阵

邻接矩阵的基本结构运行算法代码

  1. const int MAXV=100; //图中最多的顶点数
  2. const int INF=0x3f3f3f3f; //用INF表示∞
  3. class MaxGraph
  4. {
  5. public:
  6. int edges[MAXV][MAXV]; //邻接矩阵数组,假设元素为int
  7. int n,e; //顶点数和边数
  8. string vexs[MAXV]; //存放顶点信息
  9. };

图的邻接矩阵存储结构的特点如下:
1、图的邻接矩阵表示是唯一的。
2、对于含有n个顶点的图,在采用邻接矩阵存储时,无论是有向图还是无向图,也无论边的数目是多少,其存储空间均为O(n^2)所以邻接矩阵适合干存储边数较多的调密图
3、无向图的邻接矩阵一定是对称矩阵,因此在顶点个数n很大时可以采用对称矩阵的压缩存储方法减少存储空间。有向图的邻接矩阵不一定是对称矩阵
4、对于无向图,邻接矩阵的第i行(或第i列)非零/非∞元素的个数正好是顶点i的度;对于有向图,邻接矩阵的第i行(或第i列)非零/非∞元素的个数正好是顶点i的出度(或入度)
5、用邻接矩阵存储图时,确定任意两个顶点之间是否有边相连的时间为0(1)

邻接矩阵代码实现

创建邻接矩阵

  1. void CreateMatGraph(int a[][MAXV],int n,int e)
  2. {
  3. this->n=n;
  4. this->e=e;
  5. for(int i=0;i<n;i++)
  6. {
  7. for(int j=0;j<n;j++)
  8. {
  9. this->edges[i][j]=a[i][j];
  10. }
  11. }
  12. }

输出图

  1. void DispMatGraph()
  2. {
  3. for(int i=0;i<n;i++)
  4. {
  5. for(int j=0;j<n;j++)
  6. {
  7. if(edge[i][j]==INF)
  8. printf("%4s","∞");
  9. else
  10. printf("%4d",edges[i][j]);
  11. printf("\n");
  12. }
  13. }
  14. }

邻接表

在含n个顶点的图G的邻接表中,顶点i(0≤i≤n-1)的每一条出边<i,j>(权值为W)对应一个边结点,顶点i的所有出边的边结点构成一个单链表,其中边结点的类型ArcNode
定义如下

  1. struct ArcNode //边结点类型
  2. {
  3. int adjvex; //邻接点
  4. int weight; //权值
  5. ArcNode* nextarc; //指向下一边的边结点
  6. };

给顶点i的单链表加上一个头结点,包含顶点i的信息和指向第一条边的边结点的指针,头结点的类型如下

  1. struct HNode //头结点类型
  2. {
  3. string info; //顶点信息
  4. ArcNode* firstarc; //指向第一条边的边结点
  5. };

图3:有向图的邻接表和有向图包含权值的邻接表

设计图的邻接表类AdjGraph如下

  1. template<class i>
  2. class AdjGraph //图邻接表类
  3. {
  4. public:
  5. HNode adjlist[MAXV]; //头结点数组
  6. int n, e;
  7. AdjGraph()
  8. {
  9. for (int i = 0; i < MAXV; i++)
  10. {
  11. adjlist[i].firstarc = NULL;
  12. }
  13. }
  14. ~AdjGraph()
  15. {
  16. ArcNode* pre, * p;
  17. for (int i = 0; i < n; i++) //遍历所有头结点
  18. {
  19. pre = adjlist[i].firstarc; //用一个ArcNode指针指向头结点的fitst区域
  20. if (pre != NULL) //若不为空
  21. {
  22. p = pre->nextarc; //则用另外一个ArcNode指针指向头结点的下一个结点
  23. while (p != NULL) //释放过程,循环删除
  24. {
  25. delete pre;
  26. pre = p; p = p->nextarc; //两个指针同步后移
  27. }
  28. delete pre;
  29. }
  30. }
  31. }
  32. void CreateAdjGraph(int a[][MAXV], int n, int e); //通过a,n,e来建立图的邻接表
  33. void DispAdjGraph(); //输出图的邻接表
  34. int Degree1(AdjGraph* G, int v); //计算无向图某个顶点的度,v为顶点名称
  35. i Degree2(AdjGraph& G, int v); //计算有向图某个顶点的出度和入度,v为顶点名称
  36. };

图的基本运算在邻接表中的实现

假设给定邻接矩阵数组啊,顶点数n和边数e来建立图的。

邻接表存储结构 
  1. template<typename i>
  2. void AdjGraph<i>::CreateAdjGraph(int a[][MAXV], int n, int e)
  3. {
  4. ArcNode* p;
  5. this->n = n; this->e = e;
  6. for (int i = 0; i < n; i++)
  7. {
  8. for (int j = n - 1; j >= 0; j--)
  9. {
  10. if (a[i][j] != 0 && a[i][j] != INF)
  11. {
  12. p = new ArcNode; //指针类型的构造函数
  13. p->adjvex = j;
  14. p->weight = a[i][j];
  15. p->nextarc = adjlist[i].firstarc; //采用头插法插入p
  16. adjlist[i].firstarc = p;
  17. }
  18. }
  19. }
  20. }
输出图邻接表
  1. template<typename i>
  2. void AdjGraph<i>::DispAdjGraph()
  3. {
  4. ArcNode* p;
  5. for (int i = 0; i < n; i++) //遍历每一个头结点
  6. {
  7. printf(" [%d]", i);
  8. p = adjlist[i].firstarc; //p指向第一个邻接点
  9. if (p != NULL)
  10. printf("->");
  11. while (p!= NULL)
  12. {
  13. printf("(%d,%d)", p->adjvex, p->weight);
  14. p = p->nextarc; //p移向下一个邻接点
  15. }
  16. printf("\n");
  17. }
  18. }
邻接表计算无向图某个顶点的度
  1. template<typename i>
  2. int AdjGraph<i>::Degree1(AdjGraph* G, int v) //主要是用来统计某个顶点v的边数的算法
  3. {
  4. int d = 0;
  5. ArcNode* p = G->adjlist[v].firstarc;
  6. while (p != NULL) //对顶点v中的边数进行统计
  7. {
  8. d++;
  9. p = p->nextarc;
  10. }
  11. return d;
  12. }
邻接表计算有向图某个顶点的出度和入度
  1. template<typename i>
  2. i AdjGraph<i>::Degree2(AdjGraph& G, int v)
  3. {
  4. i ans = { 0,0 }; //ans[o]累计出度,ans[i]累计入读
  5. ArcNode* p = G.adjlist[v].firstarc;
  6. while (p != NULL) //统计单链表v中顶点边结点个数
  7. {
  8. ans[0]++;
  9. p = p->nextarc;
  10. }
  11. for (int i = 0; i < G.n; i++) //统计所有adjvex为v的边结点个数即为顶点v的入读
  12. {
  13. p = G.adjlist[i].firstarc;
  14. while (p != NULL)
  15. {
  16. if (p->adjvex == v)
  17. {
  18. ans[1]++;
  19. break; //在一个单链表中最多只有一个这样的结点
  20. }
  21. p = p->nextarc;
  22. }
  23. }
  24. return ans; //返回出度和入读
  25. }

图的遍历

从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点访问一次且仅访问一次,这个过程称为图的遍历。图的遍历得到的顶点序列称为图遍历序列。

抽象操作,可以是对结点进行的各种处理,这里简化为输出结点的数据信息。
为了保证图中的各顶点在遍历过程中访问到且仅访问一次,需要为每个顶点设一个访问标志;
可以为图设置一个访问标志数组visited[n],用于标示图中每个顶点是否被访问过,它的所有数组元素初始值为0,表示顶点均未被访问,一旦访问过顶点vi,则置访问标志数组中的visited[i]为1,以表示该顶点已访问。 
 

 深度优先遍历

思想(先深搜索):
1、深度优先遍历是指在图中任选一个顶点V,访问V并以V作为初始点
2、选择一个与顶点V相邻且未曾访问过的顶点W,访问W,并以W作为新的出发点,继续进行深度优先遍历;
3、若访问到某个顶点时,发现该顶点的所有邻接点均已访问过,则回溯到前一个访问过的顶点,从该顶点出发继续进行深度优先遍历。直到一直回溯到出发顶点V,并且图中与顶点V邻接的所有顶点均被访问过
4、若此时图中任有未访问的顶点(说明是非连通图),则另选一个尚未访问的顶点作为新的出发点,重复上述过程,直到图中所有顶点均已被访问为止。

基本定义的结构体和类如下

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. const int INF = 0x3f3f3f3f;
  5. const int MAXV = 1000;
  6. struct ArcNode
  7. {
  8. int adjvex; //邻接点
  9. int weight; //权值
  10. ArcNode* nextarc; //指向下一条边的边结点
  11. };
  12. struct HNode
  13. {
  14. string info; //顶点信息
  15. ArcNode* firstarc; //指向第一条边的边结点
  16. };
  17. class AdjGraph //图邻接表类
  18. {
  19. public:
  20. HNode adjlist[MAXV]; //头结点数组
  21. int n, e;
  22. AdjGraph()
  23. {
  24. for (int i = 0; i < MAXV; i++)
  25. {
  26. adjlist[i].firstarc = NULL;
  27. }
  28. }
  29. ~AdjGraph()
  30. {
  31. ArcNode* pre, * p;
  32. for (int i = 0; i < n; i++) //遍历所有头结点
  33. {
  34. pre = adjlist[i].firstarc; //用一个ArcNode指针指向头结点的fitst区域
  35. if (pre != NULL) //若不为空
  36. {
  37. p = pre->nextarc; //则用另外一个ArcNode指针指向头结点的下一个结点
  38. while (p != NULL) //释放过程,循环删除
  39. {
  40. delete pre;
  41. pre = p; p = p->nextarc; //两个指针同步后移
  42. }
  43. delete pre;
  44. }
  45. }
  46. }
  47. void DFS(AdjGraph& G, int v); //深度优先遍历(邻接表)
  48. void BFS(AdjGraph& G, int v); //广度优先遍历(邻接表)
  49. };

深度优先遍历算法(邻接表)

  1. int visited[MAXV]; //全局标志数组,分辨顶点是否访问
  2. void AdjGraph::DFS(AdjGraph& G, int v)
  3. {
  4. cout << v << " "; //输出访问的顶点
  5. visited[v] = 1; //将访问的顶点置1
  6. ArcNode* p = G.adjlist[v].firstarc; //将p指向顶点v的第一个邻接点
  7. while (p != NULL)
  8. {
  9. int w = p->adjvex; //邻接点为w
  10. if (visited[w] == 0) //若w顶点未被访问,递归访问
  11. {
  12. DFS(G, w);
  13. }
  14. p = p->nextarc; //将p置为下一个邻接点
  15. }
  16. }

深度优先遍历算法(邻接矩阵)

邻接矩阵结构类型代码见上——邻接矩阵的基本结构运行算法代码

该代码是类内声明类外定义

  1. int visited[MAXV];
  2. void MaxGraph::DFS(MaxGraph& g, int v)
  3. {
  4. cout << v << " "; //访问顶点v
  5. visited[v] = 1; //置顶点v为1,表示已经访问
  6. for (int w = 0; w < g.n; w++)
  7. {
  8. if (g.edges[v][w] != 0 && g.edges[v][w] != INF)
  9. {
  10. if (visited[w] == 0) //存在边<v,w>并且w没有被访问
  11. {
  12. DFS(g, w); //若w顶点没有被访问,递归访问它
  13. }
  14. }
  15. }
  16. }

广度优先遍历

广度优先遍历算法(邻接表)

  1. void AdjGraph::BFS(AdjGraph& G, int v)
  2. {
  3. ArcNode* p; int queue[MAXV], first = 0, last = 0;//定义循环队列并初始化first=0,last=0,first指针指向队头元素的前一个位置,last指向队尾元素
  4. int w;
  5. cout << G.adjlist[v].info<< " "; // 访问v
  6. visited[v] = 1;
  7. //v进队
  8. last = (last + 1) % MAXV;
  9. queue[last] = v;
  10. while (first != last)//若循环队列不为空
  11. {
  12. first = (first + 1) % MAXV;
  13. w = queue[first];//出队并赋值给w
  14. p = G.adjlist[w].firstarc;//找到与顶点w邻接的第一个顶点
  15. while (p != NULL)//当p=NULL时,表示顶点的所有邻接点都被访问过,继续判断循环队列是否为空
  16. {
  17. if (visited[p->adjvex] == 0)//如果当前邻接顶点未被访问
  18. {
  19. cout << G.adjlist[p->adjvex].info << " "; // 访问相邻顶点
  20. visited[p->adjvex] = 1;// 置该顶点已被访问
  21. last = (last + 1) % MAXV;// 该顶点进队
  22. queue[last] = p->adjvex;
  23. }
  24. p = p->nextarc; //查看下一个邻接点
  25. }
  26. }
  27. }

广度优先遍历算法(邻接矩阵)

该代码是类内声明类外定义

  1. void MaxGraph::BFS(MaxGraph& g, int v)
  2. {
  3. int visited[MAXV];
  4. memset(visited, 0, sizeof(visited)); //初始化visited数组
  5. queue<int>qu; //定义一个队列
  6. cout << v << " "; //访问顶点v
  7. visited[v] = 1; //置已访问标记
  8. qu.push(v); //顶点v入队
  9. while (!qu.empty())
  10. {
  11. int u = qu.front(); qu.pop(); //出队顶点u
  12. for (int i = 0; i < g.n; i++)
  13. {
  14. if (g.edges[u][i] != 0 && g.edges[u][i] != INF)
  15. {
  16. if (visited[i] == 0) //存在边<u,i>并且顶点i未被访问
  17. {
  18. cout << i << " "; //访问邻接点i
  19. visited[i] = 1; //置已访问标记
  20. qu.push(i); //邻接点i入队
  21. }
  22. }
  23. }
  24. }
  25. }

生成树和最小生成树

生成树:包含了图中全部顶点的一个极小连通子图,所有顶点连通但又不形成回路,至少要有n-1条边,才可以使得全部顶点连通
由生成树的定义,可以看出生成树具有以下特点:
1、n个顶点(全部顶点)2、是连通的 3、有n-1条边  4、无回路 
避免:少——不连通    多——构成回路,不满足极小 
最小生成树:在连通网(带权图)中,各边权值之和最小的生成树称为最小生成树
 

 两种常用的构造最小生成树的算法:

Prim算法

  1. #include <iostream>
  2. using namespace std;
  3. #define MAXVEX 6//定义顶点最大个数
  4. #define INF 1000//定义无穷大为1000
  5. typedef char vertexType;//顶点类型定义为字符型
  6. struct Graph {
  7. vertexType vexs[MAXVEX];//存放顶点的一维数组
  8. int arc[MAXVEX][MAXVEX];//邻接矩阵,存放边
  9. int nVex, nEdge;//存放图中的顶点数和边数
  10. };
  11. void Prim(Graph g, int v)//Prim算法从顶点v出发构造最小生成树
  12. {
  13. int lowcost[MAXVEX];//lowcost数组,存储候选最短边的权值
  14. int min;
  15. int closest[MAXVEX];//closest数组,存储候选最短边,例如closest[i]=k,表示候选最短边(vi, vk)
  16. int i, j, k;
  17. for (i = 0; i < g.nVex; i++) //给lowcost[]和closest[]置初值
  18. {
  19. lowcost[i] = g.arc[i][v];
  20. closest[i] = v;
  21. }
  22. lowcost[v] = 0; //lowcost[v] = 0表示顶点v属于集合U
  23. for (i = 1; i <= g.nVex - 1; i++) //输出(n-1)条最小生成树的边
  24. {
  25. min = INF;//给min初值
  26. for (j = 0; j < g.nVex; j++)//在lowcost中找最小值,即在候选边中找最短边
  27. if (lowcost[j] != 0 && lowcost[j] < min)
  28. {
  29. min = lowcost[j];
  30. k = j; //记录最短边的下标
  31. }
  32. cout << " 边(" << closest[k] << "," << k << ")权为:" << min;
  33. cout << " 即:边(" << g.vexs[closest[k]] << "," << g.vexs[k] << ")权为:" << min << endl;
  34. lowcost[k] = 0; //标记顶点k已经加入U
  35. for (j = 0; j < g.nVex; j++) //新顶点k从集合V-U进入集合U后,修改更新数组lowcost和closest
  36. if (lowcost[j] != 0 && g.arc[j][k] < lowcost[j])
  37. {
  38. lowcost[j] = g.arc[j][k];
  39. closest[j] = k;
  40. }
  41. }
  42. }
  43. int main()
  44. {
  45. Graph g;//定义结构体变量g,用于存储图
  46. g.nVex = 6;//存储顶点个数
  47. g.nEdge = 10;//存储边的个数
  48. int i, j;
  49. for (i = 0; i < g.nVex; i++) //初始化邻接矩阵
  50. for (j = 0; j < g.nVex; j++)
  51. {
  52. g.arc[i][j] = INF;//边的权值初始化为无穷大
  53. }
  54. //存顶点
  55. g.vexs[0] = 'A'; g.vexs[1] = 'B'; g.vexs[2] = 'C';
  56. g.vexs[3] = 'D'; g.vexs[4] = 'E'; g.vexs[5] = 'F';
  57. //存边
  58. g.arc[0][1] = 6; g.arc[0][2] = 1; g.arc[0][3] = 5;
  59. g.arc[1][0] = 6; g.arc[1][2] = 5; g.arc[1][4] = 3;
  60. g.arc[2][0] = 1; g.arc[2][1] = 5; g.arc[2][3] = 5;
  61. g.arc[2][4] = 6; g.arc[2][5] = 4;
  62. g.arc[3][0] = 5; g.arc[3][2] = 5; g.arc[3][5] = 2;
  63. g.arc[4][1] = 3; g.arc[4][2] = 6; g.arc[4][5] = 6;
  64. g.arc[5][2] = 4; g.arc[5][3] = 2; g.arc[5][4] = 6;
  65. cout << " 最小生成树的边依次是:" << endl;
  66. Prim(g, 0);//从顶点0出发构造最小生成树
  67. return 0;
  68. }

kruskal算法

最小生成树的初始状态为只有n个顶点而无边。此时,图中每个顶点自成一个连通分量
然后,按照边的权值由小到大的顺序,考察G的边集E中的各条边。
若被考察的边的两个顶点属于T的两个不同的连通分量,则将此边作为最小生成树的边加入到T中,同时把两个连通分量连接为一个连通分量
若被考察边的两个顶点属于同一个连通分量,则不能选择此边,以免造成回路,继续考察下一条边,如此下去,当T中的连通分量个数为1时,此连通分量便为G的一棵最小生成树 。

  1. #include <iostream>
  2. using namespace std;
  3. #define MAXUEX 6//定义顶点最大个数
  4. #define INF 1000//定义无穷大为1000
  5. typedef char vertexType;//顶点类型定义为字符型
  6. struct Graph {
  7. vertexType vexs[MAXUEX];//顶点表,存放顶点的一维数组
  8. int arc[MAXUEX][MAXUEX];//邻接矩阵,存放边
  9. int nVex, nEdge;//存放图中的顶点数和边数
  10. };
  11. struct Edge
  12. {
  13. int u;//边的起始顶点
  14. int v;//边的终止顶点
  15. int w;//边的权值
  16. };
  17. void InsertSort(Edge E[], int n)//插入排序,对E数组所存的边按权值递增排序
  18. {
  19. int i, j; //循环变量
  20. Edge temp; //存储待排序元素
  21. for (i = 1; i < n; i++) {
  22. j = i;
  23. temp = E[i]; //待排序元素赋值给临时变量
  24. while (j > 0 && temp.w < E[j - 1].w) { //当未达到数组的第一个元素或者待插入元素小于当前元素
  25. E[j] = E[j - 1]; //就将该元素后移
  26. j--; //下标减1,继续比较
  27. }
  28. E[j] = temp; //插入位置已经找到,立即插入
  29. }
  30. }
  31. void Kruskal(Graph g)
  32. {
  33. int i, j, u1, v1, sn1, sn2, k;
  34. int vset[MAXUEX];//记录每个顶点所在的连通分量编号
  35. Edge E[10];//定义结构体数组E,用于存储边
  36. k = 0; //E数组从下标0开始存储边
  37. for (i = 0; i < g.nVex; i++) //将邻接矩阵arc[][]中的边存储到数组E
  38. for (j = 0; j <= i; j++)
  39. if (g.arc[i][j] != INF)
  40. {
  41. E[k].u = i; E[k].v = j; E[k].w = g.arc[i][j];
  42. k++;
  43. }
  44. InsertSort(E, g.nEdge); //对E数组按边权值递增排序
  45. for (i = 0; i < g.nVex; i++) //初始化辅助数组,记录每个顶点的连通分量编号
  46. vset[i] = i;//每个顶点各自为一个连通分量
  47. k = 1; //k表示当前构造生成树的第几条边,用于控制循环次数
  48. j = 0; //从E数组中下标0处开始选边
  49. while (k <= g.nVex - 1) //循环n-1次,生成最小生成树的n-1条边
  50. {
  51. u1 = E[j].u; v1 = E[j].v; //取一条边的头尾顶点
  52. sn1 = vset[u1];
  53. sn2 = vset[v1]; //分别得到两个顶点所属的连通分量编号
  54. if (sn1 != sn2) //如果两顶点属于不同的连通分量,这条边可以选为最小生成树的边
  55. {
  56. cout << " 边(" << u1 << "," << v1 << ")权为:" << E[j].w;
  57. cout << " 即:边(" << g.vexs[u1] << "," << g.vexs[v1] << ")权为:" << E[j].w << endl;
  58. k++; //最小生成树的边数增1
  59. for (i = 0; i < g.nVex; i++) //两个不同的连通分量统一编号
  60. if (vset[i] == sn2) //连通分量编号为sn2的顶点改为sn1
  61. vset[i] = sn1;
  62. }
  63. j++; //找E数组中的下一条边
  64. }
  65. }
  66. int main()
  67. {
  68. Graph g;//定义结构体变量g,用于存储图
  69. g.nVex = 6;//存储顶点个数
  70. g.nEdge = 10;//存储边的个数
  71. for (int i = 0; i < g.nVex; i++) //初始化邻接矩阵
  72. for (int j = 0; j < g.nVex; j++)
  73. {
  74. g.arc[i][j] = INF;//边的权值初始化为无穷大
  75. }
  76. //存顶点
  77. g.vexs[0] = 'A'; g.vexs[1] = 'B'; g.vexs[2] = 'C';
  78. g.vexs[3] = 'D'; g.vexs[4] = 'E'; g.vexs[5] = 'F';
  79. //存边
  80. g.arc[0][1] = 6; g.arc[0][2] = 1; g.arc[0][3] = 5;
  81. g.arc[1][0] = 6; g.arc[1][2] = 5; g.arc[1][4] = 3;
  82. g.arc[2][0] = 1; g.arc[2][1] = 5; g.arc[2][3] = 5;
  83. g.arc[2][4] = 6; g.arc[2][5] = 4;
  84. g.arc[3][0] = 5; g.arc[3][2] = 5; g.arc[3][5] = 2;
  85. g.arc[4][1] = 3; g.arc[4][2] = 6; g.arc[4][5] = 6;
  86. g.arc[5][2] = 4; g.arc[5][3] = 2; g.arc[5][4] = 6;
  87. cout << " 最小生成树的边依次是:" << endl;
  88. Kruskal(g);
  89. system("pause");
  90. return 0;
  91. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/733015
推荐阅读
相关标签
  

闽ICP备14008679号