编程问题:
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
- 输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
解法:
1.回溯
- 时间复杂度:O(N×N!),其中 N 为序列的长度。算法的复杂度首先受 backtrack 的调用次数制约,backtrack 的调用次数为 ∑ k = 0 N P ( N , k ) \sum_{k=0}^N {P\left(N,k\right)} k=0∑NP(N,k)次,其中 P ( N , k ) = N ! ( N − k ) ! = N ( N − 1 ) . . . ( N − k + 1 ) {P\left(N,k\right)} = \frac{N!}{(N-k)!} =N(N-1)...(N-k+1) P(N,k)=(N−k)!N!=N(N−1)...(N−k+1),该式被称作 n 的 k - 排列,或者部分排列。 ∑ k = 0 N P ( N , k ) = N ! ( N − k ) ! = N ! + N ! 1 ! + N ! 2 ! + N ! 3 ! + . . . + N ! ( N − 1 ) ! < 2 N ! + N ! 2 + N ! 2 2 + . . . + N ! 2 n − 2 < 3 N ! \sum_{k=0}^N {P\left(N,k\right)} = \frac{N!}{(N-k)!} =N!+\frac{N!}{1!}+\frac{N!}{2!}+\frac{N!}{3!}+...+\frac{N!}{(N-1)!}<2N!+\frac{N!}{2}+\frac{N!}{2^2}+...+\frac{N!}{2^{n-2}}<3N! k=0∑NP(N,k)=(N−k)!N!=N!+1!N!+2!N!+3!N!+...+(N−1)!N!<2N!+2N!+22N!+...+2n−2N!<3N!,这说明 backtrack 的调用次数是 O(N!) 的。而对于 backtrack 调用的每个叶结点(共 N! 个),我们需要将当前答案使用 O(N) 的时间复制到结果数组中,相乘得时间复杂度为 O(N×N!)。因此时间复杂度为 O(N×N!)。
- 空间复杂度:O(N),其中 N 为序列的长度。除结果数组以外,递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,这里可知递归调用深度为 O(N)。
class Solution {
private:
void backTrack(vector<int>& nums, int n, vector<bool>& status, vector<vector<int>>& res, vector<int>& track)
{
if (track.size() == n)
{
res.push_back(track);
return;
}
for (int i = 0; i < n; i++)
{
if (!status[i]) //false 则可以填入结果数组
{
status[i] = true;
track.push_back(nums[i]);
backTrack(nums, n, status, res, track);
track.pop_back();
status[i] = false;
}
}
}
public:
vector<vector<int>> permute(vector<int> &nums)
{
vector<bool> status; //标记数组中已经填过的数
vector<vector<int>> res;
vector<int> track; //存放数字排列的结果数组
int n = nums.size();
status.resize(n, false);
backTrack(nums, n, status, res, track);
return res;
}
};