深度优先搜索和回溯(实例LeetCode 39 Combination Sum)

搜索是广泛使用的算法策略,就是用问题的所有可能按照一定的顺序、规则去试探。它利用计算机的高性能来有目的的穷举一个问题的部分或所有可能情况,从而求出问题的解。而深度优先搜索是搜索算法中最简单最常见的。

         深度优先搜索(DepthFirst Search,缩写DFS),属于图算法的一种。DFS基本思想是对每一个可能的分支路径深入到不能再深入为止,且每个节点只能访问一次。如下图所示,为一无向图,其各点访问过程如下表:

(1)

A–>B–>E(不能再深入,回到A)

(2)

–>C–>F–>H–>G–>D(回到A,发现A不存在未访问相邻节点,结束搜索,若A存在未访问相邻节点,则由此节点开始搜索)

 

         回溯搜索是深度优先搜索(DFS)的一种,回溯法通俗的将其采用的思想是“一直向下走,走不通就掉头”,类似于树的先序遍历。dfs和回溯法其主要的区别是:回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树。

         为了减少存储空间,在深度优先搜索中,用标志的方法记录访问过的状态,这种处理方法使得深度优先搜索法与回溯法没什么区别了。

由于回溯法花费时间较长,所以对于没有明确的动态规划(DP)和递归解法的或问题要求满足某种性质(约束条件)的所有解或最优解时,才考虑使用回溯法。

例如:使用程序输出1-n之间整数的所有排列组合,实现方法一次确定第一、二、三个位置。

其整个调用回溯过程如下(按序号依次执行):

① DFS(0)  i = 1

 

 

 

 

 

 

flag

nums

step

 

 

 

 

 

 

[1,0,0]

[1,0,0]

0

 

 

 

 

 

 

 

② DFS(1)  i = 1  i = 2

回溯结果i = 3

 

 

 

 

flag

nums

step

flag

nums

 

 

 

 

[1,1,0]

[1,2,0]

1

[1,1,0]

[1,3,0]

 

 

 

 

   回溯到第层,接着执行**(flag[2] = 0,在执行i=3)

③ DFS(2)  i = 1  i = 2  i = 3

回溯结果(结束)

 

 

 

 

flag

nums

step

flag

nums

 

 

 

 

[1,1,1]

[1,2,3]

2

[1,1,0]

[1,2,0]

 

 

 

 

               回溯到第层,接着执行**

④ DFS(3) (for输出)(结束)

 

 

 

 

 

 

flag

nums

step

 

 

 

 

 

 

[1,1,1]

[1,2,3]

3

 

 

⑦DFS(2)  i = 1  i = 2 

 

 

 

 

 

 

flag

nums

step

 

 

 

 

 

 

[1,1,1]

[1,3,2]

2

 

                                               回溯到第层,接着执行**(flag[1] = 0)

 

 

 

 

 

⑧DFS(3) (for输出)(结束)

 

 

 

 

 

 

flag

nums

step

 

 

 

 

 

 

[1,1,1]

[1,2,3]

3

 

Note:为了说明原理,这里列举了前几次递归调用DFS过程。

具体实现:

#include<stdio.h> 

#define MAX_SIZE10 

int n;//全排列的数字为[1,n]之间的所有整数

int nums[MAX_SIZE];//排列顺序 

int flag[MAX_SIZE];//若某数排列过,标志位为1

int count; 

void DFS(int step)//深度优先搜索,初始状态step = 0

    if(step == n)//若step == n 说明本轮排列结束,出现一种新的排列方式

    { 

        count++; 

        for(int i = 0;i < n;i++) 

            printf(“%d “,nums[i]); 

        printf(“\n”); 

        return; 

    } 

    for(int i = 1;i <= n;i++) 

    { 

        if(flag[i-1] == 0)//还存在没有排列的数字 

        { 

            flag[i-1] = 1;           

                     nums[step] = i;//正在排列的数值 

            DFS(step+1);//排下一个数 

            flag[i-1] = 0;//**回溯到前一步或上一层dfs递归执行时的位置 

        } 

    } 

    return; 

int main() 

         freopen(“printf_log.txt”,“a+”,stdout);

     printf(“请输入n的值:\n”); 

     scanf(“%d”,&n); 

        DFS(0); 

        printf(“总排列组合数为%d \n”,count);       

            return 0; 

 

实践应用【LeetCode 39 Combination Sum】:

给定一个一些数组成的候选集合C,和一个目标数T。现从C中选出一些数,求所有不同的取数方案使其累加和恰好等于T。

C中的每个数都可以取若干次。

注意:

l  所有的数(包括目标数)都为正整数

l  组合中的元素(a1,a2, … , ak) 不能为降序排列。(ie, a1 ≤ a2 ≤… ≤ ak)

l  不能有重复的组合方案

例如:候选集合2,3,6,7 和目标值7”’

【python实现】(搜索回溯的原理相同)

class Solution(object):

   def combinationSum(self, candidates, target):

       “””

       :type candidates: List[int]

       :type target: int

       :rtype: List[List[int]]

       “””

       Solution.res = []#全局

       candidates.sort()

       self.dfs(candidates, target, 0, [])

       return Solution.res

   def dfs(self, candidates, target, start, reslist):     

       if target == 0:

           return Solution.res.append(reslist)

       for i in range(start, len(candidates)):

           if target < candidates[i]:

                return  #回溯,回到上层调用dfs那层         

           else:

                self.dfs(candidates, target -candidates[i], i, reslist+[candidates[i]])

                 

if __name__ == ‘__main__’ :

    s= Solution()

   candidates = [2,3,6,7]

   target = 7

   s.combinationSum(candidates, target) 

    原文作者:回溯法
    原文地址: https://blog.csdn.net/yangjingjing9/article/details/76153158
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞