# [47. Permutations II](https://leetcode.com/problems/permutations-ii/) # 思路 返回一个数组的所有排列,数组中元素可能有重复。(注意和46题对比,学习如何处理有重复元素的情况) || 求组合 | 求排列 | | -- | -- | -- | |无重复元素| [77. Combinations](https://leetcode.com/problems/combinations/) | [46. Permutations](https://leetcode.com/problems/permutations/) | |有重复元素| - | [47. Permutations II](https://leetcode.com/problems/permutations-ii/) | ## 思路一、Next Permutation 有了[31. Next Permutation](https://leetcode.com/problems/next-permutation/)和[46. Permutations](https://leetcode.com/problems/permutations/)的做题经验,这题就显得是一个送分题了。 这题与46题唯一区别就是此题允许重复元素,所以需要注意在元素比较的时候相等的情况。参考[46的题解思路一](https://github.com/ShusenTang/LeetCode/blob/master/solutions/46.%20Permutations.md)即可很容易写出代码。值得一提的是,**STL中现成的`next_permutation`函数已经考虑了重复元素的情况**,所以和代码和46题完全一样。 ## 思路二、DFS(常规思路,务必掌握) 类似46题,此题常规思路还是DFS做,与46题不同的是此题中数组可能有重复元素。同46题,也有两个DFS的思路,所以务必先阅读[46的题解思路二](https://github.com/ShusenTang/LeetCode/blob/master/solutions/46.%20Permutations.md)。 ### 普通DFS **我们先对nums进行排序,这样重复的元素总是挨着的**(注意我们始终不会改动nums,而swap DFS则会不断改动nums)。为了避免重复结果的产生,在递归函数中要**判断前面一个数和当前的数是否相等,如果相等且其visited中的值为false,需要跳过**,因为前面一个数字和当前数字相等的话前面的数字未被使用的话当前数字也不应该被使用。 ### swap DFS 此思路我们会不断改变nums,所以先排序也没用了,那要怎样避免重复呢。设我们递归处理到了下标为start,在这次调用中,我们会让nums[start]分别和第i(i = start, start+1,...)个数字进行交换(然后向下递归),即`swap(nums[i], nums[start])`,因此为了避免重复,我们需要检查每个nums[i]是否之前已经出现过了,即是否存在下标 j 满足`start <= j < i`且`nums[j] == nums[i]`,如果存在则跳过就是,否则就重复了。 为了避免重复,也可以直接粗暴一点,用set来存放结果,这样就会自动去除重复元素,只是复杂度上去了。 # C++ ## 思路一、Next Permutation ### 使用STL中的next_permutations(完全同46题) ``` C++ class Solution { public: vector> permuteUnique(vector& nums) { vector>res; if(nums.empty()) return res; sort(nums.begin(), nums.end()); // 先排序 res.push_back(nums); while(next_permutation(nums.begin(), nums.end())) res.push_back(nums); return res; } }; ``` ### 手动实现(即31题题解) ``` C++ class Solution { private: bool my_next_permute(vector& nums){ int len = nums.size(); int i = len - 1; while(i > 0 && nums[i] <= nums[i - 1]) i--; // 与46题的不同点一 if(i == 0) return false; int mid, low = i, high = len - 1; while(low <= high){ // 二分查找 mid = low + (high - low) / 2; if(nums[mid] <= nums[i - 1]) high = mid - 1; // 与46题的不同点二 else low = mid + 1; } // int high = len - 1; // while(nums[i - 1] >= nums[high]) high--; // 与46题的不同点二 swap(nums[i - 1], nums[high]); reverse(nums.begin() + i, nums.end()); return true; } public: vector> permuteUnique(vector& nums) { vector>res; if(nums.empty()) return res; sort(nums.begin(), nums.end()); // 先排序 res.push_back(nums); while(my_next_permute(nums)) res.push_back(nums); return res; } }; ``` ## 思路二、DFS ### 普通DFS ``` C++ class Solution { private: void DFS(vector> &res, vector &visited, vector &pmt, const vector &nums){ if(pmt.size() == nums.size()){ res.push_back(pmt); return; } for(int i = 0; i < nums.size(); i++){ if(visited[i]) continue; if(i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) continue; // 剪枝 visited[i] = true; pmt.push_back(nums[i]); DFS(res, visited, pmt, nums); pmt.pop_back(); visited[i] = false; } } public: vector> permuteUnique(vector& nums) { vector>res; if(nums.empty()) return res; vectorvisited(nums.size(), false); vectorpmt; sort(nums.begin(), nums.end()); // 先排序 DFS(res, visited, pmt, nums); return res; } }; ``` ### swap DFS ```C++ class Solution { private: int n; void DFS_swap(vector>&res, vector &nums, const int start){ if(start >= n){ res.push_back(nums); return; } int j; for(int i = start; i < n; i++){ for(j = i - 1; j >= start; j--) if(nums[j] == nums[i]) break; if (j != start - 1) continue; // 找到了与nums[i]相等的值 swap(nums[start], nums[i]); DFS_swap(res, nums, start + 1); swap(nums[start], nums[i]); } } public: vector> permuteUnique(vector& nums) { n = nums.size(); vector>res; DFS_swap(res, nums, 0); return res; } }; ```