当前位置首页 > 百科> 正文

深度优先搜寻

2019-07-16 13:59:02 百科
深度优先搜寻

深度优先搜寻

深度优先搜寻是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜寻结构的叶结点(即那些不包含任何超链的HTML档案) 。在一个HTML档案中,当一个超链被选择后,被连结的HTML档案将执行深度优先搜寻,即在搜寻其余的超链结果之前必须先完整地搜寻单独的一条链。深度优先搜寻沿着HTML档案上的超链走到不能再深入为止,然后返回到某一个HTML档案,再继续选择该HTML档案中的其他超链。当不再有其他超链可选择时,说明搜寻已经结束。

基本介绍

  • 中文名:深度优先搜寻
  • 外文名:Depth-First-Search
  • 提出者:霍普克洛夫特与罗伯特·塔扬
  • 套用学科:计算机

详细解释

事实上,深度优先搜寻属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.
举例说明之:下图是一个无向图,如果我们从A点发起深度优先搜寻(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜寻结束).简要说明深度优先搜寻的特点:每次深度优先搜寻的结果必然是图的一个连通分量.深度优先搜寻可以从多点发起.如果将每个节点在深度优先搜寻过程中的"结束时间"排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个鍊表),则我们可以得到所谓的"拓扑排序",即topological sort.
图

基本思路

深度优先遍历图的方法是,从图中某顶点v出发:
(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。 当然,当人们刚刚掌握深度优先搜寻的时候常常用它来走迷宫.事实上我们还有别的方法,那就是广度优先搜寻(BFS).

穷举

在我们遇到的一些问题当中,有些问题我们不能够确切的找出数学模型,即找不出一种直接求解的方法,解决这一类问题,我们一般採用搜寻的方法解决。搜寻就是用问题的所有可能去试探,按照一定的顺序、规则,不断去试探,直到找到问题的解,试完了也没有找到解,那就是无解,试探时一定要试探完所有的情况(实际上就是穷举);
对于问题的第一个状态,叫初始状态,要求的状态叫目标状态。
搜寻就是把规则套用于实始状态,在其产生的状态中,直到得到一个目标状态为止。
产生新的状态的过程叫扩展(由一个状态,套用规则,产生新状态的过程)
搜寻的要点:(1)初始状态;
(2)重複产生新状态;
(3)检查新状态是否为目标,是结束,否转(2);
如果搜寻是以接近起始状态的程式依次扩展状态的,叫宽度优先搜寻。
如果扩展是首先扩展新产生的状态,则叫深度优先搜寻。
深度优先搜寻
深度优先搜寻用一个数组存放产生的所有状态。
(1) 把初始状态放入数组中,设为当前状态;
(2) 扩展当前的状态,产生一个新的状态放入数组中,同时把新产生的状态设为当前状态;
(3) 判断当前状态是否和前面的重複,如果重複则回到上一个状态,产生它的另一状态;
(4) 判断当前状态是否为目标状态,如果是目标,则找到一个解答,结束算法。
(5) 如果数组为空,说明无解。
对于pascal语言来讲,它支持递归,在递归时可以自动实现回溯(利用局部变数)所以使用递归编写深度优先搜寻程式相对简单,当然也有非递归实现的算法。
搜寻是人工智慧中的一种基本方法,是一项非常普遍使用的算法策略,能够解决许许多多的常见问题,在某些情况下我们很难想到高效的解法时,搜寻往往是可选的唯一选择。按照标準的话来讲:搜寻算法是利用计算机的高性能来有目的的穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。
搜寻虽然简单易学易于理解,但要掌握好并写出速度快效率高最佳化好的程式却又相当困难,总而言之,搜寻算法灵活多变,一般的框架很容易写出,但合适的最佳化却要根据实际情况来确定。在搜寻算法中,深度优先搜寻(也可以称为回溯法)是搜寻算法里最简单也最常见的,今天我们就从这里讲起,下面的内容假设读者已经知道最基本的程式设计和简单的递归算法。

系统算法

所有的搜寻算法从其最终的算法实现上来看,都可以划分成两个部分──控制结构和产生系统。正如前面所说的,搜寻算法简而言之就是穷举所有可能情况并找到合适的答案,所以最基本的问题就是罗列出所有可能的情况,这其实就是一种产生式系统。
我们将所要解答的问题划分成若干个阶段或者步骤,当一个阶段计算完毕,下面往往有多种可选选择,所有的选择共同组成了问题的解空间,对搜寻算法而言,将所有的阶段或步骤画出来就类似是树的结构(如图)。
从根开始计算,到找到位于某个节点的解,回溯法(深度优先搜寻)作为最基本的搜寻算法,其採用了一种“一只向下走,走不通就掉头”的思想(体会“回溯”二字),相当于採用了先根遍历的方法来构造搜寻树。
上面的话可能难于理解,没关係,我们通过基本框架和例子来阐述这个算法,你会发现其中的原理非常简单自然。

基本框架

·dfs(状态)
–if 状态 是 目标状态then
·dosomething
–else
·for 每个新状态
–if 新状态合法
»dfs(新状态)
·主程式:
·dfs(初始状态)

C++的实现

定义一个结构体来表达一个NODE的结构:
struct Node  {    int self; //数据     node *left; //左节点     node *right; //右节点  };
那幺我们在搜寻一个树的时候,从一个节点开始,能首先获取的是它的两个子节点。例如:
“                A           B           C      D   E          F   G”
A是第一个访问的,然后顺序是B和D、然后是E。然后再是C、F、G。那幺我们怎幺来保证这个顺序呢?
这里就应该用堆叠的结构,因为堆叠是一个先进后出的顺序。通过使用C++的STL,下面的程式能帮助理解:
“    const int TREE_SIZE = 9;     std::stack<node*> visited, unvisited;     node nodes[TREE_SIZE];     node* current;     for( int i=0; i<TREE_SIZE; i++) //初始化树         {                nodes[i].self = i;               int child = i*2+1;                if( child<TREE_SIZE ) //Left child                   nodes[i].left = &nodes[child];                else nodes[i].left = NULL;                child++;                if( child<TREE_SIZE ) //Right child                       nodes[i].right = &nodes[child];                else       nodes[i].right = NULL;        }                  unvisited.push(&nodes[0]); //先把0放入UNVISITED stack       while(!unvisited.empty()) //只有UNVISITED不空       {             current=(unvisited.top()); //当前应该访问的             unvisited.pop();               if(current->right!=NULL)              unvisited.push(current->right); // 把右边压入 因为右边的访问次序是在左边之后              if(current->left!=NULL)              unvisited.push(current->left);              visited.push(current);              cout<<current->self<<endl;       }”

举例

这道题来举例(迷宫)
1
1
1
1
0
1
0
1
0
1
0
1
0
1
1
1
记录起点为(1,1)找到所有的到(4,4)的路径.
pascal程式如下:
constb:array[1..4,1..4]of integer=((1,1,1,1),(0,1,0,1),(0,1,0,1),(0,1,1,1));c:array[1..4,1..2]of -1..1=((0,1),(0,-1),(1,0),(-1,0));vara:array[1..16,1..2]of integer;procedure print;vari,j:integer;beginfor i:=1 to 4 dobeginfor j:=1 to 4 dowrite(b[i,j]:3);writeln;end;writeln('--------------');end;procedure try(k:integer);vari:integer;beginif (a[k,1]=4)and(a[k,2]=4) thenbeginprint;exit;end;for i:=1 to 4 dobegina[k+1,1]:=a[k,1]+c[i,1];a[k+1,2]:=a[k,2]+c[i,2];if (a[k+1,1]>=1) and (a[k+1,1]<=4 )and (a[k+1,2]>=1) and (a[k+1,2]<=4) and(b[a[k+1,1],a[k+1,2]]=1) thenbeginb[a[k+1,1],a[k+1,2]]:=2;try(k+1);b[a[k+1,1],a[k+1,2]]:=1;end;end;end;begina[1,1]:=1;a[1,2]:=1;b[1,1]:=2;try(1);end.
这个程式的意思就是:进行搜寻一条路,直到不能走为止,换另一条路。
声明:此文信息来源于网络,登载此文只为提供信息参考,并不用于任何商业目的。如有侵权,请及时联系我们:baisebaisebaise@yeah.net