LeetCode 47 全排列II
47.全排列II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
双hash+回溯递归
本题在46.全排列问题的基础之上发生了改变,主要的不同之处在于:给定的序列中包含重复的元素,那么在全排列的过程中就会导致重复的结果,例如 nums=[1,1,2] 完全按照排列问题进行处理将会出现重复结果: [1,2,1] 和 [1,1,2] ,那么在于全排列问题的基础上此处的重点就在于处理重复的结果。
那么基本思路还是:递归实现,每一层选取一个元素作为排列中的第 cur 个元素,当 cur=nums.size() 选取结束,将排列加入到结果集中。
此处采取双hash的方式进行处理,创建两个hash表:
used[8] 是一个全局变量,记录的是整个序列中那些位置的的元素已经使用了 used[i]=true 代表 nums[i] 处的元素已经在前层使用了。那么在某层选取 nums[i] 后需要对 used[i] 进行标记,在递归返回后需要进行回溯,取消标记( nums 的最大长度为8,定长数组即可)
curUsed[21] 是递归函数中的局部变量,记录的是在当前层中那些元素值已经使用过了, curUed[i]=true 表示在当前层元素值 i 已经使用过了,假设当前的递归层数为第 j 层,那么表示在排列的第 j 个位置已经使用过元素 i ,那么在当前层的后续选择中遇到元素 i 就需要进行跳过
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public: vector<vector<int>> res; vector<int> tmp; bool used[8]={false}; void DFS(vector<int>& nums,int cur){ if(cur==nums.size()){ res.push_back(tmp); } int curUsed[21]={false}; for(int i=0;i<nums.size();i++){ if(!used[i]&&!curUsed[nums[i]+10]){ used[i]=true; tmp.push_back(nums[i]); curUsed[nums[i]+10]=true; DFS(nums,cur+1); tmp.pop_back(); used[i]=false; } } } vector<vector<int>> permuteUnique(vector<int>& nums) { DFS(nums,0); return res; } };
|
单hash削减空间复杂度
将数组 nums 排序,使用单个hash进行记录。使用一个hash vis 记录 nums 数组中的第i个元素是否被使用。在第i层进行元素的选取时,若是当前元素和前一个元素相等,并且上一个元素在当前层没有被选择,那么就不选择该元素。也就是对于多个连续的重复元素,在第i层中要对齐进行选取,仅仅选取一次,那么这一次就是选取多个连续重复元素中的第一个元素。当然对于 vis[i]==true 的情形,肯定是不能进行选取了。
1 2 3
| if (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1]) { continue; }
|
整体代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public: vector<vector<int>> res; vector<int> tmp; bool used[8]={false}; void DFS(vector<int>& nums,int cur){ if(cur==nums.size()){ res.push_back(tmp); } for(int i=0;i<nums.size();i++){ if(used[i]||(i>0&&nums[i]==nums[i-1]&&!used[i-1])){ continue; } used[i]=true; tmp.push_back(nums[i]); DFS(nums,cur+1); tmp.pop_back(); used[i]=false; } } vector<vector<int>> permuteUnique(vector<int>& nums) { sort(nums.begin(),nums.end()); DFS(nums,0); return res; } };
|