为了学好算法我也是拼了。。。 断断续续看了两天的深度优先搜索算法,始终都感觉没有get到问题的本质,今天下午又看了了半天资料也没想明白,索性去睡了一觉。。。。。 没有想到一觉睡醒来感觉豁然开朗 0.0, 诶!就很舒服~~ 一下从床上跳下来。看来学习的本质还是要多睡觉才行啊。 咳咳。
Depth-First Search,也就是DFS算法,一般可以用来遍历或者搜索树或图。基本思想用大白话来说是这样滴,比如说走迷宫(图1左侧假设是迷宫),我先从头开始随便选择一条路走到死 =。=,这一路上遇到所有的岔路口都随便选一条走,于是从a->b->d->h(一条任意的路径), OK 走到h没有路了,没路了我们就回到最后一次分叉的地方,也就是b, 再换一条路走到黑,这就叫回溯!这回走的是b->e->i, 妈耶又死路了,没事,回到最后一次分叉的地方,也就是e, 再换一条路走到黑, e->j, 走死了。这回回到e 节点后面的路都没了,回到b节点后面的路都走过了,我们走完了左边所有的可能性再回到a, 从a 开始走下一条刚才没走过的路,a->c->f->k。 剩下g 也是一样的方法走,差不多就是这样了。
刚才是大白话,现在我们来用变准一点的算法语言来描述一下这个过程:
输入:一个图(G) 和 G上的一个节点v
要求输出:从V节点开始深度优先遍历图的所有节点。
一般来说有两种实现这个遍历的方案,第一种是递归,第二种是用栈的数据结构。
1.递归实现深度优先搜索
1 procedure DFS(G, V) is
2 将顶点V标记为 “已遍历”
3 对于 V 的所有邻接点 W
4 如果 w 未被遍历过 则
5 深度优先遍历 (G,V)
原著,英文版本
procedure DFS(G, V) is
Label v as discovered
for all directes edges from v to w that are in G,adjacentEdges(V) do
if vertex w is not labeled as discovered then
recursively call DFS(G,W)
从字面上其实也能分析出来,首先我们选择顶点之后会因为 第5行的存在 一直往下走(递归)到底,并将这走过的顶点全部标记一边,当走到最底下之后,最后一层的V这时候没有邻接点了,也就是最后一层递归出口---->到了倒数第二层那个节点的For loop中(第三行), 明白这个意思了嘛,解递归的过程其实就是回溯每一个岔路口的过程!这个地方开始困扰了我好久。
好了我没来尝试代码实现一下吧 ^^, 图有两种表示方式,邻接矩阵 和 邻接表。我们先来用邻接矩阵表示一下图吧。
import
ok, 上面这段代码标识了一张这样的图。(纯手绘奥,给点掌声)
ok,图有了,那么接下来我们要开始进行搜索了。再来回顾一下算法的语言描述:
procedure DFS(G, V) is
1 Label v as discovered
2 for all directes edges from v to w that are in G,adjacentEdges(V) do
3 if vertex w is not labeled as discovered then
4 recursively call DFS(G,W)
我们这里就把顶点打印出来表示遍历过了,
/*
我们肯定需要两个函数
1。findNextVertex(i,w)。这个函数可以根据输入的顶点找到该顶点所有的邻接点,注意他在DFS中的意义是同层查找。
2. findVertex(i), 根据当前节点号返回下一个节点号,注意他在DFS中的意义是为了向下一层不断深入。
ok! 我们只需要完成 findNextVertex(i,w) 和 findVertex(i) 。
//再将这两个函数加入到Graph Class
public int findVertex(int i){
for(int j=0;j<vertices.size();j++){
if(edges[i][j]>0){
return j;
}
}
return -1;
}
public int findNextVertex(int v, int w){
for(int i=w+1;i<vertices.size();i++){
if(edges[v][i]>0){
return i;
}
}
return -1;
}
有没有想过一个问题: 就是说为什么要将 findNextVertex 和 findVertex 分成两个函数呢?一定有这个必要吗? 我是这样理解的,我们在发现下个未被标记过的的新节点的时候,想继续找下一层的时候用 findVertex(int i),因为我们只需要知道这个节点的标号就可以了,然后就可以根据邻接矩阵从这个节点所在的行(邻接矩阵中对应的行)开始遍历,目的只是要找到下一个相连的节点。 但是在我们递归函数回溯的时候,就需要用findNextVertex (v,w) 了, 原因是我们不想走重复的路,每次都从0开始这样就会无限回溯了,递归出口的时候我们会得到w 的值,也就是知道了当前我们走到了(v,w)这条路了,(v,1)(v,2)....(v,w-1), (v,w) 这些路线我们都尝试过了,所以直接走下一条路(v,w+1)就行了。(v,w+1)就相当于在进行刚才算法的第2步,对这一条路继续递归往下搜索, 下一次递归回溯回来的时候,我们就得到了(v,w +1),并继续走(v,w +2)这条路。 这就是原因啦!
2.栈实现深度优先搜索/非递归
第二种非递归的方法,好像这个方法还更好理解一些。
procedure DFS-iterative(G, v) is
let S be a stackS.push(v)while S is not empty dov = S.pop()if v is not labeled as discovered then
label v as discoveredfor all edges from v to w in G.adjacentEdges(v) do S.push(w)
public