深度优先搜索
事实上,深度优先搜索属于图算法的一种,其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。
举例说明之:下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束)。
下面我们有一个例子来分析其过程:
假如有编号为1,2,3,的3张扑克牌和编号为1,2,3的3个盒子。现在需要将这3张扑克牌分别放到3个盒子里面,并且每个盒子有且只能放一张扑克牌。那么一共有多少种不同的放法呢?
我们现在来解决最基本的问题:如何往小盒子里放扑克牌。每个小盒子都有可能放1号,2号或者3号扑克牌,这需要一一去尝试,这里一个for循环就可以解决。
for(i = 1; i <= n; i ++)
{
a[step] = i;//将i号扑克牌放入第step个盒子
}
这里数组a是用来表示小盒子的,变量step表示当前正处于第step个小盒子面前。这里有一个问题就是,如果一张扑克牌已经放到别的小盒子中了,那么此时就不能再放入同样的扑克牌到当前小盒子中,因为此时手中已经没有那张扑克牌了。因此,还需要一个数组book来标记哪些牌已经使用了。
for(i = 1; i <= n; i ++)
{
if(book[i] == 0)//book[i]等于0表示i号扑克牌仍然在手上
{
a[step] = i;
book[i] = 1;//将book[i]设为1,表示i号扑克牌已经不在手上了
}
}
OK,现在已经处理完第step个小盒子了,接下来需要往下走一步,继续处理第step+1个小盒子。那么如何处理第step+1个小盒子呢?处理方法其实和我们刚才处理step是一样的。因此就可以想到把刚才的处理第step个小盒子的代码封装为一个函数,dfs函数。
void dfs(int step)//step表示现在站在第几个盒子面前
{
for(i = 1; i <= n; i ++)
{
if(book[i] == 0)
{
a[step] = i;
book[i] = 1;
}
}
return 0;
}
把这个过程写成函数后,刚才的问题就好办了。在处理完第step个小盒子之后,紧接着处理第step+1个小盒子,处理step+1个小盒子的方法就是dfs(step+1),请注意下面代码标记的语句。
void dfs(int step)
{
for(i = 1; i <= n; i ++)
{
if(book[i] == 0)
{
a[step] = i;
book[i] = 1;
dfs(step+1);//这里通过函数的递归调用来实现(自己调用自己)
book[i] = 0;// 这是非常重要的一步 ,一定要将刚才尝试的扑克牌收回,
//才能进行下一次尝试
}
}
return 0;
}
还剩下一个问题,什么时候该输出一个满足要求的序列呢。其实当我们处理到第n+1个小盒子的时候(即step等于n+1),那么说明前n个盒子都已经放好扑克牌了,这里将1到n个小盒子中的扑克牌编号输出就可以了。
void dfs(int step)
{
if(step == n+1)
{
for(i = 1; i <= n; i ++)
printf("%d",a[i]);
printf("\n");
return ;//返回之前的一步(最近一次调用dfs函数的地方)
}
for(i = 1; i <= n; i ++)
{
if(book[i] == 0)
{
a[step] = i;
book[i] = 1;
dfs(step+1);//这里通过函数的递归调用来实现(自己调用自己)
book[i] = 0;// 这是非常重要的一步 ,一定要将刚才尝试的扑克牌收回,
//才能进行下一次尝试
}
}
return 0;
}
完整代码
#include<stdio.h>
int a[10],book[10],n;
void dfs(int step)
{
int i;
if(step == n+1)
{
for(i = 1; i <= n; i ++)
printf("%d",a[i]);
printf("\n");
return ;
}
for(i = 1; i <= n; i ++)
{
if(book[i] == 0)
{
a[step] = i;
book[i] = 1;
dfs(step+1);
book[i] = 0;
}
}
return ;
}
int main()
{
while(scanf("%d",&n) != EOF)
{
dfs(1);//首先站在1号小盒子面前
}
return 0;
}