这个文章大部分内容来自代码随想录的文章,我自己补充一些内容,把递归过程全部列举一遍,写的更详细一点,自己复习用。
题目:给定⼀个 没有重复数字的序列,返回其所有可能的全排列。
⽰例:
输⼊: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
回溯法通用模板:
一般是两个全局变量
vector<string>path;
vector<vector<string>>result;
void backtracking (参数) 组合问题一般要传startIndex,排列问题用不上startIndex一般不传
{
if(递归终止条件)
{
result.push_back(path) 即存放结果
return;
}
for (遍历本层内容)
{
//正式执行题目相关要求,处理节点
path.push_back(参数) 一般在此时会写入path
backtracking(参数)
回溯,撤销此前的所有操作
最起码是有一个path.pop_back()
}
}
然后是主函数调用backtracking就行,主函数一般没有内容,只负责调用和传初始参数
下面聚焦到此题上:
排列问题不用startIndex,但要多用一个used[]数组(图中橘色内容)保存数字是否使用过的信息。
当收集元素的数组
path
的⼤⼩达到和
nums
数组⼀样⼤的时候,说明找到了⼀个全排列,也
表⽰到达了叶⼦节点。
因为排列问题,每次都要从头开始搜索,例如元素
1
在
[1,2]
中已经使⽤过了,但是在
[2,1]
中
还要再使⽤⼀次
1
。
⽽
used
数组,其实就是记录此时
path
⾥都有哪些元素使⽤了,⼀个排列⾥⼀个元素只能使
⽤⼀次
。
全代码如下:
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
void backtracking (vector<int>& nums, vector<bool>& used){
if(path.size()==nums.size())
{
result.push_back(path);
return ;
}
for(int i=0;i<nums.size();i++)
{
if(used[i]==true)continue;
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = false;
}
}
};
之前学的过程中不知道哪里没想通,感觉排列问题比组合问题难理解,用startIndex隔开每一层递归的内容还好理解,这里老是没想明白,把整个过程打印出来捋一捋最后才想明白了。画一个图展示一下递归过程,下一次就不用想半天了,对着图和代码一起看就容易理清过程了。
结果集那里已经不是递归阶段负责的,到结果集那一步已经符合递归推出条件了。以{1,2,3}为例,可以在上面的代码里加上一段打印的代码看看过程。
...........
path.push_back(nums[i]);
backtracking(nums, used);
for (int i = 0; i < path.size(); i++)
{
cout << path[i] << " ";
}
cout << endl;
...........
几张图结合着看就方便构思出整个过程,有助于理解回溯的思想。