46. 全排列
题目描述
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
题解:
很明显的 回溯 。想象一下有 n
个坑,一个坑一个数字,往里面放数字就好了。
从前往后一个坑一个坑的枚举,每次选择一个没用过的数,选好之后,将该数标记为 已用 ,将该数保存到结果数组中,继续递归。
递归返回时,将该数的状态改为 未用 ,从结果数组中删除该数。
时间复杂度: O ( n × n ! ) O(n \times n!) O(n×n!)
代码:
class Solution {
public:
vector<vector<int>> ret;
vector<int> ans;
vector<bool> vis;
void dfs( vector<int>& nums, int now ) {
if ( now >= nums.size() ) {
ret.push_back( ans );
return;
}
for ( int i = 0; i < nums.size(); ++i ) {
if ( vis[i] ) continue;
vis[i] = true;
ans.push_back( nums[i] );
dfs( nums, now + 1 );
vis[i] = false;
ans.pop_back();
}
}
vector<vector<int>> permute(vector<int>& nums) {
int n = nums.size();
if ( !n ) return {};
vis.resize( n );
dfs( nums, 0 );
return ret;
}
};
/*
时间:4ms,击败:90.46%
内存:7.7MB,击败:91.34%
*/
优化:
因为时间复杂度为
O
(
n
×
n
!
)
O(n \times n!)
O(n×n!) ,所以 10
应该就是极限数据了,那么就不必开辟一个 vis
数组来记录每个数字的使用状态了,可以使用一个 int
变量,对应的二进制第 k
位上为 1
,表示 nums[k]
被用过了。
优化版本代码:
class Solution {
public:
vector<vector<int>> ret;
vector<int> ans;
int vis;
void dfs( vector<int>& nums, int now ) {
if ( now >= nums.size() ) {
ret.push_back( ans );
return;
}
for ( int i = 0; i < nums.size(); ++i ) {
if ( vis >> i & 1 ) continue;
vis ^= 1 << i;
ans.push_back( nums[i] );
dfs( nums, now + 1 );
vis ^= 1 << i;
ans.pop_back();
}
}
vector<vector<int>> permute(vector<int>& nums) {
int n = nums.size();
if ( !n ) return {};
vis = 0;
dfs( nums, 0 );
return ret;
}
};
/*
时间:0ms,击败:100.00%
内存:7.7MB,击败:90.24%
*/