当前位置:   article > 正文

【leetcode 47. 全排列 II 解析】

47. 全排列 ii

官方题解太难理解了,看看怎么修改下更方便理解呢。

先看一下原题:

47. 全排列 II

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

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

提示:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

官方题解:

  1. class Solution {
  2. boolean[] vis;
  3. public List<List<Integer>> permuteUnique(int[] nums) {
  4. // 结果集
  5. List<List<Integer>> ans = new ArrayList<List<Integer>>();
  6. // 处理过程中用到的子集容器
  7. List<Integer> perm = new ArrayList<Integer>();
  8. // 存放数的位置是否被访问
  9. vis = new boolean[nums.length];
  10. // 排序主要的目的是把一样的数放到一起
  11. Arrays.sort(nums);
  12. // 调用递归收集每一项子集
  13. backtrack(nums, ans, 0, perm);
  14. return ans;
  15. }
  16. // 递归函数
  17. public void backtrack(int[] nums, List<List<Integer>> ans, int idx, List<Integer> perm) {
  18. // 递归最重要的终止条件(写错都没事,但不能停不下来)
  19. if (idx == nums.length) {
  20. ans.add(new ArrayList<Integer>(perm));
  21. return;
  22. }
  23. // 开始套娃表演了...
  24. for (int i = 0; i < nums.length; ++i) {
  25. // 这边是全排列去重的关键,前面数都排好序了,只要判断同一个数在当前位置被访问过后面的数直接跳过;关键是 !vis[i - 1] 怎么理解啊??
  26. if (vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])) {
  27. continue;
  28. }
  29. perm.add(nums[i]);
  30. vis[i] = true;
  31. backtrack(nums, ans, idx + 1, perm);
  32. vis[i] = false;
  33. perm.remove(idx);
  34. }
  35. }
  36. }
  37. 作者:LeetCode-Solution
  38. 链接:https://leetcode-cn.com/problems/permutations-ii/solution/quan-pai-lie-ii-by-leetcode-solution/
  39. 来源:力扣(LeetCode)
  40. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

递归方法中 !vis[i - 1] 这行代码怎么理解?反正从逻辑角度挺难懂,但我们用缺失部分逻辑的方法反证他的意义就会清楚些。

改成这样: if (vis[i] || (i > 0 && nums[i] == nums[i - 1] )) continue;

用这样例测试:System.out.println(new Solution().permuteUnique(new int[]{1, 1, 2, 2}));

结果输出为空,因为位置1上选1,位置2上跳过第一个数判断第二个数也是1且和第一个数一样所以又跳过了,后面的2提前访问放在位置2上,导致后面两位置上一个数也选不了。

所以这个逻辑可以这样理解:当前数与前一数一样时,如果前一数在上一位置访问过了,那当前数也要选择一次(属于第一次),后面再有一样的数就可以跳过了。

下面是更容易理解的解法,也可以参考一下:

  1. static class Solution02 {
  2. boolean[] visit;
  3. int[] nums;
  4. int size;
  5. List<List<Integer>> ans = new ArrayList<>();
  6. LinkedList<Integer> output = new LinkedList<>();
  7. /**
  8. * leetcode 47. 全排列
  9. *
  10. * @param nums
  11. * @return
  12. */
  13. public List<List<Integer>> permuteUnique(int[] nums) {
  14. Arrays.sort(nums);
  15. this.size = nums.length;
  16. visit = new boolean[size];
  17. this.nums = nums;
  18. recur(0);
  19. return ans;
  20. }
  21. public void recur(int p) {
  22. if (p >= size) {
  23. ans.add(new ArrayList<>(output));
  24. return;
  25. }
  26. Integer last = null; // 存放当前位置,上一次访问过的数
  27. for (int i = 0; i < size; i++) {
  28. // 当前位置 同一个数只能访问一次(这边是全排列去重的关键)
  29. if (visit[i] || (i > 0 && last != null && nums[i] == last)) continue;
  30. last = nums[i];
  31. output.add(last);
  32. visit[i] = true;
  33. recur(p + 1);
  34. visit[i] = false;
  35. output.remove(p);
  36. }
  37. }
  38. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/916245
推荐阅读
相关标签
  

闽ICP备14008679号