回溯问题详解

回溯问题

一、基本概念

回溯算法实际上是一个枚举的过程,在搜索尝试过程中寻找问题的解,发现不满足求解回溯返回。

基本思想:

在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。

1、若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
2、而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

步骤

(1)针对所给问题,确定问题的解空间:
首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则。
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

public void backtrack(index){
    if(回溯到边界){
        输出结果; 
        return;
    }
    
    for(i=下界;i<=上界;i++){
        if(i满足限界条件和约束条件){
            保存i到临时变量中;      //代表尝试一次
            backtrack(index+1);   //深度遍历,向下遍历一层
            清理变量等;      //回溯法的关键,相当于本节点清除,然后重新回溯
        }
    }
}

二、常见问题

  • 求全排列 (46,47)
  • 求组合 (77)
  • 求二叉树和为定值的某一路径
  • 求数组中和为定值的子数组(39,40,216)
  • 子数组问题(78,90)
  • 八皇后问题
  • 数独问题

三、LeetCode题解

以下是LeetCode中的部分问题。

LeetCode 39. Combination Sum

原题目连接
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

例子:
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

典型的回溯题,安装上面的通用算法,

public List<List<Integer>> combinationSum(int[] candidates, int target) {
    //此处res可以用参数传入回溯函数,也可以用类的属性方法实现
    
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    backtrack(0,0,target,candidates,result, new ArrayList<Integer>());
    return result;
}
  
//回溯函数的定义是回溯问题的关键,
//保存回溯的当前位置,当前计算结果,目标结果很重要
public void backtrack(int start,int sum,int target,int[] candidates,List<List<Integer>> res,List<Integer> ans){
    //回溯到边界,满足条件,直接输出结果到res
    if(target == sum){
        res.add(new ArrayList<Integer>(ans));
        return;
    }
    
    for(int i=start;i<candidates.length;i++){
        ans.add(candidates[i]);         //进行一次尝试
        backtrack(i,sum+candidates[i],target,candidates,res,ans);  //深度访问下一层
        ans.remove(ans.size()-1);   //清理本次的尝试,然后重新回溯
    }
}

LeetCode 40. Combination Sum II

原题目连接
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

例子:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    //为了保证每个元素只使用一次,先对数组排序
    Arrays.sort(candidates);
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    backtrack(0,target,candidates,result, new ArrayList<Integer>());
    return result;
}

public void backtrack(int start,int target,int[] candidates,List<List<Integer>> res,List<Integer> ans){
    //回溯到边届,直接输出结果
    if(0 == target){
        res.add(new ArrayList<Integer>(ans));
        return;
    }
    
    //target>=candidates[i]相当于剪支法,不满足的条件可以不用继续回溯
    for(int i=start;i<candidates.length && target>=candidates[i];i++){
        //对于
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值