LeetCode Day29 491&46&47

66 阅读1分钟
491. 递增子序列
  1. 递归出入参:求子序列,明显下一次不能选择重复的数,需要startIndex,调整下一层递归的起始位置。
  2. 终止条件:题目要求递增子序列大小至少为2,所以只要存储的路径path内大小>1即可
  3. 单层搜索逻辑:因为题目要求是递增的子序列,那从树形结构图我们可以了解到同层的节点使用过以后就不能再用了。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backTrace(nums, 0);
        return res;
    }
    
    public void backTrace(int[] nums, int start){
        if(path.size() > 1){
            res.add(new ArrayList(path));
        }

        int[] used = new int[201];
        for(int i = start; i < nums.length; i ++){
            if(!path.isEmpty() && nums[i] < path.get(path.size()-1) || (used[nums[i]+100]==1)) continue;
            used[nums[i]+100] = 1;
            path.add(nums[i]);
            backTrace(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}
46. 全排列
  1. 回溯出入参:这题不需要用startIndex了,因为上一个使用过的元素在下一次排序里也会被使用到。
  2. 终止条件:当收集元素的数组path的大小达到和nums数组一样大的时候,说明找到了一个全排列,也表示到达了叶子节点。
  3. 单层搜索逻辑:这道题和前面做过的最大的不同就是for循环里不用startIndex了。因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。而used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
        used = new boolean[nums.length];
        permuteHelper(nums);
        return res;
    }

    public void permuteHelper(int[] nums){
        if(path.size() == nums.length){
            res.add(new ArrayList(path));
            return;
        }
        for(int i = 0; i < nums.length; i ++){
            if(used[i]){
                continue;
            }

            used[i] = true;
            path.add(nums[i]);
            permuteHelper(nums);
            path.removeLast();
            used[i] = false;
        }
    }
}
47. 全排列 II

这题和刚刚46的区别是要返回所有不重复的全排列。那么肯定也要用到used数组去判断是否使用过,同时说明需要考虑到去重。翻到2022年我刷题时做的备注,当时不理解去重之前为什么需要排序,现在可以理解了,去重之前排序的目的是方便通过相邻的节点来判断是否重复使用了

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> permuteUnique(int[] nums) {
        used = new boolean[nums.length];
        Arrays.fill(used, false);
        Arrays.sort(nums);
        backTrace(nums, used);
        return res;
    }

    public void backTrace(int[] nums, boolean[] used){
        if(path.size() == nums.length){
            res.add(new ArrayList(path));
            return;
        }

        for(int i = 0; i < nums.length; i ++){
            if(i > 0 && nums[i] == nums[i - 1] && used[i - 1]){
                continue;
            }

            if(used[i] == false){
                path.add(nums[i]);
                used[i] = true;
                backTrace(nums, used);
                used[i] = false;
                path.removeLast();
            }
        }
    }
}