leetcode刷题思路-----回溯

leetcode刷题思路-----回溯

对于回溯问题的主要思路在于找出回溯路径,最经典的就是排列和组合的问题。

主要模板

 private void backtrace1(int[] nums){
 		//何时取出何时的结果
        if("符合条件"){
            res.add("将这次结果加入解集");
            return;
        }
        //在可选的集合中循环来取,加入当前路径
        for(int i=0; i< nums.length; i++){
            //去重等特判,直接跳过
            if(){
                continue;
            }
            //加入当前路径
            list.add(nums[i]);
            //向下继续探索
            backtrace1(nums);
            //回退状态
            list.remove(list.size()-1);

        }
    }

1. 全排列1全排列2

/*
  1.
    给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
   输入:nums = [1,2,3]
   输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

   2.
    给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
    输入:nums = [1,1,2]
    输出:
    [[1,1,2],
    [1,2,1],
    [2,1,1]]

* */
	private void backtrace1(int[] nums){
        //只要加入所有的数字就可记录下来
        if(list.size()==nums.length){
            res.add(new ArrayList(list));
            return;
        }
        //在数组里取数字
        for(int i=0; i< nums.length; i++){
        	//已经取过的直接跳过
            if(list.contains(nums[i])){
                continue;
            }
            //加入当前数
            list.add(nums[i]);
            backtrace1(nums);
            //回退
            list.remove(list.size()-1);

        }
    }
    //对于有重复数字的排列,要对其进行剪枝
    //例如nums = {1,1*,2}
    private void backtrace2(int[] nums){
        if(list1.size()==nums.length){
        	res1.add(new ArrayList(list1));
            return;
        }
        for(int i=0; i<nums.length; i++){
            if(used[i]){
                continue;
            }
            //对于相同的数字,其路径会重复,那么对于前一个相同的数字未访问,那么这次路径就会重复
            //例如存在1,1*,当1*继续往下走时,必将出现 1*->1的路,而这个是重复的,需要剪枝
            if(i>0&&nums[i]==nums[i-1]&&!used[i-1]){
                continue;
            }
            list1.add(nums[i]);
            //记录访问过的节点
            used[i] = true;
            backtrace2(nums);
            list1.remove(list1.size()-1);
            //回退
            used[i] = false;
        }

    }

总结:
1.对于排列,不重复的数组正常做,每次for内的递归从头(i=0)开始,自己路径内去重。
2.对于存在重复的数组,需要剪枝,按照相同元素,对第二个(1*)剪枝。剪枝的依据是加入一个used数组。
3.切记对于相同数字去重,要对数组排序!

2.组合总和1组合总和2

/*
1.
给你一个 无重复元素 的整数数组candidates 和一个目标整数target,找出candidates中可以使数字和为目标数target的不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

链接:https://leetcode-cn.com/problems/combination-sum

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]

2.给定一个候选人编号的集合candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
    candidates中的每个数字在每个组合中只能使用一次。注意:解集不能包含重复的组合。

链接:https://leetcode-cn.com/problems/combination-sum-ii

* */
//1.不重复
private void backtrace_zuhe(int[] nums,int cur,int target){
        if(target==0){
            res.add(new ArrayList(list));
            return;
        }
        for(int i=cur; i<nums.length&&target>=nums[i]; i++){
            list.add(nums[i]);
            target -= nums[i];
            //区别就在于组合是没有顺序的,因此要下次遍历时不能走回头路,从i继续走
            backtrace_zuhe(nums, i, target);
            target += nums[i];
            list.remove(list.size()-1);
        }
    }
//2.重复的
private void backtrace_zuhe1(int[] nums,int cur,int target){
        if(target==0){
            res1.add(new ArrayList(list1));
            return;
        }
        for(int i=cur; i<nums.length&&target>=nums[i]; i++){
        //和排列重复的一样加入剪枝
            if(i>0&&nums[i]==nums[i-1]&&!used[i-1]){
                continue;
            }
            list1.add(nums[i]);
            target -= nums[i];
            used[i] = true;
            backtrace_zuhe1(nums, i+1, target);
            target += nums[i];
            used[i] = false;
            list1.remove(list1.size()-1);
        }
    }

总结:
1.组合内部是无序的,因此递归时注意不要走回头路。
2.同样的,重复数组记得排序。
3.可以在for中提前剪枝来提高效率。

这就时回溯的基本套路,日后可以更新更多抽象的或者具体数据结构的回溯,例如二叉树或者二维数组,dan本质都是这个思路。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值