回溯、递归、DFS方法

转自:https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html1.

1.回溯解法框架

我觉得这个递归方法更好理解,迭代的方法我没太看懂。

   1: int a[n];
   2: try(int i)
   3: {
   4:     if(i>n)
   5:        输出结果;
   6:      else
   7:     {
   8:        for(j = 下界; j <= 上界; j=j+1)  // 枚举i所有可能的路径
   9:        {
  10:            if(fun(j))                 // 满足限界函数和约束条件
  11:              {
  12:                 a[i] = j;
  13:               ...                         // 其他操作
  14:                 try(i+1);
  15:               回溯前的清理工作(如a[i]置空值等);
  16:               }
  17:          }
  18:      }
  19: }

以非常典型的,leetcode22题,生成括号为例:

class Solution(object):
    def generateParenthesis(self, N):
        ans = []
        def backtrack(S = '', left = 0, right = 0):
            if len(S) == 2 * N: #给出解空间
                ans.append(S)
                return
            if left < N:#这两个if判断都是剪枝
                backtrack(S+'(', left+1, right) #扩展搜索节点
            if right < left:#这里是关键
                backtrack(S+')', left, right+1)

        backtrack()
        return ans

    (1)针对所给问题,确定问题的解空间:

            首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。 (对应if判断解)

    (2)确定结点的扩展搜索规则    (确定往下搜索调用)

    (3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。(if判断递归调用条件)

https://leetcode.com/articles/letter-combinations-of-a-phone-number/ 就像这道题,它只是一个简单的深度优先遍历然后得到所有的解,我觉得它关键是没有剪枝和回去的过程,只是遍历到最深的节点,然后返回了。

2.DFS和回溯的关系

https://www.1point3acres.com/bbs/thread-137584-1-1.html这个讲的挺好的。

特点:就是recursive call之后要退回到之前的一个状态。

回溯是DFS的一种,是它的子集。

回溯不保留树结构,也就是剪枝的意思。 

定义:https://stackoverflow.com/questions/1294720/whats-the-difference-between-backtracking-and-depth-first-search

回溯是一种更通用的算法。

深度优先搜索是与搜索树结构相关的回溯的一种特定形式。

3.递归和回溯

https://blog.csdn.net/fengchi863/article/details/80086915 这个补充的也挺好的。

  • 递归是一种算法结构,回溯是一种算法思想。
  • 一个递归就是在函数中调用函数本身来解决问题。
  • 回溯就是通过不同的尝试来生成问题的解,有点类似于穷举,但是和穷举不同的是回溯会“剪枝”。

递归的一般过程:

void f()  
{  
     if(符合边界条件)  
     {  
        ///////  
        return;  
     }  

     //某种形式的调用  
     f();  
}  

回溯的一般过程:

void DFS(int 当前状态)  
{  
      if(当前状态为边界状态)  
      {  
        记录或输出  
        return;  
      }  
      for(i=0;i<n;i++)       //横向遍历解答树所有子节点  
      {  
           //扩展出一个子状态。  
           修改了全局变量  
           if(子状态满足约束条件)  
            {  
              dfs(子状态)  
           }  
            恢复全局变量//回溯部分  
      }  
}  

BFS和DFS相似。BFS显式用队列,DFS隐式用栈,即递归。

2020-4-3周五更新——————————————

1.回溯的三种问题方式 

https://segmentfault.com/a/1190000006121957

1.有没有解

2.有多少个解

3.找出所有解是什么?

这三种分别对应着不通的返回值,当然框架都是一样的吧。

DFS它也是一种枚举的方法,暴力地走所有的路判断。

第一种,返回值是true/false。
第二种,求个数,设全局counter,返回值是void;求所有解信息,设result,返回值void。
第三种,设个全局变量best,返回值是void。

第一种:

boolean solve(Node n) {
    if n is a leaf node {
        if the leaf is a goal node, return true
        else return false
    } else {
        for each child c of n {
            if solve(c) succeeds, return true
        }
        return false
    }
}

第二种:

void solve(Node n) {
    if n is a leaf node {
        if the leaf is a goal node, count++, return;
        else return
    } else {
        for each child c of n {
            solve(c)
        }
    }
}

第三种:

void solve(Node n) {
    if n is a leaf node {
        if the leaf is a goal node, update best result, return;
        else return
    } else {
        for each child c of n {
            solve(c)
        }
    }
}

好了,学习了。记住这个模板就ok了吧。

原文地址:https://www.cnblogs.com/BlueBlueSea/p/12494908.html