当前位置:   article > 正文

数据结构 第六章(图)【下】

数据结构 第六章(图)【下】

写在前面:

  1. 本系列笔记主要以《数据结构(C语言版)》为参考(本章部分图片来源于王道),结合下方视频教程对数据结构的相关知识点进行梳理。所有代码块使用的都是C语言,如有错误欢迎指出。
  2. 视频链接:第01周a--前言_哔哩哔哩_bilibili

四、图的应用

1、最小生成树

(1)在一个连通网的所有生成树中,各边的代价(权值)之和最小的那棵生成树称为该连通网的最小代价生成树,简称为最小生成树。

(2)构造最小生成树有多种算法,其中多数算法利用了最小生成树的一种简称为MST的性质:假设N=(V, E)是一个连通网,U是顶点集V的一个非空子集,若(u, v)是一条具有最小权值(代价)的边,其中uϵU(已落在生成树上的顶点集)、vϵV-U(尚未落在生成树上的顶点集),则必存在一棵包含边(u, v)的最小生成树。

(3)普里姆算法

①算法步骤:

②算法实现:

[1]辅助部分:

  1. struct Closedge
  2. {
  3. VerTexType adjvex; //最小边在U中的那个顶点
  4. ArcType lowcost; //最小边上的权值
  5. }closedge[MVNum]; //辅助数组,用来记录从顶点集U到V-U的权值最小的边
  6. int Min(Closedge *U, int n)
  7. {
  8. int min = INT_MAX;
  9. int pos = 0;
  10. for (int i = 0; i < n; ++i)
  11. {
  12. if (U[i].lowcost != 0 && U[i].lowcost < min)
  13. {
  14. //U[i].weight != 0 说明不在U中,即V-U
  15. min = U[i].lowcost;
  16. pos = i;
  17. }
  18. }
  19. return pos;
  20. }

[2]核心部分:

  1. void MiniSpanTree_Prim(AMGraph G, VerTexType u)
  2. {
  3. //无向网G以邻接矩阵形式存储,从顶点u触发构造G的最小生成树T,输出T的各条边
  4. int k; //k为顶点u的下标
  5. for (k = 0; k < G.vexnum; k++) //确定u在G中的位置,即顶点在G.vexs中的序号
  6. {
  7. if (u == G.vexs[k])
  8. break;
  9. }
  10. for (int j = 0; j < G.vexnum; j++) //对V-U的每一个顶点初始化
  11. {
  12. if (j != k)
  13. closedge[j] = { u,G.arcs[k][j] };
  14. }
  15. closedge[k].lowcost = 0; //U集中加入顶点u
  16. for (int i = 1; i < G.vexnum; i++)
  17. {
  18. //选择其余n-1个顶点,生成n-1条边
  19. k = Min(closedge, G.vexnum); //在V-U中选取权值最小的边
  20. VerTexType u0 = closedge[k].adjvex; //u0为最小边的一个顶点,属于U
  21. VerTexType v0 = G.vexs[k]; //v0位最小边的另一个顶点,属于V-U
  22. printf("%c->%c ", u0, v0);
  23. closedge[k].lowcost = 0; //第k个顶点并入U集
  24. for (int j = 0; j < G.vexnum; j++)
  25. {
  26. if (G.arcs[k][j] < closedge[j].lowcost) //新顶点并入U后重新选择最小边
  27. closedge[j] = { G.vexs[k],G.arcs[k][j] };
  28. }
  29. }
  30. }

普里姆算法的时间复杂度为O(n2),与网中的边数无关,因此适用于求稠密网的最小生成树。

(4)克鲁斯卡尔算法

①算法步骤:

②算法实现:

[1]辅助部分:

  1. struct Edge
  2. {
  3. VerTexType Head; //边的始点
  4. VerTexType Tail; //边的终点
  5. ArcType lowcost; //边上的权值
  6. }edge[MVNum]; //存储边的信息
  7. int Vexset[MVNum]; //辅助数组
  8. void Sort(Edge *E, int length) //采用冒泡排序将数组edge中的元素按权值从小到大排序
  9. {
  10. bool flag = true; //排序flag
  11. for (int i = 0; i < length - 1 && flag; ++i) //如果未发生交换则说明有序
  12. {
  13. flag = false; //第一次设置为false,若某轮循环结束后标志仍为false,说明排序可以结束
  14. for (int j = 0; j < length - 1 - i; ++j)
  15. {
  16. if (E[j].lowcost > E[j + 1].lowcost)
  17. {
  18. flag = true; //如果发生交换,标志为true
  19. Edge temp = E[j];
  20. E[j] = E[j + 1];
  21. E[j + 1] = temp;
  22. }
  23. }
  24. }
  25. }

[2]核心部分:

  1. void MiniSpanTree_Kruskal(AMGraph G)
  2. {
  3. //无向网G以邻接矩阵形式存储,构造G的最小生成树T,输出T的各条边
  4. Edge *edge = (Edge*)malloc(sizeof(Edge) * G.arcnum); //为G建立Edge数组
  5. Edge *p = edge;
  6. for (int i = 0; i < G.vexnum; i++) //初始化Edge数组(无向图遍历邻接矩阵的上三角即可)
  7. {
  8. for (int k = i + 1; k < G.vexnum; k++)
  9. {
  10. if (G.arcs[i][k] < MaxInt)
  11. {
  12. p->Head = G.vexs[i];
  13. p->Tail = G.vexs[k];
  14. p->lowcost = G.arcs[i][k];
  15. p++;
  16. }
  17. }
  18. }
  19. Sort(edge, G.arcnum); //Edge中的元素按权值升序排序
  20. for (int i = 0; i < G.arcnum; i++)
  21. Vexset[i] = i; //辅助数组,表示各顶点自成一个连通分量
  22. for (int i = 0; i < G.arcnum; i++)
  23. {
  24. int v1, v2;
  25. for (v1 = 0; v1 < G.vexnum; v1++) //v1为边的始点Head的下标
  26. if (edge[i].Head == G.vexs[v1])
  27. break;
  28. for (v2 = 0; v2 < G.vexnum; v2++) //v2为边的终点Tail的下标
  29. if (edge[i].Tail == G.vexs[v2])
  30. break;
  31. int vs1 = Vexset[v1]; //获取边edge[i]的始点所在的连通分量vs1
  32. int vs2 = Vexset[v2]; //获取边edge[i]的终点所在的连通分量vs2
  33. if (vs1 != vs2) //边的两个顶点分属不同的连通分量
  34. {
  35. printf("%c->%c ", edge[i].Head, edge[i].Tail); //输出此边
  36. for (int j = 0; j < G.vexnum; j++) //合并vs1和vs2两个分量,两个集合统一编号
  37. if (Vexset[j] == vs2)
  38. Vexset[j] = vs1; //集合编号为vs2的都改为vs1
  39. }
  40. }
  41. }

③克鲁斯卡尔算法的时间复杂度为O\left ( elog_{2}e \right ),与网中的边数有关。与普里姆算法相比,克鲁斯卡尔算法更适合于求稀疏网的最小生成树。

2、最短路径

(1)在带权有向网中,习惯上称路径上的第一个顶点为源点,最后一个顶点为终点。

(2)求从某个源点到其余各顶点的最短路径——迪杰斯特拉算法

①算法思路:

②算法步骤:

③算法实现:

  1. void ShortestPath_DIJ(AMGraph G, int v0, int* path)
  2. {
  3. //用Dijkstra算法求有向网的v0顶点到其余顶点的最短路径
  4. int n = G.vexnum; //G中顶点个数
  5. int v;
  6. bool* S = (bool*)malloc(sizeof(bool)*n);
  7. ArcType* D = (ArcType*)malloc(sizeof(ArcType)*n);
  8. for (v = 0; v < n; v++)
  9. {
  10. S[v] = false; //S初始为空集
  11. D[v] = G.arcs[v0][v]; //将v0到各个终点的最短路径长度初始化为弧上的权值
  12. if (D[v] < MaxInt)
  13. path[v] = v0; //v0和v之间有弧,v的前驱置为v0
  14. else
  15. path[v] = -1; //v0和v之间无弧,v的前驱置为-1
  16. }
  17. S[v0] = true; //将v0加入S
  18. D[v0] = 0; //源点到源点的距离为0
  19. for (int i = 1; i < n; i++)
  20. {
  21. int min = MaxInt;
  22. for (int j = 0;j < n;j++)
  23. if (!S[j] && D[j] < min)
  24. {
  25. v = j;
  26. min = D[j];
  27. } //选择一条当前的最短路径,终点为v
  28. S[v] = true; //将v加入S
  29. for (int j = 0;j < n;j++) //更新从v0出发到集合V-S上所有顶点的最短路径长度
  30. if (!S[j] && (D[v] + G.arcs[v][j] < D[j]))
  31. {
  32. D[j] = D[v] + G.arcs[v][j]; //更新D[j]
  33. path[j] = v; //更新j的前驱为v
  34. }
  35. }
  36. }

④该算法的时间复杂度为O(n^{2})。

⑤举例:

[1]对六个顶点依次初始化,结果如下:

v

0

1

2

3

4

5

S

true

false

false

false

false

false

D

0

10

30

100

Path

-1

-1

0

-1

0

0

[2]从v_{0}到各终点的最短路径长度D和最短路径的求解过程:

(3)求每一对顶点之间的最短路径——弗洛伊德算法

①算法思路:逐个顶点试探,从v_{i}v_{j}的所有可能存在的路径中选出一条长度最短的路径。

②算法步骤:

③算法实现:

  1. void ShortestPath_Floyd(AMGraph G, int** path)
  2. {
  3. //用Floyd算法求有向网G中各对顶点i和j之间的最短路径
  4. bool* S = (bool*)malloc(sizeof(bool)*G.vexnum);
  5. ArcType** D = (ArcType**)malloc(sizeof(ArcType*)*G.vexnum);
  6. for (int i = 0; i < G.vexnum; i++)
  7. {
  8. D[i] = (ArcType*)malloc(sizeof(ArcType)*G.vexnum);
  9. }
  10. for (int i = 0; i < G.vexnum; i++) //初始化n阶方阵
  11. {
  12. for (int j = 0; j < G.vexnum; j++)
  13. {
  14. D[i][j] = G.arcs[i][j];
  15. if (D[i][j] < MaxInt && i != j)
  16. path[i][j] = i; //i和j之间有弧,j的前驱置为i
  17. else
  18. path[i][j] = -1; //i和j之间无弧,j的前驱置为-1
  19. }
  20. }
  21. for (int k = 0; k < G.vexnum; k++)
  22. for (int i = 0; i < G.vexnum; i++)
  23. for (int j = 0; j < G.vexnum; j++)
  24. if (D[i][k] + D[k][j] < D[i][j])
  25. { //从i经k到j的一条路径更短,更新D[i][j]和j的前驱
  26. D[i][j] = D[i][k] + D[k][j];
  27. path[i][j] = path[k][j];
  28. }
  29. }

④该算法的时间复杂度为O(n^{3})。

⑤举例:

3、拓扑排序

(1)AOV—网:

(2)拓扑排序的过程:

(3)拓扑排序算法实现:

  1. Status TopologicalSort(ALGraph G, int topo[])
  2. {
  3. //有向图G采用邻接表作为存储结构
  4. //若G无回路,则生成G的一个拓扑序列topo并返回OK,否则返回ERROR
  5. int *indegree = (int*)malloc(sizeof(int)*G.vexnum); //存储所有顶点的入度的数组
  6. memset(indegree, 0, sizeof(int)*G.vexnum); //初始化indegree数组,各顶点入度为0
  7. for (int i = 0; i < G.vexnum; ++i) //分别对每个顶点求入度
  8. {
  9. ArcNode *p = G.vertices[i].firstarc; //指向首个邻接点
  10. while (p)
  11. {
  12. indegree[p->adjvex]++; //入度+1
  13. p = p->nextarc; //指向下一个邻接点
  14. }
  15. }
  16. SqStack S;
  17. InitStack(&S); //栈S初始化为空
  18. for (int i = 0; i < G.vexnum; i++) //入度为0者进栈
  19. if (!indegree[i])
  20. Push(&S, i);
  21. int m = 0; //对输出顶点计数
  22. while (!StackEmpty(S))
  23. {
  24. int i;
  25. Pop(&S, &i); //使栈顶顶点vi出栈
  26. topo[m] = i; //将顶点vi保存在拓扑序列数组topo中
  27. m++; //输出顶点数+1
  28. ArcNode *p = G.vertices[i].firstarc; //指向vi的第一个邻接点
  29. while (p)
  30. {
  31. int k = p->adjvex; //vk为vi的邻接点
  32. indegree[k]--; //vi的每个邻接点的入度-1
  33. if (indegree[k] == 0) //入度减为0,入栈
  34. Push(&S, k);
  35. p = p->nextarc; //指向下一个邻接点
  36. }
  37. }
  38. if (m < G.vexnum) return ERROR;
  39. else return OK;
  40. }

(4)有向无环图可以用于描述表达式,因为运算符有不同的优先级,需要完成高优先级的运算才能接着完成低优先级的运算。

4、关键路径

(1)AOE—网:

(2)关键路径求解过程:

①首先定义4个描述量:

②一个活动a_{i}的最迟开始时间l(i)和其最早开始时间e(i)的差值是该活动完成的时间余量,它是在不增加完成整个工程所需的总时间的情况下,活动a_{i}可以拖延的时间。当一活动的时间余量为0时,说明该活动必须如期完成,否则就会拖延整个工期,所以称l(i)-e(i)=0的活动a_{i}是关键活动

③对图中顶点进行排序,在排序过程中按拓扑序列求出每个事件的最早发生时间ve(i)。

④按逆拓扑序列求出每个事件的最迟发生时间vl(i)。

⑤求出每个活动a_{i}的最早开始时间e(i)。

⑥求出每个活动a_{i}的最晚开始时间(i)。

⑦找出e(i)=l(i)的活动a_{i},即关键活动,由关键活动形成的由源点到汇点的每一条路径就是关键路径(关键路径有可能不止一条)。

(3)关键路径算法实现:

  1. Status CriticalPath(ALGraph G)
  2. {
  3. //G为邻接表存储的有向网,输出G的各项关键活动
  4. int *topo = (int*)malloc(sizeof(int)*G.vexnum);
  5. if (TopologicalSort(G, topo) == OK) //拓扑排序检查是否存在有向环,获取拓扑序列
  6. return ERROR;
  7. int *ve = (int*)malloc(sizeof(int*)*G.vexnum);
  8. int *vl = (int*)malloc(sizeof(int*)*G.vexnum);
  9. for (int i = 0; i < G.vexnum; i++)
  10. ve[i] = 0; //给每个事件的最早发生时间置初值0
  11. /*按拓扑次序求每个事件的最早发生时间*/
  12. for (int i = 0; i < G.vexnum; i++)
  13. {
  14. int k = topo[i]; //取得拓扑序列中的顶点序号k
  15. ArcNode* p = G.vertices[k].firstarc; //指向k的第一个邻接点
  16. while (p)
  17. {
  18. int j = p->adjvex; //邻接顶点的序号
  19. if (ve[j] < ve[k] + p->info) //更新顶点j的最早发生时间ve[j]
  20. ve[j] = ve[k] + p->info;
  21. p = p->nextarc; //指向k的下一个邻接点
  22. }
  23. }
  24. for (int i = 0; i < G.vexnum; i++)
  25. vl[i] = ve[G.vexnum - 1]; //给每个事件的最迟发生时间置初值ve[n-1]
  26. /*按逆拓扑次序求每个事件的最晚发生时间*/
  27. for (int i = G.vexnum - 1; i >= 0; i--)
  28. {
  29. int k = topo[i]; //取得拓扑序列中的顶点序号k
  30. ArcNode* p = G.vertices[k].firstarc; //指向k的第一个邻接点
  31. while (p)
  32. {
  33. int j = p->adjvex; //邻接顶点的序号
  34. if (vl[k] < vl[j] + p->info) //更新顶点k的最迟发生时间vl[k]
  35. vl[k] = vl[j] + p->info;
  36. p = p->nextarc; //指向k的下一个邻接点
  37. }
  38. }
  39. /*判断每一活动是否为关键活动*/
  40. for (int i = 0; i < G.vexnum; i++)
  41. {
  42. ArcNode* p = G.vertices[i].firstarc; //指向i的第一个邻接点
  43. while (p)
  44. {
  45. int j = p->adjvex; //邻接顶点的序号
  46. int e = ve[i]; //计算活动的最早开始时间
  47. int l = vl[j] - p->info; //计算活动的最晚开始时间
  48. if (e == l) //若为关键活动,输出之
  49. printf("%d-%d ", G.vertices[i].data, G.vertices[i].data);
  50. p = p->nextarc; //指向i的下一个邻接点
  51. }
  52. }
  53. }

(4)使用该算法的注意事项:

①若网中有几条关键路径,则需加快同时在几条关键路径上的关键活动。

②如果一个活动处于所有的关键路径上,那么提高这个活动的速度就能缩短整个工程的完成时间。

③处于所有的关键路径上的活动完成时间缩短太多的话,可能会使原来的关键路径变为非关键路径,这时需要重新寻找关键路径。

五、算法设计举例

1、例1

(1)问题描述:分别以邻接矩阵和邻接表作为存储结构,实现无向网(使用邻接矩阵)/无向图(使用邻接表)的几个基本操作,分别为增加一个新顶点、删除一个顶点及其相关的边、增加一条边和删除一条边。

(2)代码:

①邻接矩阵部分:

  1. Status InsertVex(AMGraph &G, VerTexType v) //增加一个新顶点v
  2. {
  3. if (G.vexnum == MVNum)
  4. return OVERFLOW; //顶点数已达最大值
  5. G.vexs[G.vexnum] = v;
  6. for (int i = 0; i <= G.vexnum; i++) //仅增加顶点,不增加与其相关的边
  7. {
  8. G.arcs[i][G.vexnum] = MaxInt;
  9. G.arcs[G.vexnum][i] = MaxInt;
  10. }
  11. G.vexnum++; //当前顶点数+1
  12. return OK;
  13. }
  14. Status DeleteVex(AMGraph &G, VerTexType v) //删除顶点v及其相关的边
  15. {
  16. if (G.vexnum == 0)
  17. return ERROR; //没有顶点可删
  18. int i;
  19. for (i = 0; i < G.vexnum; i++) //找到顶点v所在位置
  20. {
  21. if (G.vexs[i] == v)
  22. break;
  23. }
  24. if (i != G.vexnum) //如果顶点v存在于图G中,可以进行删除(逻辑删除)
  25. {
  26. VerTexType tmp = G.vexs[i];
  27. G.vexs[i] = G.vexs[G.vexnum - 1];
  28. G.vexs[G.vexnum - 1] = tmp; //vexs中最后一个顶点与预删除顶点v互换存储位置
  29. for (int j = 0; j < G.vexnum; j++) //矩阵中最后一个顶点与预删除顶点v的相关边互换存储位置
  30. {
  31. ArcType tmp = G.arcs[i][j];
  32. G.arcs[i][j] = G.arcs[G.vexnum - 1][j];
  33. G.arcs[G.vexnum - 1][j] = tmp;
  34. tmp = G.arcs[j][i];
  35. G.arcs[j][i] = G.arcs[j][G.vexnum - 1];
  36. G.arcs[j][G.vexnum - 1] = tmp;
  37. }
  38. for (int j = 0; j < G.vexnum; j++) //如果是针对有向图的操作,则需要二维遍历
  39. {
  40. if (G.arcs[j][G.vexnum - 1] != MaxInt) //如果逻辑删除的边不是无穷大(也就是存在)
  41. G.arcnum--; //边总数-1
  42. }
  43. G.vexnum--; //顶点总数-1
  44. return OK;
  45. }
  46. else
  47. return ERROR;
  48. }
  49. Status InsertArc(AMGraph &G, VerTexType v, VerTexType w, ArcType t) //增加一条边<v,w>
  50. {
  51. int i, j;
  52. for (i = 0; i < G.vexnum; i++) //需要顶点v在G中存在,并找到其下标
  53. {
  54. if (G.vexs[i] == v)
  55. break;
  56. }
  57. for (j = 0; j < G.vexnum; j++) //需要顶点w在G中存在,并找到其下标
  58. {
  59. if (G.vexs[j] == w)
  60. break;
  61. }
  62. if (i != G.vexnum && j != G.vexnum && i != j && G.arcs[i][j] != MaxInt) //该边需要不存在才能增加
  63. {
  64. G.arcs[i][j] = t;
  65. G.arcs[j][i] = t;
  66. G.arcnum++; //边总数+1
  67. return OK;
  68. }
  69. else
  70. return ERROR;
  71. }
  72. Status DeleteArc(AMGraph &G, VerTexType v, VerTexType w) //删除一条边<v,w>
  73. {
  74. int i, j;
  75. for (i = 0; i < G.vexnum; i++) //需要顶点v在G中存在,并找到其下标
  76. {
  77. if (G.vexs[i] == v)
  78. break;
  79. }
  80. for (j = 0; j < G.vexnum; j++) //需要顶点w在G中存在,并找到其下标
  81. {
  82. if (G.vexs[j] == w)
  83. break;
  84. }
  85. if (i != G.vexnum && j != G.vexnum && i != j && G.arcs[i][j] != MaxInt) //该边需要存在才能删除
  86. {
  87. G.arcs[i][j] = MaxInt;
  88. G.arcs[j][i] = MaxInt;
  89. G.arcnum--; //边总数-1
  90. return OK;
  91. }
  92. else
  93. return ERROR;
  94. }

②邻接表部分:

  1. Status InsertVex(ALGraph &G, VerTexType v) //增加一个新顶点v
  2. {
  3. if (G.vexnum == MVNum)
  4. return OVERFLOW; //顶点数已达最大值
  5. G.vertices[G.vexnum].data = v;
  6. G.vertices[G.vexnum].firstarc = NULL; //仅增加顶点,不增加与其相关的边
  7. G.vexnum++; //当前顶点数+1
  8. return OK;
  9. }
  10. Status DeleteVex(ALGraph &G, VerTexType v) //删除顶点v及其相关的边
  11. {
  12. if (G.vexnum == 0)
  13. return ERROR; //没有顶点可删
  14. int i;
  15. for (i = 0; i < G.vexnum; i++) //找到顶点v所在位置
  16. {
  17. if (v == G.vertices[i].data)
  18. break;
  19. }
  20. if (v != G.vexnum) //如果顶点v存在于图G中,可以进行删除(物理删除)
  21. {
  22. ArcNode* p = G.vertices[i].firstarc;
  23. int num = 0;
  24. while (p)
  25. {
  26. ArcNode* q1 = p;
  27. p = p->nextarc;
  28. ArcNode* q2 = G.vertices[q1->adjvex].firstarc;
  29. ArcNode* q3 = q2;
  30. if (q2->adjvex == i)
  31. {
  32. G.vertices[q1->adjvex].firstarc = q2->nextarc;
  33. free(q3);
  34. num++;
  35. }
  36. else
  37. {
  38. q2 = q2->nextarc;
  39. while (q2)
  40. {
  41. if (q2->adjvex == G.vexnum - 1)
  42. {
  43. q2->adjvex = i;
  44. }
  45. else if (q2->adjvex == i)
  46. {
  47. ArcNode* q4 = q2;
  48. q3->nextarc = q2->nextarc;
  49. free(q4);
  50. num++;
  51. break;
  52. }
  53. q2 = q2->nextarc;
  54. }
  55. }
  56. free(q1);
  57. }
  58. VNode tmp = G.vertices[i];
  59. G.vertices[i] = G.vertices[G.vexnum - 1];
  60. G.vertices[G.vexnum - 1] = tmp;
  61. G.vexnum--; //顶点总数-1
  62. G.arcnum = G.arcnum - num / 2; //如果是针对有向图的操作,num不需要除以2
  63. return OK;
  64. }
  65. else
  66. return ERROR;
  67. }
  68. Status InsertArc(ALGraph &G, VerTexType v, VerTexType w) //增加一条边<v,w>
  69. {
  70. int i, j;
  71. for (i = 0; i < G.vexnum; i++) //需要顶点v在G中存在,并找到其下标
  72. {
  73. if (v == G.vertices[i].data)
  74. break;
  75. }
  76. for (j = 0; j < G.vexnum; j++)
  77. {
  78. if (w == G.vertices[j].data) //需要顶点w在G中存在,并找到其下标
  79. break;
  80. }
  81. if (i == G.vexnum || i == G.vexnum)
  82. return ERROR;
  83. ArcNode* p1 = (ArcNode*)malloc(sizeof(ArcNode));
  84. p1->adjvex = j;
  85. p1->nextarc = G.vertices[i].firstarc;
  86. G.vertices[i].firstarc = p1;
  87. ArcNode* p2 = (ArcNode*)malloc(sizeof(ArcNode));
  88. p2->adjvex = i;
  89. p2->nextarc = G.vertices[j].firstarc;
  90. G.vertices[j].firstarc = p2;
  91. G.arcnum++; //边总数+1
  92. return OK;
  93. }
  94. Status DeleteArc(ALGraph &G, VerTexType v, VerTexType w) //删除一条边<v,w>
  95. {
  96. int i, j;
  97. for (i = 0; i < G.vexnum; i++) //需要顶点v在G中存在,并找到其下标
  98. {
  99. if (v == G.vertices[i].data)
  100. break;
  101. }
  102. for (j = 0; j < G.vexnum; j++) //需要顶点w在G中存在,并找到其下标
  103. {
  104. if (w == G.vertices[j].data)
  105. break;
  106. }
  107. if (i == G.vexnum || i == G.vexnum)
  108. return ERROR;
  109. if (G.vertices[i].firstarc->adjvex == w)
  110. {
  111. ArcNode* p = G.vertices[i].firstarc;
  112. G.vertices[i].firstarc = G.vertices[i].firstarc->nextarc;
  113. free(p);
  114. }
  115. else
  116. {
  117. ArcNode* p1 = G.vertices[i].firstarc;
  118. ArcNode* p2 = G.vertices[i].firstarc;
  119. while (p2->nextarc)
  120. {
  121. if (p2->nextarc)
  122. {
  123. p1->nextarc = p2->nextarc;
  124. break;
  125. }
  126. p1 = p2;
  127. p2 = p2->nextarc;
  128. }
  129. }
  130. if (G.vertices[j].firstarc->adjvex == v)
  131. {
  132. ArcNode* p = G.vertices[j].firstarc;
  133. G.vertices[j].firstarc = G.vertices[j].firstarc->nextarc;
  134. free(p);
  135. }
  136. else
  137. {
  138. ArcNode* p1 = G.vertices[j].firstarc;
  139. ArcNode* p2 = G.vertices[j].firstarc;
  140. while (p2->nextarc)
  141. {
  142. if (p2->nextarc)
  143. {
  144. p1->nextarc = p2->nextarc;
  145. break;
  146. }
  147. p1 = p2;
  148. p2 = p2->nextarc;
  149. }
  150. }
  151. G.arcnum--; //边总数-1
  152. return OK;
  153. }

2、例2

(1)问题描述:设计一个算法,求图G中距离顶点v的最短路径长度最大的一个顶点,设v可达其余各个顶点。

(2)代码:

  1. int T1(AMGraph G, int v0)
  2. {
  3. int n = G.vexnum; //n为G中顶点的个数
  4. bool S[MVNum];
  5. int D[MVNum];
  6. int Path[MVNum];
  7. int v;
  8. for (v = 0; v < n; v++) //n个顶点依次初始化
  9. {
  10. S[v] = false; //S初始为空集
  11. D[v] = G.arcs[v0][v]; //将v0到各个终点的最短路径长度初始化为弧上的权值
  12. if (D[v] != 0)
  13. D[v] = MaxInt;
  14. if (D[v] < MaxInt)
  15. Path[v] = v0; //如果v0和v之间有弧,则将v的前驱置为v0
  16. else
  17. Path[v] = -1; //如果v0和v之间无弧,则将v的前驱置为-1
  18. }
  19. S[v0] = true; //将v0加入S
  20. D[v0] = 0; //源点到源点的距离为0
  21. //初始化结束,开始主循环,每次求得v0到某个顶点v的最短路径,将v加到S集
  22. for (int i = 1; i < n; i++) //对其余n-1个原点依次进行计算
  23. {
  24. int min = MaxInt;
  25. for (int w = 0; w < n; w++)
  26. {
  27. if (!S[w] && D[w] < min)
  28. {
  29. v = w;
  30. min = D[w]; //选择一条当前的最短路径,终点为v
  31. }
  32. }
  33. S[v] = true; //将v加入S
  34. for (int w = 0; w < n; w++) //更新从v0出发到集合V-S上所有顶点的最短路径长度
  35. {
  36. if (!S[w] && (D[v] + G.arcs[v][w] < D[w]))
  37. {
  38. D[w] = D[v] + G.arcs[v][w]; //更新D[w]
  39. Path[w] = v; //更改w的前驱为v
  40. }
  41. }
  42. }
  43. //最短路径求解完毕,设距离顶点v0的最短路径长度最大的一个顶点为m
  44. int Max = D[0];
  45. int m = 0;
  46. for (int i = 1; i < n; i++)
  47. if (Max < D[i]) m = i;
  48. return m; //返回顶点下标
  49. }

3、例3

(1)问题描述:一个连通图采用邻接表作为存储结构,设计一个算法,实现从顶点v出发的深度优先遍历的非递归过程。

(2)代码:

  1. void T2(ALGraph G, int v)
  2. {
  3. SqStack S;
  4. InitStack(&S); //构造一个空栈
  5. Push(&S, v); //顶点v进栈
  6. while (!StackEmpty(S))
  7. {
  8. int k;
  9. Pop(&S, &k); //栈顶元素k出栈
  10. if (!visited_AL[k])
  11. {
  12. printf("%c ", k); //访问第k个节点
  13. visited_AL[k] = true;
  14. ArcNode* p = G.vertices[k].firstarc; //p指向k的边链表的第一个边节点
  15. while (p != NULL) //边节点非空
  16. {
  17. int w = p->adjvex;
  18. if (!visited_AL[w]) //如果k的邻接点未访问,则进栈
  19. Push(&S, w);
  20. p = p->nextarc;
  21. }
  22. }
  23. }
  24. }

4、例4

(1)问题描述:基于图的深度优先搜索策略设计一算法,判别以邻接表方式存储的有向图中是否存在由顶点vi到顶点vj的路径(i≠j)。

(2)代码:

  1. bool T3(ALGraph G, int i, int j)
  2. {
  3. if (i == j) //首尾相遇,说明存在路径,递归结束
  4. return true;
  5. else
  6. {
  7. visited_AL[i] = true; //访问第v个顶点,并置访问标志数组相应分量值为true
  8. ArcNode* p = G.vertices[i].firstarc; //p指向v的边链表的第一个边节点
  9. while (p != NULL) //边节点非空
  10. {
  11. int w = p->adjvex; //w是v的邻接点
  12. p = p->nextarc; //p指向下一个边节点
  13. if (!visited_AL[w] && T3(G, w, j))
  14. return true;
  15. }
  16. }
  17. return false;
  18. }

5、例5

(1)问题描述:采用邻接表存储结构,设计一个算法,判别无向图中任意给定的两个顶点之间是否存在一条长度为k的简单路径。

(2)代码:

  1. bool T4(ALGraph G, int i, int j, int k)
  2. {
  3. if (i == j && k == 0) //找到符合要求的路径,递归结束
  4. return true;
  5. else if (k > 0)
  6. {
  7. visited_AL[i] = true; //访问第v个顶点,并置访问标志数组相应分量值为true
  8. ArcNode* p = G.vertices[i].firstarc; //p指向v的边链表的第一个边节点
  9. while (p != NULL) //边节点非空
  10. {
  11. int w = p->adjvex; //w是v的邻接点
  12. p = p->nextarc; //p指向下一个边节点
  13. if (!visited_AL[w] && T4(G, w, j, k - 1))
  14. return true;
  15. }
  16. visited_AL[i] = false; //允许曾经被访问过的节点出现在另一条路径中
  17. }
  18. return false;
  19. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/396903
推荐阅读
相关标签
  

闽ICP备14008679号