深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。
DFS 可以使用栈实现也可以使用递归实现,使用递归比较好理解但是在某些语言或者环境下,对规模较大的图使用递归思路会导致栈溢出。
首先是递归版本
#include <bits/stdc++.h>
using namespace std;
#define N 100
// 未访问状态
#define UNDISCOVERED 0
// 已访问状态
#define DISCOVERED 1
// 访问结束状态
#define VISITED 2
int n, M[N][N];
// status:状态 dtime: 访问时间 ftime: 结束访问时间
int status[N], dtime[N], ftime[N];
int _clock;
void dfs_visit(int u){
status[u] = DISCOVERED;
dtime[u] = ++_clock;
for (int v = 0; v < n; v++){
if (M[u][v] == 0) continue;
if (status[v] == UNDISCOVERED){
dfs_visit(v);
}
}
ftime[u] = ++_clock;
status[u] = VISITED;
}
void dfs(){
for (int u = 0; u < n; u++) status[u] = UNDISCOVERED;
_clock = 0;
for (int u = 0; u < n; u++){
if (status[u] == UNDISCOVERED)
dfs_visit(u);
}
for (int u = 0; u < n; u++){
printf("%d %d %d\n", u+1, dtime[u], ftime[u]);
}
}
int main(){
int u, v, k, i, j;
scanf("%d", &n);
memset(M, 0, sizeof(M));
for (i = 0; i < n; i++){
scanf("%d %d", &u, &k);
u--;
for (j = 0; j < k; j++){
scanf("%d", &v);
v--;
M[u][v] = 1;
}
}
dfs();
return 0;
}
栈实现版本
#include <bits/stdc++.h>
using namespace std;
static const int N = 100;
static const int UNDISCOVERED = 0;
static const int DISCOVERED = 1;
static const int VISITED = 2;
int n, M[N][N];
int status[N], dtime[N], ftime[N], _clock;
int next_v[N];
int next(int u){
for (int v = next_v[u]; v < n; v++){
next_v[u] = v + 1;
if (M[u][v]) return v;
}
return -1;
}
void dfs_visit(int r){
stack<int> S;
S.push(r);
status[r] = DISCOVERED;
dtime[r] = ++_clock;
while(!S.empty()){
int u = S.top();
int v = next(u);
if (v != -1){
if (status[v] == UNDISCOVERED){
status[v] = DISCOVERED;
dtime[v] = ++_clock;
S.push(v);
}
} else {
S.pop();
status[u] = VISITED;
ftime[u] = ++_clock;
}
}
}
void dfs(){
for (int i = 0; i < n; i++){
status[i] = UNDISCOVERED;
next_v[i] = 0;
}
_clock = 0;
for (int u = 0; u < n; u++){
if (status[u] == UNDISCOVERED)
dfs_visit(u);
}
for (int i = 0; i < n; i++){
cout << i+1 << " " << dtime[i] << " " << ftime[i] << endl;
}
}
int main()
{
int u, k, v;
cin >> n;
memset(M, 0, sizeof(M));
for (int i = 0; i < n; i++){
cin >> u >> k;
u--;
for (int j = 0; j < k; j++){
cin >> v;
v--;
M[u][v] = 1;
}
}
dfs();
return 0;
}
递归版本好理解,没啥好说的,重点要说一下栈实现版本中的next函数以及next_v[] 数组
next函数的作用是找该节点的下一个相邻顶点,next_v[] 数组是将图中所有的顶点的访问状态记录下来,这样可以保证当同时有两个节点可以走时,每次都是走编号比较小的那个节点。
参考链接:
[1] 维基百科-深度优先搜索
[2] (日)渡部有隆. 挑战程序设计竞赛 2 算法和数据结构. 北京:人民邮电出版社, 2016.09.