代码随想录算法训练营第二十九天|491. 非递减子序列、46. 全排列、47. 全排列 II。

491. 非递减子序列

题目链接:非递减子序列

题目描述
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

解题思路
本题和子集乍一看差不多,但是还是有区别,本题要求得是自增排序,所以不能对原数组进行排序,所以不能用之前的去重逻辑,因此需要使用一个used数组来标记是否在本层使用过,因为当然使用map也是可以的,但是因为nums的数值大小确定,所以使用数组效率更高。

代码实现

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    
    public List<List<Integer>> findSubsequences(int[] nums) {
        backtrack(nums, 0);
        return res;
    }

    public void backtrack(int[] nums, int begin) {
        if (path.size() >= 2) {
            res.add(new ArrayList<>(path));
        }
        int[] used = new int[201]; // 记录当前层是否使用过 递归后重新定义
        for (int i = begin; i < nums.length; i++) {
            if (!path.isEmpty() && nums[i] < path.get(path.size() - 1)) {
                continue;
            }
            if (used[nums[i] + 100] == 1) {
                continue;
            }
            
            used[nums[i] + 100] = 1;
            path.add(nums[i]);
            backtrack(nums, i + 1);
            path.removeLast();
        }
    }
}

46. 全排列

题目链接:全排列

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

解题思路
本题比较简单,只需要进行暴力递归即可,根据回溯三部曲,确定好终止条件是path长度与nums一致,而每层都要遍历所有的元素,然后当不包含时加入path。

代码实现

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();

    public List<List<Integer>> permute(int[] nums) {
        backtrack(nums);
        return res;
    }

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

47. 全排列 II

题目链接:全排列 II

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

解题思路
本题和上一题的区别在于要去重,因为给定的数组含有重复数字,所以不能使用contains去重,而且本题在树枝和树层都要进行去重,所以可以使用两个数组来进行记录是否使用过,一个全局数组确定同树枝是否使用过(因为递归也就是树枝递归不会清除这个数组,要注意记住回溯这个数组),一个用局部数组进行同层去重。

代码实现

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    int[] used = new int[8];

    public List<List<Integer>> permuteUnique(int[] nums) {
        backtrack(nums);
        return res;
    }

    public void backtrack(int[] nums) {
        if (path.size() == nums.length) {
            res.add(new ArrayList<>(path));
            return;
        }
        int[] used2 = new int[21];
        for (int i = 0; i < nums.length; i++) {
            if (used[i] == 1) { //判断同树枝上去重不使用使用过的元素
                continue;
            }
            if(used2[nums[i]+10]==1){//判断同层是否使用过,去重。
                continue;
            }
            used2[nums[i]+10]=1;
            used[i] = 1;
            path.add(nums[i]);
            backtrack(nums);
            used[i] = 0;
            path.removeLast();
        }
    }
}

你可能感兴趣的:(算法)