代码随想录算法训练营day32 | 491.递增子序列 46.全排列 47.全排列 II

139 阅读2分钟

491.递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。 提示:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100

思路

  • 使用path记录当前构建的递增子序列的元素
  • 使用map来记录当前层某个元素是否已使用过了,因为同一层相同元素递归遍历产生相同的结果,因此同一层中,一个数字只能继续递归遍历一次。

代码

class Solution {
    public List<List<Integer>> findSubsequences(int[] nums) {

        List<Integer> path = new ArrayList<>();
        List<List<Integer>> res = new ArrayList<>();

        if(nums.length < 2){
            return res;
        }

        

        backTracking(nums, 0, path, res);

        return res;

    }

    private void backTracking(int[] nums, int startIndex, List<Integer> path, List<List<Integer>> res){

        if(startIndex == nums.length){
            return;
        }

        boolean[] map = new boolean[201];

        for(int i = startIndex; i < nums.length;i++){
            // 当前元素与path不形成递增
            if(path.size() > 0 && path.get(path.size() - 1) > nums[i]){
                continue;
            }

            // map记录本层是否某个数字是否有使用过
            // 去重
            if(map[nums[i] + 100]){
                continue;
            }

            path.add(nums[i]);
            map[nums[i] + 100] = true;

            if(path.size() >= 2){
                res.add(List.copyOf(path));
            }

            backTracking(nums, i + 1, path, res);

            path.remove(path.size() - 1);
        }
    }
}

46.全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

46. 全排列 - 力扣(Leetcode)

思路

  • 本题需要对数组元素的任意排列方式进行枚举,使用回溯法
  • 枚举时需要判断当前数组中哪些元素已经被使用了,创建与数组相同长度的boolean类型数组isUsed。对于下标i,使用isUsed[i]标识nums[i]是否已被使用。

代码

class Solution {
    public List<List<Integer>> permute(int[] nums) {

        boolean[] isUsed = new boolean[nums.length];

        List<Integer> path = new ArrayList<>();
        List<List<Integer>> res = new ArrayList<>();

        backTracking(nums, isUsed, path, res);

        return res;

    }

    private void backTracking(int[] nums, boolean[] isUsed, List<Integer> path, List<List<Integer>> res){

        if(path.size() == nums.length){
            res.add(List.copyOf(path));
            return;
        }

        for(int i = 0; i < nums.length; i++){
            if(isUsed[i]){
                continue;
            }

            isUsed[i] = true;

            path.add(nums[i]);

            backTracking(nums, isUsed, path, res);

            path.remove(path.size() - 1);

            isUsed[i] = false;
        }

    }
}

47.全排列 II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

提示:

  • 1 <= nums.length <= 8
  • 10 <= nums[i] <= 10

47. 全排列 II - 力扣(Leetcode)

思路

46.全排列 相比,本题的区别是数组的元素是可重复的,因此多了一个去重的要求。

对于可重复元素的数组去重问题,可以参考40.组合总和II的方法,即在回溯树的每一层,设置一个map记录本层已取过的值,当再次遇到这个值时,跳过本轮操作,从而避免出现重复的结果。

代码

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {

        boolean[] isUsed = new boolean[nums.length];
        List<Integer> path = new ArrayList<>();
        List<List<Integer>> res = new ArrayList<>();

        backTracking(nums, isUsed, path, res);

        return res;
    }

    private void backTracking(int[] nums, boolean[] isUsed, List<Integer> path, List<List<Integer>> res){

        if(path.size() == nums.length){
            res.add(List.copyOf(path));
            return;
        }

        boolean[] map = new boolean[21];

        for(int i = 0; i < nums.length; i++){
            if(isUsed[i] || map[nums[i] + 10]){
                continue;
            }

            map[nums[i] + 10] = true;
            isUsed[i] = true;

            path.add(nums[i]);

            backTracking(nums, isUsed, path, res);

            path.remove(path.size() - 1);

            isUsed[i] = false;
            
        }
    }
}