当前位置:   article > 正文

邻接表表示图进行深度优先搜索,广度优先搜索,最小生成树_邻接表画深度优先生成树

邻接表画深度优先生成树

图的邻接表定义

下面用邻接表实现图的深度优先搜索和广度优先搜索,用邻接矩阵来实现最小生成树。

图的邻接表:首先定义一个图的邻接表的类,里面包括图的顶点数,图的边数,顶点表数组。由于顶点表数组里存放的都是图的一个个节点,因此需要用到顶点节点和边的节点。定义顶点节点的结构体中数据域中存放顶点的信息,指针域指向边表的第一个节点,用first来表示。定义边节点的数据域中存放被指向的节点的信息,指针域指向下一个边表节点,用next表示。

图的邻接表的类的成员函数包括图的构造函数,深度优先搜索,广度优先搜索。下面给出图的邻接表的类的定义以及顶点节点和边节点的定义。

  1. #define Maxvernum 20
  2. #define inf 0x3f3f3f3f
  3. //边表(节点)
  4. template<typename datatype>
  5. struct arcnode
  6. {
  7. int adjvex;//所指向的顶点是哪个(用两个节点在逻辑上连接了一条弧)
  8. arcnode<datatype>* next; //指向此边表的下一个节点(同样也是用两个节点在逻辑上连接了一条弧)
  9. };
  10. //顶点表(节点,边表的特殊情况)
  11. template<typename datatype>
  12. struct vertexNode
  13. {
  14. datatype data;//顶点信息
  15. arcnode<datatype>* first;//指向的第一个节点
  16. };
  17. //图类(用邻接表的方式存储图)
  18. template<typename datatype>
  19. class graph
  20. {
  21. public:
  22. graph();//图的构造函数
  23. void bfs(datatype);//广度优先搜索
  24. void dfs(datatype);//深度优先搜索
  25. private:
  26. int vernum;//顶点数
  27. int arcnum;//边数
  28. vertexNode<datatype> adj_list[Maxvernum];//顶点表
  29. };

图的邻接表构造函数

首先先输入邻接表的顶点数和边数,接下来输入每个顶点的值,将其存放在顶点表中每个顶点元素的数据域当中,将当前顶点表的每个节点的first都设为空。接下来按照边的数目,依次输入哪两个节点之间存在边,并在邻接表中将他们互相连接到各自的边表中,比如A和B相连,就让A的边表节点中有B,B的边表节点中有A,按照这个方法依次构建,就构建出了图的邻接表。

广度优先搜索

广度优先搜索类似于树中按层序遍历,一层层进行遍历,得到广度优先遍历序列。在实现的时候用到了队列这种数据结构。为了避免重复搜索的情况出现,因此要设置一个标记数组,用于标记已经被搜索过的节点。首先先选取一个要从其开始进行搜索的节点,将其放入队列并标记,接下来依次遍历它的边表节点,只要没被标记,就将其入队,直到这个节点的边表节点被全部搜索完毕,输出这个顶点的值,并将其出队,下次取队头元素,继续按照上面的步骤进行搜索队头节点的边表节点,依次类推,直到队列为空,则说明广度优先遍历完成。代码如下

  1. //广度优先搜索
  2. template<typename datatype>
  3. void graph<datatype>::bfs(datatype e)
  4. {
  5. queue<datatype> q;
  6. int visited[Maxvernum] = { 0 };
  7. q.push(e);//从编号为e的这个顶点开始
  8. visited[e] = 1;
  9. int s = e;
  10. while (!q.empty())
  11. {
  12. s = q.front();//取队头元素
  13. arcnode<datatype>* tmp = adj_list[s].first;
  14. while (tmp!=NULL)
  15. {
  16. if (visited[tmp->adjvex] != 1)
  17. {
  18. q.push(tmp->adjvex);
  19. visited[tmp->adjvex] = 1;
  20. }
  21. tmp = tmp->next;
  22. }
  23. cout << adj_list[q.front()].data << " ";
  24. q.pop();
  25. }
  26. }

深度优先搜索

深度优先遍历是使用递归的思想,这里我们采用栈这种数据结构来模拟递归的实现。深度优先搜索同样需要一个标记数组来标记已经被搜索过的节点,避免重复搜索。深搜是沿着一条路搜到底,直到没有节点可以继续往下搜,再逐层返回得到。首先先确定从某个节点开始进行深搜,并将这个节点进行标记,然后沿着这个节点的边表进行搜索,如果遇到没被搜索过的节点,就将其入栈,直到这个节点的边表被全部搜索完,接下来取栈顶元素,对其进行打印(这里注意与广搜不同,要先进行打印),然后再对这个元素继续上述的操作,以此类推,直到栈为空,则说明深度优先搜索已经完成,可以结束。代码如下

  1. //深度优先搜索
  2. template<typename datatype>
  3. void graph<datatype>::dfs(datatype e)
  4. {
  5. stack<int> st;
  6. int visited[Maxvernum] = { 0 };
  7. st.push(e);
  8. int s = e;
  9. visited[e] = 1;
  10. while (!st.empty())
  11. {
  12. s = st.top();
  13. arcnode<datatype>* tmp = adj_list[s].first;
  14. cout << adj_list[st.top()].data << " ";
  15. st.pop();
  16. while (tmp != NULL)
  17. {
  18. if (visited[tmp->adjvex] != 1)
  19. {
  20. st.push(tmp->adjvex);
  21. visited[tmp->adjvex] = 1;
  22. }
  23. tmp = tmp->next;
  24. }
  25. }
  26. }

最小生成树

图的邻接矩阵定义

最小生成树用图的邻接矩阵的方式来编写,首先给出图的邻接矩阵的定义。其私有成员包括图的顶点数,边数,顶点数组,邻接矩阵的二维数组,成员函数包括构造函数,打印邻接矩阵的函数,生成最小生成树算法的函数。定义代码如下

  1. //图类(用邻接矩阵的形式存储)
  2. template<typename datatype>
  3. class graph2
  4. {
  5. public:
  6. graph2();//图(邻接矩阵)的构造函数
  7. void show_matrix();//打印邻接矩阵
  8. void prim();//prim最小生成树算法
  9. private:
  10. int vernum2;//顶点数
  11. int arcnum2;//边数
  12. datatype vertex[Maxvernum];//顶点数组
  13. int graph_matrix[Maxvernum][Maxvernum];//图的邻接矩阵
  14. };

构造函数

根据顶点数建立相应大小的二维矩阵,然后把每个节点先设成inf(无穷大),然后根据输入确定哪两个节点之间有边,并确定它们之间的权值,然后将相应的位置的元素的数值设置为对应的权值,这里注意graph_matrix[i][j]和graph_matrix[j][i]都要设置相同的权值,因为两个节点连着的边是同一条边(因为是无向图,对应的邻接矩阵是一个对称矩阵)。代码如下

  1. //图(邻接矩阵)的构造函数
  2. template<typename datatype>
  3. graph2<datatype>::graph2()
  4. {
  5. int weight = 0;
  6. cout << "请输入顶点数和边数" << endl;
  7. cin >> vernum2 >> arcnum2;
  8. for (int i = 0; i < vernum2; i++)
  9. {
  10. for (int j = 0; j < vernum2; j++)
  11. {
  12. graph_matrix[i][j] = inf;
  13. }
  14. }
  15. cout << "请输入各个顶点" << endl;
  16. for (int i = 0; i < vernum2; i++)
  17. {
  18. cin >> vertex[i];
  19. }
  20. cout << "请输入哪两个点之间有边,以及这两个边的权值" << endl;
  21. for (int i = 0; i < arcnum2; i++)
  22. {
  23. int m = 0, n = 0;
  24. cin >> m >> n>>weight;
  25. graph_matrix[m][n] = weight;
  26. graph_matrix[n][m] = weight;
  27. }
  28. }

打印邻接矩阵函数

代码如下

  1. //打印邻接矩阵
  2. template<typename datatype>
  3. void graph2<datatype>::show_matrix()
  4. {
  5. for (int i = 0; i < vernum2; i++)
  6. {
  7. for (int j = 0; j < vernum2; j++)
  8. {
  9. cout << graph_matrix[i][j] << " ";
  10. }
  11. cout << endl;
  12. }
  13. }

prim最小生成树算法

要生成最小生成树,采用不断将新的节点并入到最小生成树当中,直到没有节点被并入最小生成树了,那么就说明最小生成树已经完成了,因此还需要一个标记数组isjoined来判断节点是否被标记。首先先选取一个顶点作为最小生成树的起始点,首先要将这个点并入到最小生成树的集合当中。接下来需要寻找下一个能够被并入到最小生成树集合中的节点,每次选一个连接到当前最小生成树集合中花费最小的节点,因此需要一个lowcost数组来记录每个节点到最小生成树集合的花费,并且在每次最小生成树集合更新的时候,这些值如果想被更新,只可能比原来的花费更小,才会被更新。接下来在lowcost数组中寻找花费最小的节点,并将其并入到最小生成树集合中,并将这个节点标记,并将其加入到最小花费之和sum中,依次类推,直到所有节点都被标记,那么说明此时的sum就是最小生成树的最小花费。代码如下

  1. //prim最小生成树算法(输出最小生成树的权值之和)
  2. template<typename datatype>
  3. void graph2<datatype>::prim()
  4. {
  5. int sum = 0;//权值之和
  6. int flag = 0;
  7. int isjoined[Maxvernum] = {0};//标记数组,判断是否被并入最小生成树,先全置为1,表示都未被标记过
  8. int lowcost[Maxvernum] = { 0 };//最低耗费的数组
  9. for (int i = 0; i < Maxvernum; i++)
  10. isjoined[i] = 1;
  11. for (int i = 0; i < Maxvernum; i++)
  12. lowcost[i] = inf;
  13. int vertex = 0;
  14. cin >> vertex;//从哪个顶点开始进行最小生成树
  15. isjoined[vertex] = 0;//将这个点标记
  16. for (int k = 0; k < vernum2; k++)
  17. flag += isjoined[k];
  18. while (flag)//一旦标记数组为0,则说明所有节点的最小生成树已经完成
  19. {
  20. for (int i = 0; i < vernum2; i++)//如果当前节点未被标记过,那么就判断每个未被标记的节点与上一次刚加入到
  21. { //已经连接好的最小生成树中的节点直接是否会存在更短的路径,如果存在
  22. //就将其更新,如果比lowcost[i]大,那么就不更新
  23. if (isjoined[i] == 1)
  24. {
  25. lowcost[i] = min(lowcost[i], graph_matrix[vertex][i]);
  26. }
  27. }
  28. int minx = inf;
  29. for (int i = 0; i < vernum2; i++)
  30. {
  31. if (lowcost[i] < minx && isjoined[i] == 1)//在未被标记过的节点中寻找权值最小的节点,并标记
  32. {
  33. vertex = i;
  34. minx = lowcost[i];
  35. }
  36. }
  37. isjoined[vertex] = 0;
  38. sum += minx;//加上新找到的那个节点与已经标记过的集合中的最小消耗
  39. int u = 0;
  40. for (int k = 0; k < vernum2; k++)
  41. {
  42. u+=isjoined[k];
  43. }
  44. flag = u;//更改标记数组的总和
  45. }
  46. cout << sum << endl;
  47. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号