46. 全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
解题思路: 这题是《剑指offer》书上的题,采取的是不停的交换数据的方法,有点像洗牌,具体的是,每次将start位置的元素与后面的元素进行交换,然后递归地调用start+1的交换。
class Solution {
public:
void helper(vector<int>& nums, int start, vector<vector<int>>& res) {
int n = nums.size();
if (start >= n) {res.push_back(nums); return;}
for (int i = start; i < n; ++i) {
swap(nums[start], nums[i]);
helper(nums, start + 1, res);
swap(nums[start], nums[i]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
helper(nums, 0, res);
return res;
}
};
还有一种解法用图的深度优先遍历解题,既然将此题转化成图的遍历,那么现在要解决的是如何构建图,我们可以这样思考,此题是求取全排列,那么全排列的定义是我们不重复的从1…n中取n次点,那么我们可以构建成一个n层的树(图),每层n-1个节点,那么对应到DFS遍历下一个节点,就可以对应到这n-1个节点,至此,情景转化完毕,解析来,我们只需将此题转化为求树的所有路径的题即可解。这是典型的回溯解法,即维护一个路径,遍历的过程中进行伸缩,当遍历完一条路径时,保存结果。
用方法二解题,几乎是暴力解法,时间复杂度会非常高,解全排列的题,依然推荐用交换法解题,解法二的回溯思想可以借鉴。
class Solution {
public:
void helper(vector<int>& nums, vector<bool>& visited, vector<int> out, vector<vector<int>>& res) {
if (out.size() == nums.size()) {res.push_back(out); return;}
for (int i = 0; i < nums.size(); ++i) {
if (visited[i]) continue;
visited[i] = true;
out.push_back(nums[i]);
helper(nums, visited, out, res);
visited[i] = false;
out.pop_back();
}
}
vector<vector<int>> permute(vector<int>& nums) {
int n = nums.size();
vector<bool> visited(n, false);
vector<vector<int>> res;
helper(nums, visited, {}, res);
return res;
}
};