试设计递归算法dfs traverse_Depth-First Search (DFS算法)

fe014b89437da9672e6116b0fc80ea7f.png

为了学好算法我也是拼了。。。 断断续续看了两天的深度优先搜索算法,始终都感觉没有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 也是一样的方法走,差不多就是这样了。

5aaf89f6aeb2a6a85db86c68a9224062.png
图1: 左侧深度优先

刚才是大白话,现在我们来用变准一点的算法语言来描述一下这个过程:

输入:一个图(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, 上面这段代码标识了一张这样的图。(纯手绘奥,给点掌声)

1c58465a631dcc18a2b251e8ba7b8857.png
图2:上面代码表示的图

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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值