leetcode 47. Permutations II-排列|回溯算法

原题链接:47. Permutations II

【思路1-Java】
这题是leetcode 46. Permutations-全排列|回溯|递归|非递归思路1延伸,每次交换 nums 中的两个元素,新生成一种新的组合,将该组合放入中间集,再放入结果集中,对于重复元素的处理,这里采用 set 集合进行去重,思路虽然好理解,可是也费了不少空间:

public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        if (nums==null || nums.length==0) { return ans; }
        permute(ans, nums, 0);
        return ans;
    }

    private void permute(List<List<Integer>> ans, int[] nums, int index) {
        if (index == nums.length) { 
            List<Integer> temp = new ArrayList<>();
            for (int num: nums) { temp.add(num); }
            ans.add(temp);
            return;
        }
        Set<Integer> appeared = new HashSet<>();
        for (int i=index; i<nums.length; ++i) {
            if (appeared.add(nums[i])) {
                swap(nums, index, i);
                permute(ans, nums, index+1);
                swap(nums, index, i);
            }
        }
    }

    private void swap(int[] nums, int i, int j) {
        int save = nums[i];
        nums[i] = nums[j];
        nums[j] = save;
    }
}
30 / 30  test cases passed. Runtime: 6 ms  Your runtime beats 44.44% of javasubmissions.


【思路2-Java】

本思路与leetcode 46. Permutations-全排列|回溯|递归|非递归思路2比较接近,都是逐一将元素添加到中间集中,然后将中间集添加到结果集中。由于数据有重复,如果不加以处理的话,[1,1,2]这样的实例就会出现重复的结果,那么我们应该如何避免重复呢?方法就是对与重复的元素循环时跳过递归的调用只对第一个未被使用的进行递归,那么这一次的结果将会唯一出现在结果集中,而后重复的元素将会被略过。如果第一个重复元素还没在当前结果中,那么我们就不需要进行递归。想明白了这点,其实代码很好修改,首先我们对nums 数组进行排序,让重复元素相邻,接下来就是一行代码对于重复元素和前面元素的使用情况进行判断:

public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        dfs(res, new ArrayList<Integer>(), nums, new boolean[nums.length]);
        return res;
    }
    private void dfs(List<List<Integer>> res, List<Integer> temp, int[] nums, boolean[] used) {
        if(temp.size() == nums.length) {
            res.add(new ArrayList<>(temp));
            return;
        }
        /*
            上面这一连串判断条件,重点在于要能理解!used(i-1)
            要理解这个,首先要明白i作为数组内序号,i是唯一的
            给出一个排好序的数组,[1,2,2]
            第一层递归            第二层递归            第三层递归
            [1]                    [1,2]                [1,2,2]
            序号:[0]                 [0,1]            [0,1,2]
            这种都是OK的,但当第二层递归i扫到的是第二个"2",情况就不一样了
            [1]                    [1,2]                [1,2,2]            
            序号:[0]                [0,2]                [0,2,1]
            所以这边判断的时候!used(0)就变成了true,不会再继续递归下去,跳出循环
            步主要就是为了去除连续重复存在的,很神奇反正 = =||
        */
        for(int i = 0; i < nums.length; i++) {
            if(used[i] || i > 0 && !used[i-1] && nums[i] == nums[i-1]) continue;
            used[i] = true;
            temp.add(nums[i]);
            dfs(res, temp, nums, used);
            temp.remove(temp.size() - 1);
            used[i] = false;
        }
    }
}

30 / 30 test cases passed. Runtime: 4 ms  Your runtime beats 76.09% of javasubmissions.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值