《算法笔记》10.3 图的遍历-算法笔记103图的遍历

10.3 图的遍历

图的遍历是指对图的所有顶点按一定顺序进行访问,遍历方法一般有两种:深度优先搜索和广度优先搜索。

10.3.1 DFS遍历图

两个概念:

  • 连通分量:在无向图中,如果两个顶点之间可以相互到达(可以是通过一定路径间接到达),那么就称这两个顶点连通。如果图G(V,E)的任意两个顶点都连通,则称图G为连通图;否则,称图G为非连通图,且称其中的极大连通子图为连通分量。

image.png

v1v2v3 , v4v5v6v7 , v8v9 形成三个连通分量

  • 强连通分量:在有向图中,如果两个顶点可以各自通过一条有向路径到达另一个顶点,就称这两个顶点强连通。如果图G(V,E)的任意两个顶点都强连通,则称图G为强连通图;否则,称图G为非强连通图,且称其中的极大强连通子图为强连通分量。

image.png

v1v2v3 , v4 , v5v6v7v8 形成三个强连通分量。

可以想象,如果要遍历整个图,就需要对所有连通块分别进行遍历。所以DFS遍历图的基本思路就是将经过的顶点设置为已访问,在下次递归碰到这个顶点时就不再去处理,直到整个图的顶点都被标记为已访问。

下面是一份DFS的伪代码,不管是使用邻接矩阵还是邻接表,都是使用这种思想。注意:如果已知给定的图是一个连通图,则只需要一次DFS就能完成遍历。

DFS(u){ //访问顶点u
    vis[u] = false ; // 设置u已被访问
    for( 从u出发能到达的所有顶点v ){ //枚举从u出发可以到达的所有顶点v
       if( vis[v] == false ) //如果v未被访问
          DFS(v) ;  //递归访问v
    }
}

DFSTrave(G){  //遍历图G
    for( G的所有顶点u ){
        if( vis[u] == false )  //如果u未被访问
            DFS[U] ; //访问u所在的连通块
    }
}

将邻接矩阵和邻接表的实现方法带入上面伪代码中,可以得到如下模板

邻接矩阵模板

const int maxn = 1000 ; // 顶点最大数
const int INF = 1000000000 ; //一个很大的数

int n ;  //n为顶点数
int G[maxn][maxn] ;  //图
bool vis[maxn] = {false} ; //如果顶点i已被访问,则vis[i]=true ,否则vis[i]=false 

void DFS( int u , int depth ){  //u为当前访问的顶点标号,depth为深度
    vis[u] = true ; //设置u已被访问
    //如果需要对u进行操作,在这里执行
    
    for( int v = 0 ; v < n ; v++ ){ //所有从u出发能到达的顶点
        //顶点未被访问且顶点u与顶点v之间存在边
        if( vis[v] == false && G[u][v] != INF ){
            DFS(v ,depth+1 ) ; //访问v,深度+1
        }
    }

}

void DFSTrave(){ //遍历图G
   for( int u = 0 ; u < n ; u++ ){  //对每个顶点
       if( vis[u] == false ){
            DFS(u , 1) ;  //访问u和u所在的连通块,初始为第1层
       }
   }
}

邻接表模板

const int maxn = 1000 ;

vector<int> Adj[maxn] ;
int n ; //顶点个数
bool vis[maxn] = {false} ;

void DFS( int u , int depth ){
    vis[u] = true ;
    //如果需要对u进行操作
    for( int i = 0 ; i < Adj[u].size() ; i++ ){
        int v = Adj[u][i] ;
        if( vis[v] == false ){
            DFS(v , depth+1 ) ;
        } 
    }
}

void DFSTrace(){
    for( int u = 0 ; u < n ; u++ ){
        if( vis[u] == false ){
            DFS(u , 1) ;
        }
    }
}

BFS遍历图

广度优先搜索以“广度”作为关键词,每次以扩散的方式向外访问顶点。和树的遍历一样,使用BFS遍历图需要使用一个队列,通过反复取出队首顶点,将该顶点可到达的未曾加入过队列的顶点全部入队,直到队列为空时遍历结束。

BFS遍历图的具体实现

如果要遍历整个图,需要对所有连通块分别进行遍历。使用BFS遍历图的基本思想是建立一个队列,并把初始顶点加入队列,此后每次都取出队首顶点进行访问,并把从该顶点出发可以到达的未曾加入过队列
(而不是未访问)的顶点全部加入队列,直到队列为空。

邻接矩阵模板

const int maxn = 1000 ;
const int INF = 10000000 ;

int n ;
int G[maxn][maxn] ;
bool inq[maxn] = {false} ; //若顶点i曾如果队,则inq[i] = true

void BFS( int u ){
    queue<int> q ;
    q.push(u) ;
    inq[u] = true ;

    while( !q.empty()){
        int u = q.front() ;
        q.pop();
        for( int v = 0 ; v < n ; v++ ){
            if( inq[v] == false && G[u][v] != INF ){
                q.push(v) ;
                inq[v] = true ;
            }
        }
    }

}

void BFSTrave(){
    for( int u = 0 ; u < n ; u++ ){
        if( inq[u] == false ){
            BFS(u) ;
        }
    }
}

邻接表模板

const int maxn = 1000 ;


int n ;
vector<int> Adj[maxn] ;
bool inq[maxn] = {false} ; //若顶点i曾如果队,则inq[i] = true

void BFS( int u ){
    queue<int> q ;
    q.push(u) ;
    inq[u] = true ;

    while( !q.empty()){
        int u = q.front() ;
        q.pop();
        for( int i = 0 ; i < Adj[u].size() ; i++ ){
            int v = Adj[u][i] ;
            if( inq[v] == false ){
                q.push(v) ;
                inq[v] = true ;
            }
        }
    }

}

void BFSTrave(){
    for( int u = 0 ; u < n ; u++ ){
        if( inq[u] == false ){
            BFS(u) ;
        }
    }
}

如果需要记录层号

struct Node{
    int v ; //顶点编号
    int layer ; //顶点层号
};

const int maxn = 1000 ;
int n ;
vector<int> Adj[maxn] ;
bool inq[maxn] = {false} ; //若顶点i曾如果队,则inq[i] = true

void BFS( int s ){
    queue<Node> q ;
    Node start ;
    start.v = s ;
    start.layer = 0 ;
    q.push(start) ;
    inq[start.v] = true ;

    while( !q.empty()){
        Node top = q.front() ;
        q.pop();
        int u = top.v ;  //队首顶点编号
        for( int i = 0 ; i < Adj[u].size() ; i++ ){
            Node next = Adj[u][i] ;
            next.layer = top.layer + 1 ;
            if( inq[next.v] == false ){
                q.push(next) ;
                inq[next.v] = true ;
            }
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值