【算法笔记】10.3 图的遍历

  • 这是《算法笔记》的读书记录
  • 本文参考自10.3节

〇、图的基本概念

  • 定义:图由顶点(vertex)和边(edge)组成。每条边的两段都必须是两个顶点(可以是同一个点)。记顶点集合为V,边集合为E,可以用G(V,E)记录一个图

    1. 有向图:所有边都有方向,只能沿着边的方向在图中移动
    2. 无向图:所有边都没有方向(或者说是双向的),可以把无向图的每条边看作由正向和负向的两条有向边组成
  • 图的数据结构表示

    1. 邻接表:好写,但是空间复杂度高,适用于定点数小于1000的题目
    2. 邻接矩阵:写起来比较麻烦,但是空间复杂度低,一般用vector数组实现
  • 图的相关概念

    1. 顶点的度:度指和此顶点连接的边的条数,对于有向图来说,顶点出边的条数称为此顶点的出度;顶点入边的条数称为此顶点的入度
    2. 权值:顶点和边都可以有一定的属性,量化的属性称为权值。边和点分别有边权点权,具体根据题目背景设定
    3. 连通分量无向图中,如果两个顶点间相互可达(可以是通过一定的路径到达),就称这两个顶点连通。若图G(V,E)的任意两个顶点都连通,则称G为连通图;否则称G为非连通图,且其中的极大连通子图为连通分量
    4. 强连通分量有向图中,如果两个顶点间相互可达(各自通过一条有向路径),就称这两个顶点强连通。若图G(V,E)的任意两个顶点都强连通,则称G为强连通图;否则称G为非强连通图,且其中的极大强连通子图为强连通分量
    5. 连通块:为了表示简便,以下把所有连通分量强连通分量统称为连通块
  • 关于图的遍历:

    1. 遍历图,等价于遍历图中的所有连通块
    2. 从图中的任意结点开始BFS或DFS遍历,可以访问到其所能到达的所有点
    3. 对于连通图强连通图,因为整个图就是一个连通块,所以BFS或DFS一遍即可完成遍历
    4. 对于非连通图非强连通图,通常的做法是设置一访问标记数组,遍历图的点集,从没有做过访问标记的点开始BFS或DFS搜索即可,搜索过程中不断给访问过的点打上标记。

一、DFS遍历图

  • 关于DFS算法:【算法笔记】8.1 深度优先搜索DFS

  • DFS遍历图:以 “深度” 作为第一关键词,每次都是沿着路径走到不能前进时才回退到最近的岔路口。

  • 示例:下图是一个非强连通图,V0入度为0,如果从其他点开始DFS,至少需要DFS两遍(如从V2开始),而从V0开始则能访问到全部顶点。从V0开始DFS,遍历顺序为:v0->v1->v3->v4->v5->v2
    在这里插入图片描述

  • 实例代码(邻接表表示,V0开始遍历)

    /*
    	DFS遍历有向图
    */
    #include<iostream>
    #include<vector>
    using namespace std;
    
    const int MAXN = 1000;		//最大顶点数
    bool vis[MAXN] = {
          
          false}; 	//访问标记数组
    
    typedef struct NODE{
          
          
    	int v;	//连接到的端点 
    	int w;	//边权,这里其实没用
    	
    	NODE(int _v,int _w):v(_v),w(_w){
          
          }
    }NODE;
    vector<NODE> Adj[MAXN];		//邻接表
    
    void DFS(int u,int depth)
    {
          
          
    	vis[u] = true;
    	cout<<"visit:"<<u<<"	depth:"<<depth<<endl;
    	
    	for(int i=0;i<Adj[u].size();i++)
    	{
          
          
    		int v = Adj[u][i].v;
    		if(!vis[v])
    			DFS(v,depth+1);
    	}
    }
    
    int main()
    {
          
          
    	int N,M;		//顶点数、边数
    	cin>>N>>M;
    	
    	int start,end;	//有向边的起点和终点
    	for(int i=0;i<M;i++)
    	{
          
          
    		cin>>start>>end;
    		Adj[start].push_back(NODE(end,0));
    	}	
    
    
    	DFS(0,0);	//DFS遍历0号点所在的连通块,初始深度为0 
    	
    /*	
    	//DFS遍历所有连通块 
    	for(int i=0;i<N;i++)
    	{
    		if(!vis[i])
    			DFS(i,0);
    	}
    */	
    	
    	return 0;	
    } 
    
    /*
    5 9
    0 1
    0 2
    1 3
    1 4
    2 1
    2 4
    4 3
    4 5
    5 3
    */
    
  • 输出如下,可见是以先走到最深,遇到阻碍再回头的方式遍历的

    visit:0        depth:0
    visit:1        depth:1
    visit:3        depth:2
    visit:4        depth:2
    visit:5        depth:3
    visit:2        depth:1
    

二、BFS遍历图

  • 关于BFS算法:【算法笔记】8.2 广度优先搜索BFS

  • BFS遍历图:以 “广度” 作为第一关键词,每次以扩展的方式向外访问顶点。和BFS一般写法一样,这里要用一个队列,通过反复去除队首顶点,使此顶点可达的未加入过队列的顶点全部入队,直到队列为空时遍历结束

  • 示例:下图是一个非强连通图,V0入度为0,如果从其他点开始BFS,至少需要BFS两遍(如从V2开始),而从V0开始则能访问到全部顶点。从V0开始BFS,遍历顺序为:v0->v1->v2->v3->v4->v5
    在这里插入图片描述

  • 示例代码(邻接表表示,V0开始遍历)

    #include <iostream>
    #include <queue>
    using namespace std;
    
    const int MAXN = 1000;
    bool inqueue[MAXN] = {
          
          false};	//标记节点是否已经入过队 
    
    typedef struct NODE
    {
          
          
    	int v;		//连接到的端点 
    	int w;		//权值,这里其实没用 
    	int layer;	//顶点层次
    	
    	NODE(int _v,int _w,int _layer):v(_v),w(_w),layer(_layer){
          
          }
    	 
    }NODE;
    
    vector<NODE> Adj[MAXN];
     
    //以s为起点,遍历s所在的连通块 
    void BFS(int s)
    {
          
          
    	queue<NODE> q;
    	NODE start = NODE(s,0,0);
    	q.push(start);
    	inqueue[s] = true;
    	while(!q.empty())
    	{
          
          
    		NODE top = q.front();
    		q.pop();
    		cout<<"visit:"<<top.v<<"	depth:"<<top.layer<<endl;
    		int u = top.v;
    		for(int i=0;i<Adj[u].size();i++)
    		{
          
          
    			NODE next = Adj[u][i];
    			next.layer = top.layer+1;
    			if(!inqueue[next.v])
    			{
          
          
    				q.push(next);
    				inqueue[next.v] = true;	
    			}
    		}	
    	} 
    } 
    
    int main()
    {
          
          
    	int N,M;	//顶点数、边数
    	cin>>N>>M;
    	
    	int start,end;
    	for(int i=0;i<M;i++)
    	{
          
          
    		cin>>start>>end;
    		Adj[start].push_back(NODE(end,0,0));
    	}	
    	
    	BFS(0);		//BFS遍历0号点所在的联通块 
    	
    	return 0;	
    } 
    
    /*
    5 9
    0 1
    0 2
    1 3
    1 4
    2 1
    2 4
    4 3
    4 5
    5 3
    */
    
  • 输出,可见是按深度层次一路下遍历的

    visit:0        depth:0
    visit:1        depth:1
    visit:2        depth:1
    visit:3        depth:2
    visit:4        depth:2
    visit:5        depth:3
    

猜你喜欢

转载自blog.csdn.net/wxc971231/article/details/108578404