代码随想录训练营day29|回溯:491.递增子序列、46.全排列、47.全排列2

LeetCode491递增子序列

去重+排序
思路:对于终止条件:可以使用temp.size()>=2来判断。但是这里只需要ans.add 即可,不可以return。return会吧大于2的都丢弃。

在for里面的横向遍历的时候,还要判断去重和递增。
至于去重 的时候使用了辅助的map或者数组来做。是因为这里的nums不是递增的,也不可以变成递增的。只能用map来记录是否使用过。
所以使用map是标准的用法。

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    LinkedList<Integer> temp = new LinkedList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backtracking(nums, 0);
        return ans;
    }
    public void backtracking(int[] nums, int index){
        if(temp.size() >1){
            ans.add(new ArrayList(temp));
            //return; 这里不能加return 因为这里return的话只会把=2的结果返回
        }
        // 区别在于这里需要一个变量来当前元素是否被使用过
        // 使用过就不会放在第一个位置了
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int i=index;i<nums.length;i++){
            if(!temp.isEmpty() && nums[i] < temp.getLast()){ 
                continue;
            }
            if(map.getOrDefault(nums[i], 0) == 1){ // 如果当前元素用过了 后面就不会再用
                // 之所以使用map是因为这里不是递增的数组,没法用nums[i] == nums[i-1]来判断了
                // getOrDefault:如果有着key就返回对应的值,没有就返回0
                // getOrDefault是为了保证不出错
                continue;
            }
            // map.put(nums[i],map.getOrDefault( nums[i],0 )+1);// getOrDefault是为了保证不出错
            map.put(nums[i], 1); // 
            temp.add(nums[i]);
            backtracking(nums, i+1);
            temp.removeLast();
        }
    }
}

LeetCode46全排列

全排列的几个关键点:

  • 用一个used数组来标记那些数组已经使用过
  • 不在用index来作为for的开始,丢弃一些元素了,而是每次从0开始。
class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    LinkedList<Integer> temp = new LinkedList<>();
    boolean[] used; // 注意不要写成BooLean
    public List<List<Integer>> permute(int[] nums) {
        // 排列问题
        
        used = new boolean[nums.length];
        backtracking(nums);
        return ans;
    }
    public void backtracking(int[] nums){
        // 终止条件
        // 因为只返回叶子节点所以用长度来做一个判断
        // 同时要加上return
        if(nums.length == temp.size()){
            ans.add(new ArrayList(temp));
            return;
        }
        for(int i=0;i<nums.length;i++){ // 这里每次各分支都要从头开始的
            // 用used表示那些元素使用过了。
            // 这里找的是排列且从头开始 一定会遇到重复的情况。首先1,选过了再递归里面还是从1开始就跳过1,去2,3里面找,找的时候也是会从1开始,遇到用过的就跳过。
            // 例如3,for i到了3 此时结果只有[3, , ]。进入递归,还是从头开始找,这时1没有用就会被加到里面变成[3, 1, ] 接着加2 ,在回溯找到[3, 2, 1]
            if(used[i]){
                continue;
            }
            used[i] = true;
            temp.add(nums[i]);
            backtracking(nums);
            used[i] = false;
            temp.removeLast();
        }
    }
}

LeetCode47全排列2

used[i-1]很关键。
对于排列问题的剪枝去重,去除重复的排列。
在上一题的基础上需要对横向层级上的重复元素去重,这里使用了
if(i>0 && nums[i] == nums[i-1] && used[i-1] == false)。其中比较不好理解的是used[i-1] == false.
在这里插入图片描述
集合used数组就可以理解了。

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    LinkedList<Integer> temp = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> permuteUnique(int[] nums) {
        used = new boolean[nums.length];
        Arrays.sort(nums);
        backtracking(nums);
        return ans;

    }
    public void backtracking(int[] nums){
        if(nums.length == temp.size()){
            ans.add(new ArrayList(temp));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(used[i]){
                continue;
            }
            if(i>0 && nums[i] == nums[i-1] && used[i-1] == false){ // 这里的used[i-1] == false或者==true都可以。但是不是说这句话可以不写了。
            // 其实,这里是相中剪枝去重的逻辑,used[i-1] == false 结合nums[i] == nums[i-1] 表示这两个数相等,但是前一个被置为false,就说明这是在一层上考虑问题。因为在同一层 除之前使用过的其他的元素一定没使用过,二者其他的元素就是看作在同层。同层内只能取一个数,因此同层内前一个为false 且两个数相等就已经重复了,
            // 例如nums=[1,1,1], used[1,1,0] 和[1,0,1] 
            // [1,1,0] 已经记录过一次了,先有个[1,0,1] 就不要记录了,因为重复
                continue;
            }
            used[i] = true;
            temp.add(nums[i]);
            backtracking(nums);
            used[i] = false;
            temp.removeLast();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值