47. Permutations II

关联问题:排列1:46. Permutations, 排列2:47. Permutations II,组合:77. Combinations 

问题:给定(存在重复元素)的数组,进行全排列,求出所有排列的可能性。

Example 1:
Input: nums = [1,1,2]
Output:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

Example 2:
Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
 
Constraints:
1 <= nums.length <= 8
-10 <= nums[i] <= 10

  

解法:Backtracking(回溯算法)

两个变量:

  • 路径path:已经选择好的前几位结果
  • 选择列表:当前位置上元素的选择可能性:nums-visited
    • visited标记排除已经被选择过的元素。
    • nums所有元素。

处理过程:

base:递归退出条件:选择到最后一位结束,这里为已经选择好路径长度==给出的数组长度。

做选择:对于当前位置,选择其中一个可用数字a。

    • 路径.add(a)
    • 选择列表.delete(a)->visited[i]=1

撤销选择:回退到选择数字a之前的状况。

    • 路径.delete(a)
    • 选择列表.add(a)->visited[i]=0

⚠️  注意:本问题中,允许存在相同的元素,但是在排列中,相同元素在同一位置上的选择,是无效的。

因此,我们需要排除相同元素在同一个位置上的选择。

  • 首先对nums进行排列,相同元素则会被连续排列。
  • 从第二个重复的元素开始,在同一个位置上的选择:
    • 若当前path中该重复的第一个元素已被使用,则当前的排序为全新排列,没有问题。
    • 若第一元素没有被使用visited[i-1]==0,则现在选择该重复元素,一定和之前的path选择中该位置使用了第一个元素的排列发生重复,需要跳过。

代码参考:

 1 class Solution {
 2 public:
 3     void traverse(vector<vector<int>>& res, vector<int> nums, vector<int>& path, vector<int>& visited) {
 4         if(path.size() == nums.size()) {
 5             res.push_back(path);
 6             return;
 7         }
 8         for(int i = 0; i < nums.size(); i++) {
 9             if(visited[i]) continue;
10             //该元素nums[i]已被使用在目前的path中,则跳过。
11             if(i!=0 && nums[i]==nums[i-1] && !visited[i-1]) continue;
12             //该元素nums[i],与还没被目前的path中使用的,排在它之前的元素重复,
13             //(说明,上一个path的构造中,在同一个位置,一定已经使用过排在它之前的元素),则跳过。
14             visited[i] = 1;
15             path.push_back(nums[i]);
16             traverse(res, nums, path, visited);
17             path.pop_back();
18             visited[i] = 0;
19         }
20         return;
21     }
22     vector<vector<int>> permuteUnique(vector<int>& nums) {
23         vector<vector<int>> res;
24         vector<int> path;
25         vector<int> visited(nums.size(), 0);
26         sort(nums.begin(), nums.end());
27         traverse(res, nums, path, visited);
28         return res;
29     }
30 };

 

另,swap方法:

参考:leetcode说明

swap不需要恢复位置。与递归调用中重复元素的排除有关。

代码参考:

 1 class Solution {
 2 public:
 3     void traverse(vector<vector<int>>& res, vector<int> nums, int pos) {
 4         if(pos == nums.size()) {
 5             res.push_back(nums);
 6             return;
 7         }
 8         for(int i = pos; i < nums.size(); i++) {
 9             if(i!=pos && nums[i]==nums[pos]) continue;
10             swap(nums[i], nums[pos]);
11             traverse(res, nums, pos+1);
12         }
13         return;
14     }
15     vector<vector<int>> permuteUnique(vector<int>& nums) {
16         vector<vector<int>> res;
17         sort(nums.begin(), nums.end());
18         traverse(res, nums, 0);
19         return res;
20     }
21 };

 

posted @ 2021-01-03 16:36  habibah_chang  阅读(50)  评论(0编辑  收藏  举报