回溯法四舍五入和深度优先搜索差别多,一次深搜找到一种情况,然后根据递归的结束返回到上一次的递归部分,产生下一种情况。
- 全排列
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
vector<int> s;
vector<int> visited(nums.size(),0);//visited数组标记数组中的元素是否已被存入
f(res,nums,s,visited);
return res;
}
void f(vector<vector<int>>&res,vector<int>&nums,vector<int>&s,vector<int>&visited){
if(s.size()==nums.size()){//递归出口
res.push_back(s);
return;
}
for(int i=0;i<nums.size();i++){//因为判断数组中元素被没被访问需要从头开始,所以不需要start标记起始位置
if(visited[i]==1)
continue;
visited[i]=1;
s.push_back(nums[i]);
f(res,nums,s,visited);
s.pop_back();
visited[i]=0;//从序列中弹出的同时,也要将visited数组改回去
}
}
};
- 子集
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int >> result;
vector<int> tmp;//创建子集
f(result,nums,0,tmp);
return result;
}
void f(vector<vector<int>>& result, vector<int> nums, int startPoint, vector<int> tmp){
result.push_back(tmp);
for(int i=startPoint;i<nums.size();i++){
tmp.push_back(nums[i]);
f(result,nums,i+1,tmp);//数组下标也需要作为参数传入,否则每次从头开始会重复
tmp.pop_back();//将子集元素弹出
}
}
};
- 组合总和
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> s;
f(0,res,s,candidates,target);
return res;
}
void f(int start,vector<vector<int>>&res,vector<int> s,vector<int>candidates,int target){
if(target==0){//找到合适序列,存入数组
res.push_back(s);
return;
}
if(target<0){//说明这种序列不合理,返回
return;
}
for(int i=start;i<candidates.size();i++){
s.push_back(candidates[i]);
f(i,res,s,candidates,target-candidates[i]);
s.pop_back();
}
}
};
- 数组总和II
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> s;
sort(candidates.begin(),candidates.end());//排序
f(0,res,s,candidates,target);
return res;
}
void f(int start,vector<vector<int>>&res,vector<int> s,vector<int>candidates,int target){
if(target==0){
res.push_back(s);
return;
}
if(target<0){
return;
}
for(int i=start;i<candidates.size();i++){
if(i>start&&candidates[i]==candidates[i-1])//因为序列是已排序的,所以跳过相同的元素即可使结果不重复
continue;
s.push_back(candidates[i]);
f(i+1,res,s,candidates,target-candidates[i]);//因为一个元素只能使用一次,所以参数传入下一个字符
s.pop_back();
}
}
};
- 组合总和
画树状图就号理解了,儿子节点表示递归,兄弟节点表示循环。
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int target) {
vector<vector<int>> res;
vector<int> s;
f(0,res,s,k,target);
return res;
}
void f(int start,vector<vector<int>>&res,vector<int>&s,int k,int target){
if(target==0&&k==0){
res.push_back(s);
return;
}
if(target<0){
return;
}
for(int i=start+1;i<=9;i++){
s.push_back(i);
f(i,res,s,k-1,target-i);
s.pop_back();
}
}
};
- 括号生成
这道题和之前的区别在于,当我们确定之前括号有序时才能继续添加括号,所以我们可以添加记录左右括号的个数
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
f(res,"",n,0,0);
return res;
}
void f(vector<string> &res,string s,int n,int left,int right){
if(s.length()==2*n){
res.push_back(s);
return;
}
if(left<n)//左括号个数小于n
f(res,s+'(',n,left+1,right);
if(left>right)//右括号个数小于左括号
f(res,s+')',n,left,right+1);
}
};
- 字母大小写全排列
这类题本来是对于每个元素都先考虑放它的情况,再考虑不放它的情况,这道题是,对于每个字母,先考虑放它,再考虑放它的另一种大小写形式:如果是数字,就直接加进去,然后下一层递归;如果是字母,就先加进去,然后下一层递归;再加改变大小写形式, 然后下一层递归。
class Solution {
public:
vector<string> res;
vector<string> letterCasePermutation(string S) {
dfs(S,0);
return res;
}
void dfs(string s,int i){
if(i==s.size()){//递归出口
res.push_back(s);
return;
}
if(s[i]>='A'&&s[i]<='Z'){
dfs(s,i+1);
s[i]+=32;
dfs(s,i+1);
}else if(s[i]>='a'&&s[i]<='z'){
dfs(s,i+1);
s[i]-=32;
dfs(s,i+1);
}else dfs(s,i+1);
}
};