DFS深度优先搜索算法
2015-12-13 16:15
267 查看
DFS算法全英文名为:Depth First Search,中文名字叫做深度优先搜索,它的基本思想是沿着树的深度遍历树的节点,尽可能深的搜索树的分支,当该节点的所有边都已经被探寻过,那么就回溯到离这节点最近的之前的节点,然后继续这一步骤,知道所有的可能性都被遍历出来之后就可以了,该思想类似于回溯法,从最深度起一层一层的遍历然后回溯。
DFS的关键在于解决“当下应该如何做”,当下这一步做好了之后,对后续的步骤就依葫芦画瓢,与当下如何做是一样的。比如,我们将几个不同的扑克牌放入几个盒子里面,那么根据全排列我们可以知道有很多种方法,如果让我们来求出所有的访法的话,我们只需要解决当下如何做,也就是如何去放,我们可以使用循环语句来将扑克牌放入盒子里,然后使用一个中间数组来判断扑克牌是否已经被放入,这样就解决了如何放的问题,然后后面就可以根据相同的方法来求出另外几种不同的方法,然后就求出所有的方法了呗。下面的代码就是深度优先搜索的基本模型:
void dfs(int step)
{
//判断边界
if(step==n)
{
….
return ;
}
}
下面我们来举几个例子,
Q1.扑克牌的放置(全排列问题):假如有编号为1/2/3的三张牌,和编号为1、2、3的三个盒子,现在需要将这三张扑克牌分别放于不同的盒子里面,并且每个盒子里面只能放一张牌,那么一共有多少种放法呢?
A1.根据DFS的基本思想来看,我们可以这样来做:
1.先将一号牌放于一号盒子,再将二号牌放于二号盒子,再将三号牌放于三号盒子;然后我们发现手里的牌和盒子都刚好用完了。
2.然后我们在到三号盒子那里将三号牌取出来,手里只有一张三号牌,还剩一个三号盒子,然而三号牌刚放过三号盒子了,所以我们不放。
3.在到二号盒子面前讲二号牌取出来,我们发现手里面有两张牌,因为刚才二号对应二号盒子,三号对应三号盒子,因此我们不能重复放了,这次我们将三号牌放于二号盒子,将二号牌放于三号盒子,
4.然后我们又重复步骤2和3,然后我们取出一号盒子的牌,同理,几号盒子之前已经对应几号牌放置了,这次我们在一号盒子放入2号牌(因为我们在循环的时候是从1到最后的数的,因此我们这里放的是2号而不是1号,当然,主要的不是顺序问题,还有一个就是他们两个都是还未放置状态),
5.然后我们就这样重复下去,最终我们可以得到所有放置的可能性。
下面给出这个问题的完整代码:
#include
DFS的关键在于解决“当下应该如何做”,当下这一步做好了之后,对后续的步骤就依葫芦画瓢,与当下如何做是一样的。比如,我们将几个不同的扑克牌放入几个盒子里面,那么根据全排列我们可以知道有很多种方法,如果让我们来求出所有的访法的话,我们只需要解决当下如何做,也就是如何去放,我们可以使用循环语句来将扑克牌放入盒子里,然后使用一个中间数组来判断扑克牌是否已经被放入,这样就解决了如何放的问题,然后后面就可以根据相同的方法来求出另外几种不同的方法,然后就求出所有的方法了呗。下面的代码就是深度优先搜索的基本模型:
void dfs(int step)
{
//判断边界
if(step==n)
{
….
return ;
}
//尝试每一种可能 for(...) { //判断是否被访问等等 if(book[i]==0) { ... //标记已经访问等等 book[i]=1; //使用递归的方式继续下一步 dfs(step+!); //重新设置该店为未访问,重新下一次尝试 book[i]=0; } } return ;
}
下面我们来举几个例子,
Q1.扑克牌的放置(全排列问题):假如有编号为1/2/3的三张牌,和编号为1、2、3的三个盒子,现在需要将这三张扑克牌分别放于不同的盒子里面,并且每个盒子里面只能放一张牌,那么一共有多少种放法呢?
A1.根据DFS的基本思想来看,我们可以这样来做:
1.先将一号牌放于一号盒子,再将二号牌放于二号盒子,再将三号牌放于三号盒子;然后我们发现手里的牌和盒子都刚好用完了。
2.然后我们在到三号盒子那里将三号牌取出来,手里只有一张三号牌,还剩一个三号盒子,然而三号牌刚放过三号盒子了,所以我们不放。
3.在到二号盒子面前讲二号牌取出来,我们发现手里面有两张牌,因为刚才二号对应二号盒子,三号对应三号盒子,因此我们不能重复放了,这次我们将三号牌放于二号盒子,将二号牌放于三号盒子,
4.然后我们又重复步骤2和3,然后我们取出一号盒子的牌,同理,几号盒子之前已经对应几号牌放置了,这次我们在一号盒子放入2号牌(因为我们在循环的时候是从1到最后的数的,因此我们这里放的是2号而不是1号,当然,主要的不是顺序问题,还有一个就是他们两个都是还未放置状态),
5.然后我们就这样重复下去,最终我们可以得到所有放置的可能性。
分析DFS的基本模型内容: 1.边界: 这里dfs函数的参数step代表的是当前所要放置和盒子的位置,因此这里的搜索边界应该是step>3,也就是step>n if(step>n) return ; 2.尝试每一种可能: 我们是将扑克牌放于盒子里面,因此我们要尝试每次将当前扑克牌放入当前盒子里面: for(int i=1;i<=3;i++) { if(book[i]==0) { a[step]=i; book[i]=1; } } 在这里book数组是用来标记当前位置的扑克牌是否已经被放入,我们设置为0为未放入,1为已放入 3.递归继续下一步: 在这里继续下一步也就是继续下一个盒子的放入,我们可以在刚才的循环语句里面加入递归: for(int i=1;i<=3;i++) { if(book[i]==0) { book[i]=1; a[step]=i; dfs(step+!); } } 4.在这里如果当前排序尝试完了,这会是什么情况呢?从上面的代码我们可以看出,当尝试第一次尝试完毕之后,第一个排序使出来了,但是book数组里面的值全部都变成1也就是变成所有的扑克牌已经被放入了,那么就是说循环语句的条件book{i]==0变成了矛盾式。因此我们需要在每次尝试完毕之后将book数组的元素都重置。但这样也不好,因为如果每次全部重置的话,那么我们就无法得到只有一两个数变化的排列了,比如我们得到了123之后直接将数组book重置为0,那么我们就无法得到132这种排序方式了,因此我们可以在每次尝试完毕之后将上一次的book标记重置为0,以便于下次继续使用,也就是在递归DFS之后重置book[i]=0;: for(int i=1;i<=n;i++) { if(book[i]==0) { a[step]=i; book[i]=1; dfs(step+!); book[i]=0; } } //最后返回 return ;
下面给出这个问题的完整代码:
#include
相关文章推荐
- 程序员编程武器大盘点
- iOS UI 12 tabbarcontroller
- AngularJS实战(七)
- 程序员编程武器大盘点
- c++异常处理 try catch
- iOS UI 12 tabbarcontroller
- Codevs_P1021 玛丽卡(Dijkstra)
- 新建用户和用户组的shell
- 将博客搬至CSDN
- 2015年12月13日 spring初级知识讲解(四)面向切面的Spring
- IL指令详细
- 判断Activity 存在的方法
- java资源定位(非web项目)
- CentOS6.5 安装vncserver实现图形化访问
- Android 自定义卫星式弧形菜单
- mysql操作与调优
- Android中网络请求放在子线程中问题
- iOS UI 12 block传值
- Struts2框架学习之五:通用标签详解
- win32自绘按钮,使用GDI+(二)