DFS深度优先搜索基础入门(不用图论,一看就懂)


前言

刚学搜索算法时,在网上看到的大部分文章都是从图的遍历讲起,其实如果没学过图论也也可以了解dfs的一些基本做法。


一、问题引入

现在有n个箱子,有编号为1~n的纸牌,每个箱子只能放一张纸牌,输出将n张纸牌依次放入箱子的每种放法。

怎么办呢?先从比较简单的3张牌说起:


二、解题思路

如下图所示:(第一行是编号,第二行是数值, [   i   ] [\ i\ ] [ i ]表示纸牌)

123
/ / / / / / / / /

首先,我们将 [ 1 ] , [ 2 ] , [ 3 ] [1],[2],[3] [1][2][3]放入箱子,不过我们规定:
每次放牌,都是先放1号,如果1号已经放了,再放2号,以此类推,如图:

  1. 走到1号箱子前,放入1号纸牌:
123
[ 1 ] [1] [1] / / / / / /
  1. 再走到2号箱子前,按照规则,1号纸牌已经放了,所以放2号:
123
[ 1 ] [1] [1] [ 2 ] [2] [2] / / /
  1. 继续,走到3号箱子前,手中只有3号牌,所以放入3号牌:
123
[ 1 ] [1] [1] [ 2 ] [2] [2] [ 3 ] [3] [3]

此时,箱子已经放满了。或者说,把第三个箱子看作是“边界”,再往右走一格,到达假想的第四个箱子前,发现已经超过“边界”——第三个箱子,所以一种放置方法已经完成

现在我们退回来,取回第三个箱子中的纸牌,此时已经没有超出“边界”了,所以继续往前走,但手中还是只有3号牌,与第3步相同,所以再往后退,取回第2号箱子中的牌,此时,手中已经有两张牌了,于是——

  1. 在2号箱子前,按照规则,看看能不能放除了2号牌之外的其他牌。按照规则,可以放入3号牌:
123
[ 1 ] [1] [1] [ 3 ] [3] [3] / / /
  1. 同理,3号箱子放入2号牌:
123
[ 1 ] [1] [1] [ 3 ] [3] [3] [ 2 ] [2] [2]

又是一种放法。

同理,n张牌也采用同样的处理方式。

好了,说了这么多,看看代码怎么操作——


三、代码实现

  • 首先,我们可以用一个数组加上一个for循环来模拟放牌的过程:
for(int i=1;i<=n;i++)
    a[step]=i; //将编号为i的牌放入第step个箱子中
  • 由于一张牌不能放两次,所以我们用一个book数组来标记第i张牌有没有放过:
for(int i=1;i<=n;i++)
{
	if(!book[i]) //第i张牌没放过
	{
		a[step]=i;
		book[i]=true; //此时已经放过了
	}
}
  • 那么如何处理step+1个箱子呢?其实跟step个盒子的处理方法一样,所以我们可以想到采用递归的方法:
void dfs(int step) //深搜递归函数
{
	for(int i=1;i<=n;i++)
	{
		if(!book[i]) //第i张牌没放过
		{
			a[step]=i;
			book[i]=true; //此时已经放过了
			dfs(step+1); //递归处理下一个箱子
			book[i]=false; //这部非常关键,相当于将牌收回来,不然继续尝试可就会出问题
		}
	}
}
  • 当然,递归是要有边界的(不然就会陷入死循环了),边界就是到达第n+1个盒子前时停止:
void dfs(int step) //深搜递归函数
{
	if(step==n+1) //边界
	{
		for(int i=1;i<=n;i++) //输出答案
			cout<<a[i]<<" ";
		cout<<endl;
		return; //记得返回,否则递归就无穷无尽了
	}
	for(int i=1;i<=n;i++)
	{
		if(!book[i]) //第i张牌没放过
		{
			a[step]=i;
			book[i]=true; //此时已经放过了
			dfs(step+1); //递归处理下一个箱子
			book[i]=false; //这部非常关键,相当于将牌收回来,不然继续尝试可就会出问题
		}
	}
}

好了,来看看完整代码:

#include<iostream>
using namespace std;

int a[10],n;
bool book[10]; //标记数组

void dfs(int step) //深搜递归函数
{
	if(step==n+1) //边界
	{
		for(int i=1;i<=n;i++) //输出答案
			cout<<a[i]<<" ";
		cout<<endl;
		return; //记得返回,否则递归就无穷无尽了
	}
	for(int i=1;i<=n;i++)
	{
		if(!book[i]) //第i张牌没放过
		{
			a[step]=i;
			book[i]=true; //此时已经放过了
			dfs(step+1); //递归处理下一个箱子
			book[i]=false; //这部非常关键,相当于将牌收回来,不然继续尝试可就会出问题
		}
	}
}

int main()
{
	cin>>n;
	dfs(1); //站在第1个箱子前
	return 0;
}

其实,这就是最基础的“生成全排列”问题。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值