深度优先搜索--不撞南墙不回头

深度优先搜索就好比走迷宫, 不断顺着一条路走, 直到走不通为止,
然后回退到上一个路口再向另外的方向行走(走过的方向就不会再走了,又不是傻子, 知道走不通,还向走不通的方向走), 不断重复(试过所有路口,
状态转移), 重复直到找到唯一的一条合适的路径; DFS可以看做是二叉树的先序遍历。

  • 问题引入

引入问题:输入一个数n,输出1~n的全排列。
在这里我们将问题形象化,举个例子,假如有编号为1,2,3的3张扑克牌和编号为1,2,3的3个盒子。现在需要将这3张扑克牌分别放到3个盒子里面,并且每个盒子有且只能放一张扑克牌。那么一共有多少种不同的放法呢?

  • 逐步深入

现在要生成的是全排列,约定一个顺序,每次到一个盒子面前,都先放1号,再放2号,最后放3号扑克牌。
开始,把1号扑克放进1号盒子,把2号扑克放进2号盒子,最后把3号扑克放进3号盒子,现在已经形成了一种排列,这个排列就是前面盒子中的扑克牌的号码,即“1,2,3”。
产生了一种排列之后,需要立即返回。
现在小哼 需要退一步回到3号盒子面前,需要取回之前放在3号盒子中的扑克牌,再去尝试看看还能否放别的扑克牌,从而产生一个新的排列。
于是小哼取回了3号扑克牌,当小哼再想往3号盒子放别的扑克的时候,发现手中仍然只有3号扑克牌,没有别的选择。于是小哼不得不再往后会退一步,回到2号盒子面前。
小哼回到2号盒子后,收回了2号扑克牌。现在小哼手里面有两张扑克牌了,分别是2号和3号扑克牌。按照之前约定的顺序,现在需要往2号盒子中放3号扑克牌(上一次放的是2号扑克牌)。放好之后小哼又向后走了一步,再次来到3号盒子面前。
小哼再次来到3号盒子后,将手中仅剩的2号扑克牌放入了3号盒子。此时又产生了一个新排列“1,3,2”
按照刚才?️ 步骤去模拟,便会依次生成所有排列:“2 1 3”,“2 3 1”,“3 1 2”,“3 2 1 ”。

  • 如何用程序实现呢?

说了这么半天,这么复杂的过程应该如何用程序实现呢?
我们现在来解决最基本的问题:如何往小盒子中放扑克牌。每个小盒子都可能放1号,2号,3号扑克牌,需要一一尝试,这里一个for循环就能解决。

这里数组a是用来表示小盒子的,变量step表示当前正处在第step个盒子面前。a[step]=i;就是将第i号扑克牌放入第step个盒子中。这里还有一个问题就是,如果一张扑克牌已经放到别的盒子中了,那么此时就不能再放入同样的扑克牌到当前的小盒子中,因为此时手中已经没有这张扑克牌了。因此还需要一个数组book来标记哪些牌已经使用了。

for (i=1;i<=n;i++)
    {
        if (book[i]==0)
        {
            a[step]=i;
            book[i]=1;
        }
    }
  • 如何处理第step+1个小盒子?

ok,现在已经处理完了第step个小盒子了,接下来需要往下走下一步,继续处理第step个小盒子。那么如何处理第step+1个小盒子呢?处理方法其实和我们刚刚处理第step个盒子的方法是相同的。因此很容易想到把刚才处理第step个小盒子的代码封装成一个函数,给这个函数起名为dfs。

写成函数之后,刚才的问题就很好解决了。在处理完第step个盒子之后,紧跟着处理第step+1个小盒子,处理第step+1 个小盒子的方法就是dfs(step+1).

dfs(step+1);
book[i]=0;

上面代码的 book[i]=0 这条语句非常重要,这句话的作用是将小盒子中的扑克牌收回,因为在一次摆放尝试返回的时候,如果不把刚才放入小盒中的扑克牌收回,那将无法进行下一次摆放。

还有一个问题,就是什么时候输出一个满足要求的序列呢?
其实当我们处理到第n+1个小盒子的时候,即step = n+1 ,那么说明前n个盒子都已经放好扑克牌了,这里就将1~n个小盒子中的扑克牌编号打印出来就可以了。⚠️ 注意,打印完毕一定要return,不然这个程序就会永无止境的运行下去 。

  • 完整的代码如下:
#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)   //book[i]等于0表示i号扑克牌仍然在手上
        {
            a[step]=i;    //将i号扑克牌放入到第step个盒子中
            book[i]=1;    //将book[i]置为1,表示i号扑克牌已经不在手上
            dfs(step+1);
            book[i]=0;
        }
    }
    return;
}

int main()
{
    scanf("%d",&n);
    dfs(1);
    getchar();getchar();
    return 0;
}
  • 深度优先搜索的基本模型
    理解深度优先搜索 的关键在于解决“当下该如何做”。至于“下一步如何做”,则与“当下如何做”是一样的。比如这里我们写的dfs(step)函数的主要功能就是解决第step个盒子的时候你该怎么办。通常的方法就是把每一种可能都去尝试一遍(一般使用for循环来遍历)。当前这一步解决后方便进入下一步dfs(step+1)。下一步解决方法和当前这步的解决方法是完全一样的。
    下面的代码是深度优先搜索的基本模型。
void dfs(int step)
{
    判断边界
    尝试每一种可能  for(i=1;i<n;i++)
    {
        继续下一步  dfs(step+1);
    }
    返回
}

每一种尝试就是一种“拓展”。每次站在一个盒子面前的时候,其实都有n中拓展方法,但是并不是每种拓展都能够拓展成功。

引用博文链接:https://blog.csdn.net/qq_40845344/article/details/82874580

以上内容均来自于人民邮电出版社出版的啊哈!算法!!!!!作者纪磊,有需要的可以直接购入图书,以上是本人的学习笔记。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值