第六章知识小结

---恢复内容开始---

第六章—图

相比于之前学过的线性表和树,图更为复杂,功能也更加强大。图这一章节主要分为:定义和基本术语、存储结构、遍历、应用。

一、定义

概念就不说了,有几点需要注意的地方:

1.顶点v的度指的是和v相关联的边的数目。对于有向图,图的度是入度和出度的总和。

2.连通图和连通分量的概念要注意区分。连通图里任意两个顶点都是连通的,也就是说所有的顶点都是有通路的,而连通分量,就是无向图中的极大连通子图,可以看做是有几个独立开来的网状结构,就有多少个连通分量。

图的存储结构

分为邻接矩阵和邻接表,相当于线性结构和链式结构,它们各自有自己的优缺点。

 

优点

缺点

适用情况

联系

时间复杂度

邻接矩阵

容易实现图的操作,如:求某顶点的度、判断顶点之间是否有边、找顶点的邻接点等等

n个顶点需要n*n个单元存储边; 空间效率为。对稀疏图而言尤其浪费空间

稠密图

邻接表中每个链表对应于邻接矩阵中的一行,链表中节点个数等于一行中非零元素的个数

O(n2)

邻接表

便于增加和删除顶点,空间效率高

不便于实现对图的操作,不便于计算又想吐各个顶点的度

稀疏图

O(n+e)

邻接矩阵比较好理解,我主要谈谈我对邻接表的理解,邻接表的存储表示包含三个结构体:

1. 表头结点表:包含数据域和链域,主要是用来记录结点信息的。

1 typedef struct { 
2   VerTexType  data;   // 顶点信息
3   ArcNode  *firstarc; 
4        // 指向第一条依附该顶点的弧
5   } VNode, AdjList[MVNUM];
View Code

2.边表:包含邻接点域、数据域和链域,主要是用来记录边信息的(包括权值)

1 typedef struct ArcNode {  
2   int adjvex;   //该边所指向的顶点的位置
3   struct ArcNode *nextarc; //指向下一条边的指针
4   OtherInfo info;   //和边相关的信息,例如权值
5 } ArcNode;
View Code

3.图结构:包含了以上两种结构体和图当前的顶点数和边数

typedef struct {  
     AdjList  vertices;
     int  vexnum, arcnum;                                      
                  //顶点数和边数 
} ALGraph;
View Code

 三、图的遍历

DFS(深度优先搜索):

1.从顶点v出发,访问v,并置visited[v]的值为true

2. 然后依次从v的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到

BFS(广度优先搜索):

1.从图中某个顶点v出发,访问v,并置visited[v]的值为true,然后将v 进队。

2.只要队列不空,则重复下述处理:

  (1)队头顶点u出队。

  (2)依次检查u的所有邻接点w,如果visited[w]的值为false,则访问w,并置visited[w]的值为true,然后将w 进队。

四、图的应用

主要讲述了两个算法内容,普里姆算法和克鲁斯卡尔算法。

 

五、代码题解—列出连通图

步骤如下:

1.建立图

2.深度优先搜索

3.重置标记数组

4.广度优先搜索

第一步 建立图

由于题目说“总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。”,而且数据量不大,因此我选择了用邻接矩阵来建图。又因为在输入的时候直接输入了两个相连的顶点,因此我没有建立顶点表,直接设立了邻接矩阵。

1 typedef struct{
2     
3     int arcs[MVNum][MVNum];//邻接矩阵
4     int N,E; //图的当前点数和边数 
5     
6 }AMGraph;
View Code
 1 //采用邻接矩阵表示法创建无向图G
 2 void CreatUDN(AMGraph &G){ 
 3     
 4     int v1,v2,i,j,k;
 5     cin>>G.N>>G.E;//输入总顶点数、总边数 
 6 
 7     for(i=0;i//初始化邻接矩阵,将边的权值初始化为0 
 8         for(j=0;jj){
 9             G.arcs[i][j]=0;
10         }
11     }
12     for(k=0;k//构造邻接矩阵 
13         cin>>v1>>v2;  //输入一条边依附的两个顶点 
14         G.arcs[v1][v2]=1;//置边的边的权值为1 
15         G.arcs[v2][v1]=1;
16     }      
17 };
View Code

第二步 深度优先搜索

这里分为了两部分:由于可能存在多个连通分量,因此要多次调用DFS_AM函数,在这里调用多少次就说明图中有多少个连通分量。然后就是依次检查邻接矩阵v所在的行。

 1 //采用邻接矩阵表示非连通图的深度优先搜索遍历
 2 void DFS_AM(AMGraph G,int v){
 3     
 4     int w;
 5     cout<" ";
 6     visited[v]=true;//访问第v个顶点,并置访问标志数组相应分量的值为true 
 7     for(w=0;w){
 8         if((G.arcs[v][w]!=0)&&(!visited[w]))//依次检查邻接矩阵v所在的行 
 9         DFS_AM(G,w);
10     }//G.arcs[v][w]!=0表示w是v的邻接点,如果w未访问,则递归调用DFS_AM 
11 }; 
12 
13 void DFSTraverse(AMGraph G){
14     
15     int v;
16     for(v=0;vv){
17         if(!visited[v]){
18             cout<<"{"<<" ";
19             DFS_AM(G,v);
20             cout<<"}"<<endl;    
21         }//对尚未访问的顶点调用DFS_AM 
22     }
23     
24 };
View Code

第三步 重置标记数组(这里容易忽略)

标记数组:

1 bool visited[MVNum]={false};//访问标志数组初始化 

重置:

1 //重置标志数组,使其恢复到初始化状态 
2 void restore(bool visited[],AMGraph G){
3     int i;
4     for(i=0;ii){
5         visited[i]=false;
6     }
7 }; 
View Code

第四步 广度优先搜索

这里我让所有的元素先入队,然后记录队列的第一个元素并进行出队,再判断出队元素的邻接点是否已经被访问过。

 1 //采用邻接矩阵表示非连通图的广度优先搜索遍历
 2 void BFS_AM(AMGraph G,int v){
 3     
 4     visited[v]=true;//访问第v个顶点,并置访问标志数组相应分量值为true 
 5     queue <int> q;//队列初始化 
 6     int w,u;
 7     q.push(v);//v进队 
 8     while(!q.empty()){
 9         w = q.front();//队头元素出队并置为w 
10         q.pop();
11         cout<" ";
12         for(u=0;u){
13             if((G.arcs[w][u]!=0)&&(!visited[u])){//若i为w尚未访问的邻接顶点,则标志数组置为true,u进队 
14                 visited[u]=true;
15                 q.push(u);
16             }
17         } 
18     }    
19 };
20 
21 void BFSTraverse(AMGraph G){
22     
23     int v;
24     for(v=0;vv){
25         if(!visited[v]){
26             cout<<"{"<<" ";
27             BFS_AM(G,v);
28             cout<<"}"<<endl;    
29         }//对尚未访问的顶点调用BFS_AM 
30     }
31 };
View Code

小结:

图这章知识点比较难理解,特别是邻接表、DFS、BFS,所以做题的进度比较慢,希望下次能够当天消化好老师上课所讲的内容,尽可能早完成所有题解。

你可能感兴趣的:(第六章知识小结)