美文网首页
dfs与bfs的套路总结

dfs与bfs的套路总结

作者: hekirakuno | 来源:发表于2020-01-09 03:02 被阅读0次

为了我亲爱的鸽鸽!
致敬!

dfs和bfs,顾名思义就是,深度优先搜索和广度优先搜索的简称。那么什么是深度优先什么是广度优先?
举个简单的例子吧。
比如说电话的9键中。
2--a,b,c
3--d,e,f
4--g,h,i
……
有着这样的对应关系。
那么当我依次按下,2,3,4的时候,一共会有多少种组合呢?
首先我们用深度优先搜索的思路来讲。
深度优先,就是每次都会遍历得到一个结果,并将其放入结果集合中。每次都要走到无路可走,才知道回头。
最直观的反映,让我们来看结果集的样子。它会是这样增加的:
[{a,d,g}]-->[{a,d,g},{a,d,h}]-->[{a,d,g},{a,d,h},{a,d,i}]-->[{a,d,g},{a,d,h},{a,d,i},{a,e,g}]……
先找到一个结果,然后通过回溯查找,每次都在集合中增加一个可达结果。

为了迅速找到不同,接下来我们用广度优先搜索的思路来讲。
广度优先,逐层遍历,当把第一层所有情况都记录下以后,再进行第二层的遍历。
最直观的反映在结果集的样子。它会是这样增加的:
[{a},{b},{c}]-->
[{a,d},{a,e],{a,f}]-->[{a,d},{a,e],{a,f},{b,d},{b,e},{b,f}]-->[{a,d},{a,e],{a,f},{b,d},{b,e},{b,f},{c,d},{c,e},{c,f}]-->
[{a,d,g},{a,d,h},{a,d,i}……]
每次都在之前已有记录的基础上,遍历当前行拼接之后的所有情况。


一般来说,这种找到【组合】类型,获得【所有结果】,列出【所有情况】的题目,就可以考虑使用这两种解法。【贪心,动归等不要杠精,我只是说暴搜解而已】
那么,他们分别更加适用的场景是什么呢?
我们观察和思考了算法之后,很容易想到,当结果集庞大,每个最终结果的深入层次比较浅时,或者只需求最终结果时,选择bfs更快更方便;当每个最终结果都需要寻觅非常深入,或者存在回溯查找时,选择dfs会更好。
毕竟,广度优先和深度优先的名字摆在这里。自然是在得意领域更加肆意了。

好的,我们看下它们的必要条件:可重复利用上一次的结果 || 需要当前结果的状态 || 需要记录当前结果||需要记录所有已知情况

需要之前的状态和结果记录啊。也就是说可以选择用递归的方式。

而且,其实dfs和bfs的代码真的很有套路。基本上你确定了用哪一个之后,就可以套用类似模板的写法了。干倒一般性难度的题目不成问题。

回归本质,我们用这道题简化版来举个例子:
如图,二维数组,从每个子数组中取出一个值,找到所有排列组合。
String arr =
{{a,b,c},
{d,e,f},
{g,h,i}}

//dfs模式样板
public class solution{
    public List<List<String>> getResult(String[][] arr){
        //第一行,定义结果集合
        List<List<String>> result = new ArrayList<List<String>>();
        //第二行,定义每次单独产生的结果
        List<String> temp = new ArrayList<String>();
        //第三行,传入第一次dfs的条件(空的结果数组,空的tmp,开始深度0,目标数组),通过dfs拿到最终的结果。
        dfs(result,temp,0);
        //第四行,返回结果
        return result;
    }
    //dfs的逻辑
    public void dfs(List<List<String>> result,List<String> temp,int depth){
        //递归出口。返回条件-->找到一个有效结果(或者说,一条道走到死,走到无路可走,最深处)
        if(arr.length() == depth){
            //将当前结果添加到结果集合中
            result.add(new ArrayList<String>(temp));
            //如果查找到一条结果,则回溯到上个状态
            return;
        }else{
            //dfs相当于用depth这个参数做了外层的,也就是深度的控制,让你每次都更加向内走
            for(int i = 0;i<arr[depth].length;i++){
              //添加当前深度的当前字符
              temp.add(arr[depth][i]);
              //带着当前的中间态结果temp去下一层,直到找到最深一层。然后记录结果。return当前调用。
              dfs(result,temp,depth+1);
             //举例来说:第一次搜索后得到的结果是[a,d,g],此时深度为目标深度,因此,记录结果到result中,然后return。
            //此时回到**dfs(result,temp,depth+1);**这一句。此时depth为1,也就是倒数第二层。temp中有[a,d,g]三个对象,result中有一条记录[[a,d,g]]。然后,我们删掉最后的对象g,让temp为[a,d]的状态,来到当前深度的第二个字符面前。
              temp.remove(temp.size()-1);
             //然后temp会变成[a,d,h],然后再进入最后一层。然后再记录到result中,然后再回到倒数第二层。以此类推。
        }
    }
}

所以当最后dfs遍历完成后,result数组中就保存了所有结果。
有没有稍微明白一点点了呢?
所有的dfs基本都是这样的套路。

//结果函数
solution(){
    //最终结果集
    new resultSets;
    //单个结果
    new resultOne;
    //初始调用dfs
    dfs(resultSets,resultOne,0);
    return result;
}
dfs(T<M> resultSets,M resultOne, int depth){
    //递归出口
    if(depth == 最深){
         //这里每一次的单个结果都需要new一次
         resultSets.add(new resultOne);
         //回溯
         return;
    }else{
          //当前深度的数据数据获取
          for(int i = 0;i<arr[depth],length;i++){
                //记录当前深度的当前下标的数据
                resultOne.add(arr[depth][i]);
                //寻找下一层
                dfs(resultSets,resultOne,depth+1);
                //清除下一层的对象数据
                resultOne.remove(resultOne.size()-1);
          }
    }
}

好的,接下来是bfs,同样的题目。
我们来看bfs

class Solution {
    public List<List<String>> getResult(String[][] arr){
        //第一行,定义结果集合
        List<List<String>> result = new ArrayList<List<String>>();
        //第三行,使用广度优先搜索的方式
        result = bfs(result,0);
        //第四行,返回结果
        return result;
    }
    public List<List<String>> bfs(List<List<String>> result,int depth){
        //递归出口
        if(depth==arr.length){
            return;
        }else{
          List<List<String>> tempResult = new ArrayList<List<String>>();
          //不是第一行了,循环遍历,补充结果集
          if(result.size()>0){
              for(List<String> temp:result){
                  for(int i = 0;i<arr[depth].length;i++){
                      tempResult.add(temp.add(arr[depth][i]));
                  }
              }
          }else{
              //第一次遍历,初始化结果集,添加第一层的值
               for(int i = 0;i<arr[depth].length;i++){
                   tempResult.add(new ArrayList(arr[depth][i]));
               }
           }
          bfs(tempResult,depth+1);
    }
}

好困啊···明天再整理bfs模板吧。不过其实很容易就看明白了。
白版纯手打代码,这个格式和空格和制表符好难搞,难受得不行。
就这样吧,先假装写完啦~~~

相关文章

  • dfs与bfs的套路总结

    为了我亲爱的鸽鸽!致敬! dfs和bfs,顾名思义就是,深度优先搜索和广度优先搜索的简称。那么什么是深度优先什么是...

  • 用Python实现树的BFS与DFS

    一、BFS与DFS简介 在理解动态规划、BFS和DFS一文中,已经集合具体例子,介绍了图的BFS与DFS。但是比较...

  • BFS与DFS总结

    BFS判断连通性 DFS判断连通性 BFS最优解(不走重复路径) BFS最优解(走重复路径) DFS回溯(不走重复...

  • Binary Tree(2)

    BFS vs DFS for Binary Tree What are BFS and DFS for Binar...

  • DFS与BFS LeetCode 刷题小结(一)

    本节我们将汇总一些 LeetCode bfs与dfs相关的题。 关于深度优先搜索(DFS)和广度优先搜索(BFS)...

  • 海岛问题

    通过这个问题把BFS、DFS、并查集的一些思路和程序主体模板进行一下总结。 其中BFS和DFS属于最基本最容易直接...

  • LeetCode 第104题:二叉树的最大深度

    1、前言 2、思路 采用 DFS 或者 BFS 都可以。 3、代码 DFS: BFS:

  • 133. Clone Graph 复制无向图

    bfs dfs

  • BFS和DFS

    DFS BFS

  • BFS与DFS

    广度优先遍历 (BFS) 类似树的层次遍历,首先访问起始顶点v,然后选取与v邻接的全部顶点w1,w2,…wn,进行...

网友评论

      本文标题:dfs与bfs的套路总结

      本文链接:https://www.haomeiwen.com/subject/rtueactx.html