用于回顾数据结构与算法时刷题的一些经验记录
-
回溯与递归都要注意剪枝,避免重复计算
-
回溯的问题,一定要把整个树图画出来,然后再去考虑如何缩小问题,还要注意恢复状态
-
回溯算法的大致模板:
- 根据这个模板,然后根据自己画出的树图,基本所有回溯问题都可以搞定。
void backtrack(已经做的选择, 选择列表): if 当前达到了结束位置: result.push(最终的选择序列) for 每个选择 in 选择列表: 做选择 //可能这里需要判断选择是否可行 backtrack(新的已选择, 新的选择列表) //这里的已选择和选择列表都要更新 撤销选择 //在此基础上,如果部分节点已经无解,则将它能够推理出的无解的树枝均剪掉
文章目录
-
-
-
- [46. 全排列](https://leetcode-cn.com/problems/permutations/)
- [78. 子集](https://leetcode-cn.com/problems/subsets/)
- [90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/)
- [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/)
- [40. 组合总和 II](https://leetcode-cn.com/problems/combination-sum-ii/)
- [22. 括号生成](https://leetcode-cn.com/problems/generate-parentheses/)
- [77. 组合](https://leetcode-cn.com/problems/combinations/)
- [51. N皇后](https://leetcode-cn.com/problems/n-queens/)
-
-
46. 全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
分析: 全排列的发现过程:
因此,每一步,我们需要确定一下当前深度的位置的元素,然后就可以继续向下展开。例如:如果在第一个位置选择了1,则在第二个位置可以选择2或者3,选择后再次选择第三个位置的元素。
如果在第i个位置选过后,则之后等价于前i个位置的元素排列已经确定,而后面实际上就是还没有排列的元素的全排列。这里就可以发现递归的思想。
因此回溯的大致思路为:
- 函数需要保存的内容有:当前已经确定的序列,当前的位置,序列的总长度
- 每次判断,是否已经遍历完,即当前位置是否等于序列的总长度,如果等于说明当前序列已经是一个结果,存储
- 如果没有遍历完,则确定一下该位置的元素,然后继续递归,递归时需要将当前位置右移
- 在这里确定元素时,可以用循环的方式直接确定,但是每次递归结束后,需要恢复现场,将状态均恢复,否则将导致回溯的难以结束或者结果不足
class Solution {
public:
vector<vector<int> > result;
vector<vector<int> > permute(vector<int>& nums)
{
backtrack(nums,0,nums.size());
return result;
}
void backtrack(vector<int>& output,int first,int len)
//output为当前已有的排列,first表示现在排列到的位置,len为长度
{
if(first==len)
{
result.emplace_back(output);
return ;
}
for(int i=first;i<len;i++) //进行回溯,每次确定好当前位置的元素后继续递归即可
{
swap(output[i],output[first]); //将目前的位置依次换成output[i]
backtrack(output,first+1,len);
swap(output[first],output[i]); //回溯的关键,在于递归后要恢复现场
}
}
};
78. 子集
给定一组不含重复元素的整数数组 n u m s nums nums,返回该数组所有可能的子集(幂集)。
**说明:**解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[[3],[1],[2],[1,2,3],[1,3],[2,3],[1,2],[]]
分析:
方法一:枚举或递归的思想
对于 n u m s nums nums 中每个元素,都有两种考虑,即选择或者不选择,因此最终结果数目是 2 n 2^n 2n ,时间复杂度也是 O ( n 2 n ) O(n2^n) O(n2n),因为每个结果都需要加到列表中,这是一个 O ( n ) O(n) O(n