第六章总结

「爱情、让人受尽委屈。」 2021-11-01 08:56 582阅读 0赞

第六章我们学习到的是图,一种比树还要复杂一点的数据结构。

首先是图的定义: 图G有两个集合V和E组成,记为G=(V,E),其中V是顶点的有穷非空集合,E为V中顶点偶对的有穷集合,

这些顶点偶对成为边。V(G)和E(G)通常分为表图G的顶点集合和边集合,E(G)可以为空集。若E(G)为空,则图G

只有顶点而没有边;若边集E(G)为有向边的集合,则称该图有向图;若边集E(G)为无向边的集合,则称该图为无向图。

然后介绍一下比较重要的图的遍历

图的遍历

图的遍历和树的遍历类似,希望从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫图的遍历。

对于图的遍历来说,如何避免因回路陷入死循环,就需要科学地设计遍历方案,通过有两种遍历次序方案:深度优先遍历和广度优先遍历。

1.深度优先遍历

深度优先遍历,也有称为深度优先搜索,简称DFS。其实,就像是一棵树的前序遍历。

它从图中某个结点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中的所有顶点都被访问到为止。

邻接矩阵表示图代码:

  1. void DFS_AM (AMGraph G,int v)
  2. {
  3. //图G为邻接矩阵类型,从第v个顶点出发深度优先搜索遍历图G
  4. cout<<v; visited[v]=true;//访问第v个顶点,并置访问的标志数组相应的分量值为true
  5. for(w=0;w<G.vexnum;w++)//依次检查邻接矩阵v所在的行
  6. if((G.arcs[v][w]!=0)&&(!visited[w])) DFS_AM(G,w);//G.arcs[v][w]表示w是v的邻接点,如果w未访问,则递归调用DFS_AM
  7. }

邻接表表示图代码:

  1. void DFS(GraphList g, int i)
  2. {
  3. EdgeNode *p;
  4. visited[i] = TRUE;
  5. cout<<g->adjList[i].data; //打印顶点,也可以其他操作
  6. p = g->adjList[i].firstedge;
  7. while(p)
  8. {
  9. if(!visited[p->adjvex])
  10. {
  11. DFS(g, p->adjvex); //对访问的邻接顶点递归调用
  12. }
  13. p = p->next;
  14. }
  15. }

2. 广度优先遍历

广度优先遍历,又称为广度优先搜索,简称BFS。图的广度优先遍历就类似于树的层序遍历了。

代码如下:

  1. //广度优先遍历
  2. void BFS(AGraph* G,int v) {
  3. ANode *p;
  4. queue<int> qu;
  5. vector<int> flag(G->n);
  6. int w;
  7. cout<<v<<" ";
  8. flag[v]=1;
  9. qu.push(v);
  10. while(!qu.empty()) {
  11. w = qu.front();
  12. qu.pop();
  13. p = G->adjlist[w]->firstarc;
  14. while(p) {
  15. if(!flag[p->adjvex]) {
  16. cout<<p->adjvex<<" ";
  17. flag[p->adjvex] = 1;
  18. qu.push(p->adjvex);
  19. }
  20. p = p->nextarc;
  21. }
  22. }
  23. cout<<endl;
  24. }

然后就是图的应用

1.最小生成树

a.普里姆算法

Prim算法
Prim算法是用来解决最小生成树问题的。
基本思想:对图G(V,E)设置集合S,存放已经被访问的顶点,然后每次从集合V-S中选择与集合S的最短距离最小的一个顶点(记为u),访问并加入集合S。之后,令顶点u为中介点,优化所有从u能到达的顶点v与集合S之间的最短距离。这样的操作执行n次(n为顶点个数),直到集合S已包含所有顶点。

核心代码如下:

  1. void prim(Graph G,int vcount,int father[])
  2. {
  3. int i,j,k;
  4. int lowcost[max_vertexes];
  5. int closeset[max_vertexes],used[max_vertexes];
  6. int min;
  7. for (i=0;i<vcount;i++)
  8. {
  9. /* 最短距离初始化为其他节点到1号节点的距离 */
  10. lowcost[i]=G[0][i];
  11. /* 标记所有节点的依附点皆为默认的1号节点 */
  12. closeset[i]=0;
  13. used[i]=0;
  14. father[i]=-1;
  15. }
  16. used[0]=1; /*第一个节点是在U集合里的*/
  17. /* vcount个节点至少需要vcount-1条边构成最小生成树 */
  18. for (i=1;i<=vcount-1;i++)
  19. {
  20. j=0;
  21. min = infinity;
  22. /* 找满足条件的最小权值边的节点k */
  23. for (k=1;k<vcount;k++)
  24. /* 边权值较小且不在生成树中 */
  25. if ((!used[k])&&(lowcost[k]<min))
  26. {
  27. min = lowcost[k];
  28. j=k;
  29. }
  30. father[j]=closeset[j];
  31. used[j]=1;;//把第j个顶点并入了U中
  32. for (k=1;k<vcount;k++)
  33. /* 发现更小的权值 */
  34. if (!used[k]&&(G[j][k]<lowcost[k]))
  35. {
  36. lowcost[k]=G[j][k];/*更新最小权值*/
  37. closeset[k]=j;;/*记录新的依附点*/
  38. }
  39. }
  40. }

b.kruskal算法

kruskal算法

基本思想:假设连通网N=(V,{E})。则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。在E中选择最小代价的边,若该边依附的顶点落在T中不同的连通分量中,则将该边加入到T中,否则舍去此边而选择下一条代价最小的边,依次类推,直到T中所有顶点都在同一连通分量上为止。

核心代码:

  1. void MiniSpanTree_Kruskal(MGraph G)
  2. {
  3. int i, j, n , m;
  4. int k = 0;
  5. int parent[MAXVEX];/* 定义一数组用来判断边与边是否形成环路 */
  6. Edge edges[MAXEDGE];/* 定义边集数组,edge的结构为begin,end,weight,均为整型 */
  7. /* 此处省略将邻接矩阵G转换为边集数组edges并按权由小到大排列的代码*/
  8. for (i = 0; i < G.numVertexes; i++)
  9. parent[i] = 0;
  10. cout << "打印最小生成树:" << endl;
  11. for (i = 0; i < G.numEdges; i++)/* 循环每一条边 */
  12. {
  13. n = Find(parent, edges[i].begin);
  14. m = Find(parent, edges[i].end);
  15. if (n != m)/* 假如n与m不等,说明此边没有与现有的生成树形成环路 */
  16. {
  17. parent[n] = m;/* 将此边的结尾顶点放入下标为起点的parent中。 */
  18. /* 表示此顶点已经在生成树集合中 */
  19. cout << "(" << edges[i].begin << ", " << edges[i].end << ") "
  20. << edges[i].weight << endl;
  21. }
  22. }

本周回顾:

上周确实非常忙碌,所以pta上作业完成程度只有一半,还没对作业进行整理总结,而且没有对第四第五章进行复习,导致小测成绩不尽人意,而

学到图之后,要处理的东西也越来越多,更需要花时间去消化和理解,对于算法的理解是很重要的,然后提高将算法转为可运行的代码的能力,这

个过程我觉得会很辛苦,但是收获也是很多的,所以还是给自己打打气,下周已没有活动,是时候专心去打代码了。

学习资料推荐:

帮助理解普里姆算法和克鲁斯卡尔算法的大牛博客:https://www.cnblogs.com/PJQOOO/p/3855017.html

帮助了解路径规划(最短路径)的博客:https://www.cnblogs.com/zhuweisky/archive/2005/09/29/246677.html

转载于:https://www.cnblogs.com/fengwanthousand/p/10890271.html

发表评论

表情:
评论列表 (有 0 条评论,582人围观)

还没有评论,来说两句吧...

相关阅读

    相关

    1.目录管理的主要功能:按名存取;提高检索速度;文件共享;允许文件重名。 2.在索引节点中设置连接引用(links)计数的目的是为了实现目录管理的文件共享功能。 3.实现“

    相关 总结

    1、属性文法   是在上下文无关文法的基础上为每个文法符号(终结符或非终结符)配备若干个相关的“值”(称为属性)。      属性:代表与文法符号相关的信息,和变量一样,可

    相关 字典

    6.1 使用字典 在Python中, 字典 是一系列键—值对 。 每个键 都与一个值相关联, 你可以使用键来访问与之相关联的值。 与键相关联的值可以是数字、 字符串、

    相关 总结

    第六章总结: 1.数组:数组元素在内存中顺次存放并且地址是连续的 (1)数组名字是数组首元素的地址 (2)数组名是一个常量(不可以写成:a++) 2.对象数组: