lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1. 题目

 

 

 

读题

https://leetcode.cn/problems/permutations-ii/submissions/428949841/

 

考查点

这道题的考查点主要有以下几个:

- **回溯法**:回溯法是一种搜索所有可能解的方法,它的基本思想是从一个初始状态开始,每次尝试一个可能的选择,然后递归地搜索剩余的选择空间,如果发现当前的选择不能达到目标或者已经搜索完所有的选择,就回退到上一步,撤销当前的选择,继续尝试其他的选择,直到找到所有的解或者没有解为止。¹²
- **去重**:由于题目给定的序列可能包含重复的数字,所以在搜索全排列时,需要避免生成重复的结果。一种常用的方法是先对数组进行排序,然后在遍历数组时判断当前元素是否和前一个元素相同,如果相同且前一个元素没有被使用过,就跳过当前元素。这样可以保证相同的元素只会出现在同一位置一次。²³
- **编程技巧**:在实现回溯法时,需要注意一些编程技巧,比如如何定义和使用辅助变量(例如结果列表、布尔数组、当前序列等),如何递归地调用辅助函数,如何在递归返回后恢复原来的状态(例如移除当前元素、标记未使用等),以及如何优化代码的效率和可读性。²⁴

 

2. 解法

思路

回溯法是一种搜索所有可能解的方法,它的基本思想是从一个初始状态开始,每次尝试一个可能的选择,然后递归地搜索剩余的选择空间,如果发现当前的选择不能达到目标或者已经搜索完所有的选择,就回退到上一步,撤销当前的选择,继续尝试其他的选择,直到找到所有的解或者没有解为止。

对于这道题,我们可以把全排列看作是一个长度为n的序列,其中每个位置可以放置数组中的一个元素。我们从第一个位置开始,遍历数组中的每个元素,如果该元素没有被使用过,就把它放在第一个位置,并标记为已使用,然后递归地搜索剩余的n-1个位置。当搜索完所有的位置后,就得到了一个全排列,把它加入结果列表。然后回退到上一步,把当前元素从第一个位置移除,并标记为未使用,继续尝试其他的元素。为了避免重复的全排列,我们需要在遍历数组时判断当前元素是否和前一个元素相同,如果相同且前一个元素没有被使用过,就跳过当前元素。这样可以保证相同的元素只会出现在同一位置一次。

 

步骤来条理一下回溯法的思路:

  1. 定义一个结果列表,用来存放所有的全排列。
  2. 定义一个布尔数组,用来记录每个元素是否被使用过。
  3. 定义一个辅助函数,用来递归地搜索所有的全排列。
  4. 在辅助函数中,判断当前的序列是否已经达到了数组的长度,如果是,就把它加入结果列表,返回。
  5. 如果不是,就遍历数组中的每个元素,如果该元素没有被使用过,就把它加入当前的序列,并标记为已使用,然后递归地搜索剩余的位置。
  6. 在递归返回后,把当前元素从序列中移除,并标记为未使用,继续尝试其他的元素。
  7. 为了避免重复的全排列,我们需要在遍历数组时判断当前元素是否和前一个元素相同,如果相同且前一个元素没有被使用过,就跳过当前元素。
  8. 最后返回结果列表。

具体实现

class Solution {
    List<List<Integer>> list; // 存放结果
    boolean[] used; // 记录元素是否被使用过

    public List<List<Integer>> permuteUnique(int[] nums) {
        list = new ArrayList<>(); // 初始化结果列表
        used = new boolean[nums.length]; // 初始化布尔数组
        Arrays.sort(nums); // 对数组进行排序
        backtrack(new ArrayList<>(), nums); // 回溯
        return list; // 返回结果
    }

    private void backtrack(List<Integer> current, int[] nums) {
        if (current.size() == nums.length) { // 如果当前列表长度等于数组长度,说明找到了一个全排列
            list.add(new ArrayList<>(current)); // 把当前列表加入结果列表
        } else { // 否则,继续搜索
            for (int i = 0; i < nums.length; i++) { // 遍历数组中的每个元素
                if (used[i] || i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) { // 如果元素已经被使用过,或者和前一个元素相同且前一个元素没有被使用过,跳过
                    continue;
                }
                used[i] = true; // 标记元素被使用过
                current.add(nums[i]); // 把元素加入当前列表
                backtrack(current, nums); // 回溯
                current.remove(current.size() - 1); // 把元素从当前列表移除
                used[i] = false; // 标记元素未被使用过
            }
        }
    }
}

  

3. 总结

posted on 2023-04-29 23:17  白露~  阅读(5)  评论(0编辑  收藏  举报