图论系列之「深度优先遍历及联通分量」
图的深度优先遍历及联通分量#
一、介绍#
深度优先遍历 (Depth First Search) 的主要思想是首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点。当没有未访问过的顶点时,则回到上一个顶点,继续试探别的顶点,直至所有的顶点都被访问过。
联通分量,无向图 G 的一个极大连通子图称为 G 的一个连通分量(或连通分支)。连通图只有一个连通分量,即其自身;非连通的无向图有多个连通分量。连通分量与连通分量之间没有任何边相连。深度优先遍历可以用来求连通分量。
下图有 3 个联通分量:
二、遍历过程#
如下图:
以邻接表为例
遍历过程中需要注意的是:遍历到 x 节点时,同样遍历方向会向第 x 行移动,如果 x 行为空,则会返回到原来的行
从 0 开始遍历:(0 遍历完后向第 0 行继续遍历)
然后遍历 1:(遍历 1 时已经在第 1 行,)
遍历第 1 行时,第 1 行后续只有 0 (0 已被遍历然后返回第 0 行),所以开始遍历 2 (同时遍历方向也向第 2 行)
同理
返回到了第 0 行,遍历 5,同时遍历方向变成了第 5 行
第 5 行的后续第一个节点是 0 (已被遍历), 接着往后走就是节点 3
遍历完节点 3 后,看第 3 行的后续是节点 4,遍历节点 4
同理现在就该遍历 6 了
至此图的遍历就完成了
三、代码实现#
#include <iostream>
#include <cassert>
using namespace std;
//求无权图的联通分量
template <typename Graph>
class Component{
private:
Graph &G; //图的引用
bool *visited; //记录深度遍历已经被访问过的节点
int ccount; //记录联通分量
int *id; //每个节点对应的联通分量
//dfs遍历
void dfs(int v){
visited[v] = true;
id[v] = ccount;
//传入迭代器
typename Graph::adjIterator adj(G, v);
for(int i = adj.begin(); !adj.end(); i = adj.next()){
if(!visited[i]){
dfs(i);
}
}
}
public:
//构造函数,求无权图的联通分量
Component(Graph &graph):G(graph){
//算法初始化
id = new int[G.V()];
visited = new bool[G.v()];
ccount = 0;
for(int i = 0; i < G.v(); i++){
visited[i] = false;
id[i] = -1;
}
for(int i = 0; i < G.v(); i++){ //对子图的遍历
//如果visited[i]没有被访问过
if(!visited[i]){
dfs(i);
ccount ++;
}
}
}
//析构函数
~Component(){
delete[] visited;
delete[] id;
}
//返回图的联通分量
int count(){
return ccount;
}
//查询v和w是否联通
bool isComponent(int v, int w){
assert( v >= 0 && v < G.V() );
assert( w >= 0 && w < G.V() );
return id[v] = id[w];
}
};
本作品采用《CC 协议》,转载必须注明作者和本文链接