LeetCode/solutions/46. Permutations.md
2019-02-27 00:11:22 +08:00

189 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [46. Permutations](https://leetcode.com/problems/permutations/)
# 思路
返回一个数组的所有排列,数组中元素没有重复。
## 思路一、Next Permutation
如果做了[31. Next Permutation](https://leetcode.com/problems/next-permutation/)的话那这题就没什么问题了。稍有不同的是此题的数组元素没有重复而39题中可能重复。
所以此题可以使用STL中现成的next_permutation函数
> bool next_permutation (first, last)返回的是bool型如果已经是最后一个排列了则返回false并将数组变成第一个排列(即按照从小到大排好序)否则返回true并将数组变成下一个排列。
此外还可以传入comp参数: next_permutation (first, last, comp)这样就可以自定义数组的大小规则。
另外也可手动实现这个函数,参见我的[31题题解](https://github.com/ShusenTang/LeetCode/blob/master/solutions/31.%20Next%20Permutation.md)。
亲测使用STL中的要比手动实现慢很多。
## 思路二、DFS常规思路务必掌握
根据经验像这种要求出所有结果的集合常规思路DFS递归求解。这里有两种DFS思路。
### 普通DFS
用一个数组pmt记录此时的排列还需要用到一个visited数组来标记某个数字是否访问过然后每个DFS递归函数的for循环应从头开始注意[77. Combinations题解](https://github.com/ShusenTang/LeetCode/blob/master/solutions/77.%20Combinations.md)中不是从头开始的注意区别。当pmt和nums一样长时说明pmt是一个全排列此时应将pmt存放进结果数组res里并跳出当前递归。注意这里for循环应从头开始是因为这是求全排列而不像77题那样求组合每个位置都可能放任意一个数字这样会有个问题数字有可能被重复使用由于全排列是不能重复使用数字的所以我们需要用一个visited数组来标记某个数字是否使用过。
### swap DFS
此题可以使用的另一种DFS是基于这样一种思路每次交换num里面的两个数字经过递归可以生成所有的排列情况。即第一次递归时nums的第1个数字可以选择分别和第1、2...、n个数字交换在第一次递归的某种情况的基础上第二次递归可以让nums的第2个数字分别和第2、3...、n个数字进行交换......我们利用一个参数start记录当前所在的递归层数从0开始当`start = n`时当前nums中就是一个之前未出现过的全排列将其送入结果数组res中并跳出当前递归这样经过n次递归就可以获得所有全排列。
## 思路三、
最后再来看一种巧妙的方法:
* 当n=1时数组中只有一个数a1其全排列只有一种即为a1
* 当n=2时数组中此时有a1a2其全排列有两种即a1a2和a2a1。此时我们考虑和n=1时的关系我们发现其实就是在a1的前后两个位置分别加入了a2
* 当n=3时数组中有a1a2a3此时全排列有六种分别为a1a2a3、a1a3a2、a2a1a3、a2a3a1、a3a1a2和a3a2a1。对比n=2发现实际上是在a1a2和a2a1的基础上在不同的位置上加入a3而得到的。
针对上述思路可以写出递归和迭代两种代码。
# C++
## 思路一、Next Permutation
### 使用STL中的next_permutation
``` C++
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>>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;
}
};
```
### 手动实现(亲测更快)
``` C++
class Solution {
private:
bool my_next_permute(vector<int>& nums){
int len = nums.size();
int i = len - 1;
while(i > 0 && nums[i] < nums[i - 1]) i--;
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;
else low = mid + 1;
}
// int high = len - 1;
// while(nums[i - 1] > nums[high]) high--;
swap(nums[i - 1], nums[high]);
reverse(nums.begin() + i, nums.end());
return true;
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>>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
``` C++
class Solution {
private:
void DFS(vector<vector<int>>&res, vector<int>&pmt, vector<int>&visited, const vector<int> &nums){
int n = nums.size();
if(n == pmt.size()){
res.push_back(pmt);
return;
}
for(int i = 0; i < n; i++){
if(visited[i]) continue;
visited[i] = 1;
pmt.push_back(nums[i]);
DFS(res, pmt, visited, nums);
visited[i] = 0;
pmt.pop_back();
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>>res;
vector<int>pmt;
vector<int>visited(nums.size(), 0);
DFS(res, pmt, visited, nums);
return res;
}
};
```
### swap DFS
``` C++
class Solution {
private:
void DFS_swap(vector<vector<int>>&res, vector<int> &nums, const int start){
int n = nums.size();
if(start >= n){
res.push_back(nums);
return;
}
for(int i = start; i < n; i++){
swap(nums[start], nums[i]);
DFS_swap(res, nums, start + 1);
swap(nums[start], nums[i]);
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>>res;
DFS_swap(res, nums, 0);
return res;
}
};
```
## 思路三
### 递归
``` C++
class Solution {
private:
void helper(vector<vector<int>>&res, const int curr_n, const vector<int> &nums){
if(curr_n == nums.size()) return;
if(curr_n == 0) res.push_back(vector<int>(1, nums[0]));
else{
int res_size = res.size();
for(int i = 0; i < curr_n; i++){ // curr_n为复制次数
for(int j = 0; j < res_size; j++){
auto pmt = res[j];
pmt.insert(pmt.begin() + i, nums[curr_n]);
res.push_back(pmt);
}
}
for(int i = 0; i < res_size; i++)
res[i].push_back(nums[curr_n]);
}
helper(res, curr_n + 1, nums);
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>>res;
helper(res, 0, nums);
return res;
}
};
```
### 迭代
``` C++
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>>res;
res.push_back(vector<int>(1, nums[0]));
int curr_n = 1, n = nums.size();
while(curr_n < n){
int res_size = res.size();
for(int i = 0; i < curr_n; i++) // curr_n为复制次数
for(int j = 0; j < res_size; j++){
auto pmt = res[j];
pmt.insert(pmt.begin() + i, nums[curr_n]);
res.push_back(pmt);
}
for(int i = 0; i < res_size; i++)
res[i].push_back(nums[curr_n]);
curr_n++;
}
return res;
}
};
```