第六章学习小结

一、本章学习小结

本章学习了图这一复杂的非线性数据结构,图包括有向图和无向图,有向图中又包含始点和终点,弧尾和弧头的概念。两者的本质区别应该是有无序。重点学习了DFS算法和BFS算法实现图的遍历,理解了迪杰斯特拉算法的逻辑思想。

1、图的基本术语(顶点数目为n,边数目为e)

子图、稀疏图和稠密图、权和网、邻接点、度、入度和出度、路径和路径长度、回路或环、连通、连通图和连通分量、强连通图和强连通分量、有向树和生成森林。

 有向完全图:具有n(n-1)条边的有向图。         无向完全图:具有n(n-1)/2条边的无向图。      

有向图:度=入度+出度。                                 无向图:无入度出度之分。

路径长度:一条路径上经过的边或弧的数目。

连通分量:无向图中的极大连通子图 。(对于连通图,其连通分量是其本身)

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

连通图的生成树:一个极小的连通子图包含图中全部顶点,但只有足以构成一棵树的n-1条边。

有向树:有一个顶点的入度为0,其余顶点的入度均为1的有向图。

2、图的存储结构

邻接矩阵表示法:适用于稠密图,时间和空间复杂度均为O(n^2)。

 1 #define MaxInt    //表示极大值 
 2 #define MVNum 100    //最大顶点数 
 3 typedef char VerTexType;    //顶点的数据类型 
 4 typedef int ArcType;     //边的权值类型 
 5 typedef struct
 6 {
 7     VerTexType vexs[MVNum];   //顶点表 
 8     ArcType arcs[MVNum][MVNum];    //邻接矩阵 
 9     int vexnum, arcnum;   //图的当前点数和边数 
10 }AMGraph;

邻接表表示法:适用于稀疏图,时间和空间复杂度均为O(n+e)。

 1 #define MVNum 100        //最大顶点数 
 2 typedef struct ArcNode   //邻接点的结构 
 3 {
 4     int adjvex;        //该边所指向的顶点的位置
 5     struct ArcNode *nextarc;     //指向下一条边的指针
 6     OtherInfo info;        //和边相关的信息,例如权值 
 7 }ArcNode;
 8 typedef struct VNode   //顶点的结构 
 9 {
10     VerTexType data;     //顶点信息 
11     ArcNode *firstarc;    //指向第一条依附该顶点的弧 
12 }VNode, AdjList[MVNum];
13 typedef struct
14 {
15     AdjList vertices;
16     int vexnum, arcnum;     //顶点数和边数 
17 }ALGraph;

还有十字链表(有向图)和邻接多重表(无向图)。

3、图的遍历

深度优先遍历(DFS算法):类似树的先序遍历,借助栈结构实现运用递归

 1 bool visited[MVNum];      //访问标志数组, 其初值为"false" 
 2 //连通图 
 3 void DFS(Graph G, int v)  
 4 {
 5     cout<<v;   visited[v]=true; 
 6     //访问第v个顶点,并置访问标志数组相应分扯值为true 
 7     for(w=FirstAdjVex(G,v); w>=O; w=NextAdjVex(G,v,w)) 
 8     //依次检查v的所有邻接点w,FirstAdjVex(G,v)表示v的第一个邻接点 
 9     //NextAdjVex(G,v,w)表示v相对于w的下一个邻接点,w≧0表示存在邻接点
10         if(!visited[w])     DFS(G,w);   
11         //对v的尚未访问的邻接顶点w递归调用 DFS 
12 }
13 //非连通图 
14 void DFSTraverse(Graph G)   
15 {
16     for(v=0; v<G.vexnum; ++v)   
17         visited[v]=false;    //访问标志数组初始化
18     for(v=0; v<G.vexnum; ++v)   //循环调用算法6.3
19         if(!visited[v])     DFS(G, v);    //对尚未访问的顶点调用DFS
20 }

广度优先遍历(BFS算法):类似树的层次遍历,借助队列结构

 1 void BFS{Graph G, int v)   //连通图 
 2 {
 3     cout<<v;    
 4     visited[v]=true; 
 5     InitQueue(Q);    //辅助队列Q初始化,置空 
 6     EnQueue(Q, v);    //v进队 
 7     while(!QueueEmpty(Q))  //队列非空 
 8     {
 9         DeQueue(Q, u);   //队头元素出队并置为u
10     for(w=FirstAdjVex(G,u); w>=O; w=NextAdjVex(G,u,w)) 
11     //依次检查u的所有邻接点w, FirstAdjVex(G,u)表示u的第一个邻接点 
12     //NextAdjVex(G,u,w)表示u相对于w的下一个临界点,w≧0表示存在邻接点
13         if(!visited[w])       //w为u的尚未访问的邻接顶点 
14         {
15             cout<<w;  
16             visited[w]=true;  
17             EnQueue(Q, w);    //w进队
18         }   //if
19     }   //while
20 }

DFS算法和BFS算法的空间复杂度相同,均为O(n)(借用了堆栈或队列);时间复杂度雨存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关。(邻接矩阵----时间复杂度为O(n^2),邻接表----时间复杂度为O(n+e))

4、图的应用

1)最小生成树:

普里姆算法:时间复杂度为O(n^2),与网中的边数无关,适用于稠密图。

克鲁斯卡尔算法:时间复杂度为O(eloge),与网中的边数有关,适用于稀疏图。

2)最短路径

迪杰斯特拉算法:源点到其余各点,时间复杂度为O(n^2)。

弗洛伊德算法:每对顶点之间,时间复杂度为O(n^3)。

二、实践心得

1、打代码过程中,接触到一个新函数

//重置visit[]数组
memset(visit, 0, sizeof(visit));

2、拯救007中,刚开始看题目没有头绪,后来查询了一番,将图画了出来,便清晰了然,发现这也是应用了图的遍历,可以使用DFS或BFS解决问题。

3、小测的题目是对图进行遍历,而固定思维总以为是从左至右,从上至下,结果导致出错。日后还是应该具体分析代码。

posted on 2020-06-14 11:59  周淑霞  阅读(198)  评论(0编辑  收藏  举报