回溯(Flash back)学习
Posted 楠c
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回溯(Flash back)学习相关的知识,希望对你有一定的参考价值。
目录
回溯是一种通过穷举所有可能情况来找到所有解的思想。如果一个候选解最后被发现并不是可行解,回溯思想会舍弃它,并在前面的一些步骤做出一些修改,并重新尝试找到可行解。回溯一般会结合在搜索算法中
普通回溯
矩阵中的路径
class Solution
private:
int m;
int n;
bool DFS(vector<vector<char>>& board, string& word,int x,int y,int k)
if(x<0||y<0||x>=m||y>=n||board[x][y]!=word[k])
return false;
if(k==word.size()-1)//一直为ture,才能进来,说明都能搜索到,等于字符串长度就退出
return true;
//先标记为' '防止影响这个点的上下左右搜索(当前方案不要走回头路)
board[x][y]=' ';
bool ret=DFS(board,word,x+1,y,k+1)||DFS(board,word,x-1,y,k+1)||DFS(board,word,x,y+1,k+1)||DFS(board,word,x,y-1,k+1);
//恢复,防止影响别人搜索(进行下一次搜索方案)
board[x][y]=word[k];
return ret;
public:
bool exist(vector<vector<char>>& board, string word)
m=board.size();
n=board[0].size();
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
//如果某一点搜索到全部单词就返回
if(DFS(board,word,i,j,0))
return true;
return false;
;
二叉树和为某一值的路径
深度搜索+回溯。创建一个临时数组,先push进节点,然后搜索左和右,不满足的话回溯。
class Solution
private:
vector<int> temp;
void Dfs(TreeNode* root, int target,vector<vector<int>>& ret)
if(root==nullptr)
return;
//先插入临时数组
temp.push_back(root->val);
target-=root->val;
//左右都为空,且目标值为0才插入到二维数组当中
if(root->left==nullptr&&root->right==nullptr&&target==0)
ret.push_back(temp);
//搜索左
Dfs(root->left,target,ret);
//搜索右
Dfs(root->right,target,ret);
//回溯
temp.pop_back();
public:
vector<vector<int>> pathSum(TreeNode* root, int target)
vector<vector<int>> ret;
Dfs(root,target,ret);
return ret;
;
N皇后
class Solution
void DFS(vector<vector<pair<int,int>>>& ret,vector<pair<int,int>>& temp,int curRow,int n)
if(curRow==n)
//临时数组坐标全部符合,curRow才能加到n,才能入结果数组
ret.push_back(temp);
for(int col=0;col<n;col++)
//坐标有效才插入临时数组
//临时数组:(保护当前一种方法的所有皇后坐标)
if(isValid(temp,curRow,col))
temp.push_back(make_pair(curRow,col));
DFS(ret,temp,curRow+1,n);
temp.pop_back();
//皇后坐标是否有效
bool isValid(vector<pair<int,int>>& temp,int curRow,int col)
for(auto& e:temp)
if(e.second==col||e.first+e.second==curRow+col||e.first-e.second==curRow-col)
return false;
return true;
//输出有点奇怪,所以要转换
vector<vector<string>> transRet(vector<vector<pair<int,int>>> ret,int n)
vector<vector<string>> vv;
for(auto& v:ret)
//观察输出,二维数组,每个一维数组由4个string组成,每个string都有四个'.'
vector<string> vs(n,string(n,'.'));
for(auto& e:v)
vs[e.first][e.second]='Q';
vv.push_back(vs);
return vv;
public:
vector<vector<string>> solveNQueens(int n)
vector<vector<pair<int,int>>> ret;
vector<pair<int,int>> temp;
DFS(ret,temp,0,n);
vector<vector<string>> vv=transRet(ret,n);
return vv;
;
N皇后||
我们用一维临时数组来存储当前方法的皇后坐标。有几种方法就插入几次二维数组。最后二维数组的size就是方法数量
class Solution
private:
void DFS(vector<vector<pair<int,int>>>& ret,vector<pair<int,int>>& temp,int curRow,int n)
if(curRow==n)
ret.push_back(temp);
return;
for(int col=0;col<n;col++)
if(isVaild(temp,curRow,col))
temp.push_back(make_pair(curRow,col));
DFS(ret,temp,curRow+1,n);
temp.pop_back();
bool isVaild(vector<pair<int,int>> temp,int curRow,int col)
for(auto&e :temp)
if(col==e.second||curRow+col==e.first+e.second||curRow-col==e.first-e.second)
return false;
return true;
public:
int totalNQueens(int n)
vector<vector<pair<int,int>>> ret;
vector<pair<int,int>> temp;
DFS(ret,temp,0,n);
return ret.size();
;
排列问题
全排列
字符串排列
字符串排列
题目的样例要求向前回退,即b可以匹配a,所以在循环中我们每次都要从0开始,但是这样肯定会有aaa,aab,aac,等这种一个元素重复使用的情况。
vis是一个标记数组
这样就避免了重复使用一个元素。
class Solution
private:
void DFS(string s,unordered_set<string>& ret,vector<bool>& vis,string curStr,int index)
//放入哈希set当中,这样做的原因是防止字符串中有重复的例如"aab"
if(index==s.size())
ret.insert(curStr);
return;
for(int i=0;i<s.size();i++)
if(vis[i])
vis[i]=false;
DFS(s,ret,vis,curStr+s[i],index+1);
vis[i]=true;
public:
vector<string> permutation(string s)
unordered_set<string> ret;
vector<bool> vis(s.size(),true);
DFS(s,ret,vis,"",0);
//去重之后再放进数组中返回
vector<string> ret1(ret.begin(),ret.end());
return ret1;
;
24点游戏
#include<iostream>
#include<string>
#include<vector>
using namespace std;
bool Dfs(vector<double>& v,vector<bool> used,int point,double result,int index)
if(index==4&&result==point)
return true;
for(int i=0;i<4;i++)
if(!used[i])
used[i]=true;
bool ret=Dfs(v,used,point,result+v[i],index+1)\\
||Dfs(v,used,point,result-v[i],index+1)\\
||Dfs(v,used,point,result*v[i],index+1)\\
||Dfs(v,used,point,result/v[i],index+1);
used[i]=false;
if(ret)
return true;
return false;
int main()
vector<double> v(4,0);
vector<bool> used(4,false);
while(cin>>v[0]>>v[1]>>v[2]>>v[3])
if(Dfs(v,used,24,0,0))
cout<<"true"<<endl;
else
cout<<"false"<<endl;
return 0;
活字印刷
可以向前回退,但重复元素不能使用。即循环从0开始,且需要标记数组
class Solution
private:
void DFS(string tiles,unordered_set<string>& ret,vector<bool>& vis,string curStr)
if(curStr!="")
ret.insert(curStr);
for(int i=0;i<tiles.size();i++)
if(vis[i])
vis[i]=false;
DFS(tiles,ret,vis,curStr+tiles[i]);
vis[i]=true;
public:
int numTilePossibilities(string tiles)
unordered_set<string> ret;
vector<bool> vis(tiles.size(),true);
DFS(tiles,ret,vis,"");
return ret.size();
;
组合问题
电话号码组合
Map建立号码与字符串映射。
在代码中,DFS里,curStr+e即一个回溯的过程。例如当前是cur是空串,e依次被映射为abc,先从a开始,继续调用DFS,由于index++,所以e依次被映射到def,所以curStr现在是a,就开始和def两两组合,符合条件入结果数组。执行完后回溯,curStr成为b,又开始新的一轮。
class Solution
private:
unordered_map<char,string> Map;
void DFS(const string& digits,vector<string>& Allret,string curStr,int idx)
if(idx==digits.size())
//只有和字符串大小相等时才加入数组当中,然后结束当前深搜
Allret.push_back(curStr);
return;
//取出当前字符对应的字符串
string curMap=Map[digits[idx]];
for(char e:curMap)
//curStr+e,即为一个回溯的过程
//从DFS返回他就又成为了curStr
//curStr+e是个临时变量,所以形参不能是引用
DFS(digits,Allret,curStr+e,idx+1);
public:
vector<string> letterCombinations(string digits)
vector<string> Allret;
if(digits.size()==0)
return Allret;
Map=哪个 Javascript 历史回溯实现是最好的?