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

491.递增子序列

参考文章:代码随想录 

参考视频:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列_哔哩哔哩_bilibili 

解题思路:

用[4, 7, 6, 7]这个数组来举例,抽象为树形结构如图:

确定递归函数参数,参数为数组nums,for循环起始位置startIndex。

确定终止条件,当路径数组path的大小大于1时,将path添加到结果集中。

确定单层递归逻辑,由于结果中的子集要求是递增的,因此在for循环遍历中需要判断当前遍历的元素是大于或等于path中最后一个元素的。同时,因为在树枝中不能取同一元素,因此需要添加一个标记数组used用来记录已经用过的元素,当出现上述两种情况时则跳到下一层循环。

public class Leetcode491 {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> findSubsequences(int[] nums) {
        track(nums,0);
        return res;
    }

    public void track(int[] nums, int startIndex) {
        if (path.size() > 1) {
            res.add(new ArrayList<>(path));
        }
        Set<Integer> used = new HashSet<>();
        for (int i = startIndex; i < nums.length; i++) {
            if (!path.isEmpty() && nums[i] < path.get(path.size() - 1) || used.contains(nums[i])) {
                continue;
            }
            used.add(nums[i]);
            path.add(nums[i]);
            track(nums,i+1);
            path.remove(path.size()-1);
        }

    }
}

46.全排列

参考文章:代码随想录 

参考视频:组合与排列的区别,回溯算法求解的时候,有何不同?| LeetCode:46.全排列_哔哩哔哩_bilibili 

解题思路:

本题由于需要收集一个数组的全排列,因此在递归过程中需要用标记数组uesd记录已经使用过的元素。

以[1,2,3]为例,抽象成树形结构如下:

确定递归函数参数,传进来的数组nums以及标记数组used。

确定终止条件,当路径数组path的大小等于数组长度时,添加该路径到结果集。

确定单层递归逻辑,当used[ i ] 为true时,跳到下一循环,若没有用过,添加该元素到path中,将used[ i ] 改为true,进入到下一层递归,注意used和路径数组path的回溯。

public class Leetcode46 {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> permute(int[] nums) {
        boolean[] used = new boolean[nums.length];
        track(nums,used);
        return res;
    }

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

        for (int i = 0; i < nums.length; i++) {
            if (used[i]) {
                continue;
            }
            used[i] = true;
            path.add(nums[i]);
            track(nums, used);
            path.remove(path.size() - 1);
            used[i] = false;
        }
    }
}

 47.全排列 II

参考文章:代码随想录 

解题思路:

本题和上题的区别是传入的数组会出现重复的数字,因此在同一数层的情况下要注意去重。

我以示例中的 [1,1,2]为例 (为了方便举例,已经排序)抽象为一棵树,去重过程如图:

图中我们对同一树层,前一位(也就是nums[i-1])如果使用过,那么就进行去重。

确定递归函数参数,传入数组nums以及标记数组used。

确定终止条件,当路径数组path大小等于数组nums的长度时,将路径path添加到结果集中。

确定单层递归逻辑,当出现两种情况需要跳过该循环,i > 0 && nums[i] == nums[i - 1] && !used[i - 1],或者used[i] == true时。第一种是数层的去重,第二种是树枝递归中不能使用相同的元素。

public class Leetcode47 {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        boolean[] used = new boolean[nums.length];
        track(nums, used);
        return res;
    }

    public void track(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]||used[i]) {
                continue;
            }
            used[i] = true;
            path.add(nums[i]);
            track(nums, used);
            used[i] = false;
            path.remove(path.size() - 1);
        }
    }
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值