1.回溯法概述
对于单维或者低维向量进行搜索时,可以非常简单的使用循环来遍历。但是随着维度的增加要怎么进行遍历呢?没错,使用回溯法可以对多维向量进行exhaustive searching。
它的通用编程模式:
backtrack( [v1,...,vn] )
{
if ( [v1,...,vn] is well-generated )
{
if ( [v1,...,vn] is a solution ) process solution;
return;
}
for ( x = possible values of vn+1 )
backtrack( [v1,...,vn, x] );
}
call backtrack( [] );
2.回溯法经典例题
1>列举数组(可以重复)
void backtrack1(vector< vector<int> > &res,vector<int> temp,int n)
{
if(n==0)
{
res.push_back(temp);
return ;
}
else
{
int i;
for(i=1;i<4;i++)
{
temp.push_back(i);
backtrack1(res,temp,n-1);
temp.pop_back();
}
}
}
int main()
{
vector< vector<int> > res;
vector<int> temp;
backtrack1(res,temp,3);
int i,j;
for(i=0;i<res.size();i++)
{
for(j=0;j<res[i].size();j++)
{
cout<<res[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
列举简图:
2> 列出[1,2,3] 的所有全排列,排列中不能有重复,只可能是1,2,3;1,3,2;2,3,1;2,1,3;3,1,2;3,2,1;共6中情况。
int used[4]={0};
void backtrack(vector< vector<int> > &res,vector<int> temp,int n)
{
if(n==0)
{
res.push_back(temp);
return ;
}
else
{
int i;
for(i=1;i<4;i++)
{
if(!used[i])
{
used[i]=1;
temp.push_back(i);
backtrack(res,temp,n-1);
temp.pop_back();
used[i]=0;
}
}
}
}
main函数是一样的,只需要该函数名称。
3>要求输出所有的1...n之间的k个数。
比如:n=4,k=2;输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<int>temp;
com(n,k,temp,0);
return res;
}
void com(int n,int k,vector<int> temp, int m)
{
if(k==0)
{
res.push_back(temp);
return ;
}
else
{
for(int i=1;i<=n;i++)
{
if(i>m)
{
temp.push_back(i);
com(n,k-1,temp,i);
temp.pop_back();
}
}
}
}
private:
vector<vector<int>> res;
};
4>输出序列中所有和为固定值的子序列:
例如:序列2,3,6,7 和为7,那么输出
[7]
[2, 2, 3]
class Solution {
private:
const int index_count;
vector<vector<int> > results;
public:
Solution() : index_count(10000) {};
// index记录当前找到的候选数字,n表示当前正在找第几个,n是index的下标不是candidates的下标
void backtrace(int target, int sum, vector<int> &candidates, int index[], int n)
{
if (sum > target)
{
return; // 回溯
}
if (sum == target)
{
vector<int> result;
for (int i = 1; i <= n; ++i)
{
result.push_back(candidates[index[i]]);
}
results.push_back(result);
return; // 此处可以不加,如果不加return由于都是正整数,到下面的计算时会多进行一次无用的递归。
}
// 深度搜索,为了避免重复,每次从当前候选项索引到结尾,上面的i=index[n]可以看出
for (int i = index[n]; i < candidates.size(); ++i)
{
index[n+1] = i; // 记录当前考察的候选项索引
backtrace(target, sum+candidates[i], candidates, index, n+1);
}
// 深度搜索,为了避免重复,每次从当前候选项索引到结尾,上面的i=index[n]可以看出
// for (int i = index[n]; i < candidates.size(); ++i)
// {
// index[n+1] = i+1; // 记录当前考察的候选项索引并加一,下次考察是跳过上次考察过的元素,每轮每个元素值考察一次
// backtrace(target, sum+candidates[i], candidates, index, n+1);
// }
}
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
sort(candidates.begin(), candidates.end());
int *index = new int[index_count];
memset(index, 0, sizeof(int)*index_count);
results.clear(); // 提交到leetcode的测试系统上必须添加,它应该是使用一个对象测试所有测试用例。
backtrace(target, 0, candidates, index, 0);
delete[] index;
return results;
}
};
5> 给定只有1到9这9个数,选择k个数,它的和等于n。
k=3,n=9.输出
[[1,2,6], [1,3,5], [2,3,4]]
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int n)
{
vector<int> temp;
sum(k,n,0,temp,0);
return res;
}
void sum(int k,int n,int f1,vector<int> temp,int m)
{
if(f1>n) return ;
if(f1==n && k==0) res.push_back(temp);
else
{
for(int i=1;i<10;i++)
{
if(i>m)
{
//flag[i]=1;
temp.push_back(i);
sum(k-1,n,f1+i,temp,i);
temp.pop_back();
//flag[i]=0;
}
else
continue;
}
}
}
private:
vector<vector<int>> res;
int flag[10]={0};
};
6> 输出所有的括号对的个数
例如n=3时。输出"((()))", "(()())", "(())()", "()(())", "()()()"
class Solution {
public:
vector<string> generateParenthesis(int n)
{
string s;
generate(n,n,s,res);
return res;
}
void generate(int leftNum,int rightNum,string s,vector<string> &result)
{
if(leftNum==0&&rightNum==0)
{
result.push_back(s);
}
if(leftNum>0)
{
generate(leftNum-1,rightNum,s+'(',result);
}
if(rightNum>0&&leftNum<rightNum)
{
generate(leftNum,rightNum-1,s+')',result);
}
}
private:
vector<string> res;
};