一、题目
给定一个
不含重复数字
的数组nums,返回其所有可能的
全排列
。你可以按任意顺序返回答案。
二、解题思路
全排列问题确实是一道经典的算法题,可以通过回溯算法来解决。具体实现细节可以进一步探讨。假设我们有一个长度为n的数组,我们可以将回溯算法视为一棵n叉树的前序遍历。在这棵树中,第一层有n个子节点,第二层有n-1个子节点,依此类推。为了更好地理解,我们可以通过一个具体的例子来详细说明。
代码实现:
#include <iostream>
#include <vector>
using namespace std;
void backtrack(vector<vector<int>>& list, vector<int>& tempList, vector<int>& nums) {
// 终止条件,如果数字都被使用完了,说明找到了一个排列
if (tempList.size() == nums.size()) {
list.push_back(tempList);
return;
}
// 遍历n叉树每个节点的子节点
for (int i = 0; i < nums.size(); i++) {
// 因为不能有重复的,所以有重复的就跳过
if (find(tempList.begin(), tempList.end(), nums[i]) != tempList.end())
continue;
// 选择当前值
tempList.push_back(nums[i]);
// 递归遍历子节点的子节点
backtrack(list, tempList, nums);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> list;
vector<int> tempList;
backtrack(list, tempList, nums);
return list;
}
int main() {
vector<int> nums = { 1, 2, 3 };
vector<vector<int>> result = permute(nums);
for (const auto& perm : result) {
cout << "[";
for (int i = 0; i < perm.size(); i++) {
cout << perm[i];
if (i < perm.size() - 1) cout << ", ";
}
cout << "]" << endl;
}
return 0;
}
用数组[1,2,3]来测试一下,看一下打印结果
[1, 2, 3]
是不是很意外,上面示例给出的是6个结果,这里打印的是1个,这是因为list是引用传递,
当遍历到叶子节点以后要往回走,往回走的时候必须把之前添加的值给移除了
,否则会越
加越多,我们来看下下面例子
三、代码实现
由此最后代码实现如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void backtrack(vector<vector<int>>& list, vector<int>& tempList, vector<int>& nums) {
// 终止条件,如果数字都被使用完了,说明找到了一个排列
if (tempList.size() == nums.size()) {
list.push_back(tempList);
return;
}
// 遍历n叉树每个节点的子节点
for (int i = 0; i < nums.size(); i++) {
// 因为不能有重复的,所以有重复的就跳过
if (find(tempList.begin(), tempList.end(), nums[i]) != tempList.end())
continue;
// 选择当前值
tempList.push_back(nums[i]);
// 递归遍历子节点的子节点
backtrack(list, tempList, nums);
// 撤销选择,把最后一次添加的值给移除
tempList.pop_back();
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> list;
vector<int> tempList;
backtrack(list, tempList, nums);
return list;
}
int main() {
vector<int> nums = { 1, 2, 3 };
vector<vector<int>> result = permute(nums);
for (const auto& perm : result) {
cout << "[";
for (int i = 0; i < perm.size(); i++) {
cout << perm[i];
if (i < perm.size() - 1) cout << ", ";
}
cout << "]" << endl;
}
return 0;
}
运行结果如下:
[1, 2, 3][1, 3, 2][2, 1, 3][2, 3, 1][3, 1, 2][3, 2, 1]
这道题还可以通过交换元素的方式来利用回溯算法解决。具体来说,我们可以先选择第一个数字,然后将其与后续的所有数字逐一交换,从而确定全排列的第一位。接着,我们选择第二个数字,并将其与后续的所有数字逐一交换,以确定全排列的第二位。这个过程持续进行,直到最后一个数字无法再进行交换。为了更直观地理解这个过程,我们可以绘制一个图示来展示每一步的交换情况。
交换实现:
#include <iostream>
#include <vector>
using namespace std;
void backtrack(vector<int>& nums, int index, vector<vector<int>>& res) {
// 到最后一个数字,没法再交换了,直接把数组转化为list
if (index == nums.size() - 1) {
// 把数组转为list
vector<int> tempList(nums.begin(), nums.end());
// 把list加入到res中
res.push_back(tempList);
return;
}
for (int i = index; i < nums.size(); i++) {
// 当前数字nums[index]要和后面所有的数字都要交换一遍(包括他自己)
swap(nums[index], nums[i]);
// 递归,数组[0,index]默认是已经排列好的,然后从index+1开始后面元素的交换
backtrack(nums, index + 1, res);
// 还原回来
swap(nums[index], nums[i]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
backtrack(nums, 0, res);
return res;
}
int main() {
vector<int> nums = {1, 2, 3};
vector<vector<int>> result = permute(nums);
for (const auto& perm : result) {
cout << "[";
for (int i = 0; i < perm.size(); i++) {
cout << perm[i];
if (i < perm.size() - 1) cout << ", ";
}
cout << "]" << endl;
}
return 0;
}
递归实现:
我们来探讨这个问题:假设我们有数组[1, 2, 3],如果我们已经知道[2, 3]的所有排列结果,那么我们只需要将1插入到这些排列结果的每一个可能位置,就能得到数组[1, 2, 3]的所有排列。为了更清晰地理解这个过程,我们可以通过绘制一个图示来展示如何将1插入到[2, 3]的每个排列中。
递归代码实现如下:
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> helper(vector<int>& nums, int index) {
vector<vector<int>> res;
// 递归的终止条件,如果到最后一个数组,直接把它放到res中
if (index == nums.size() - 1) {
// 创建一个临时数组
vector<int> temp;
// 把数字nums[index]加入到临时数组中
temp.push_back(nums[index]);
res.push_back(temp);
return res;
}
// 计算后面数字的全排列
vector<vector<int>> subList = helper(nums, index + 1);
// 集合中每个子集的长度
int count = subList[0].size();
// 遍历集合subList的子集
for (int i = 0, size = subList.size(); i < size; i++) {
// 把当前数字nums[index]添加到子集的每一个位置
for (int j = 0; j <= count; j++) {
vector<int> temp = subList[i];
temp.insert(temp.begin() + j, nums[index]);
res.push_back(temp);
}
}
return res;
}
vector<vector<int>> permute(vector<int>& nums) {
return helper(nums, 0);
}
int main() {
vector<int> nums = {1, 2, 3};
vector<vector<int>> result = permute(nums);
for (const auto& perm : result) {
cout << "[";
for (int i = 0; i < perm.size(); i++) {
cout << perm[i];
if (i < perm.size() - 1) cout << ", ";
}
cout << "]" << endl;
}
return 0;
}