本篇主要是介绍一下回溯算法:所谓回溯算法,又称为“试探法”。解决问题时,每进行一步,都是抱着试试看的态度,如果发现当前选择并不是最好的,或者这么走下去肯定达不到目标,立刻做回退操作重新选择。这种走不通就回退再走的方法就是回溯算法。
回溯法:实际上回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足条件的某个状态的点称为“回溯点”。也可以称为剪枝点,所谓的剪枝,指的是把不会找到的目标,或者不必要的路径裁减掉。
在包含问题的所有解中空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当深度到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。
如果使用回溯法求解问题的时候,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
而若使用回溯法求任一解时,只要搜索到问题的一个解就可以结束。(除了深度优先搜索外,常见的还有广度优先搜索)。
- /*
- // Definition for Employee.
- class Employee {
- public:
- int id;
- int importance;
- vector
subordinates; - };
- */
-
- class Solution {
- public:
- int DFS(unordered_map<int,Employee*>&info,int id)
- {
- int sumImportance=info[id]->importance;
- //深度探索员工一的下属然后往下探索员工二的下属……
- for(auto e:info[id]->subordinates)
- {
- sumImportance+=DFS(info,e);
-
- }
- //最后返回员工的数据结构中的重要度的和
- return sumImportance;
- }
- int getImportance(vector
employees, int id) { - //如果此时没有保存员工的数据结构则返回空的重要度
- if(employees.empty())
- return 0;
- //为了使时间效率更高可以使用哈希的结构,因为哈希的查找效率比较高
- unordered_map<int,Employee*> info;
- //将员工的信息的数据结构放入哈希表中
- for(auto e:employees)
- {
- info[e->id]=e;
- }
- return DFS(info,id);//将哈希表中的内容进行深度优先搜索操作
- }
- };
- int nextp[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
- class Solution {
- public:
- void DFS(vector
int >>&image,vectorint >>&book,int row,int col,int sr,int sc,int oldColor,int newColor) - {
- image[sr][sc]=newColor;
- book[sr][sc]=1;
-
- for(int i=0;i<4;i++)
- {
- int newX=sr+nextp[i][0];
- int newY=sc+nextp[i][1];
- if(newX>=row||newX<0)
- {
- continue;
- }
- if(newY>=col||newY<0)
- {
- continue;
- }
- if(image[newX][newY]==oldColor&&book[newX][newY]==0)
- {
- DFS(image,book,row,col,newX,newY,oldColor,newColor);
- }
- }
- }
- vector
int>> floodFill(vectorint>>& image, int sr, int sc, int color) { - if(image.empty())
- {
- return image;
- }
- vector
int>> book(image.size(),vector<int>(image[0].size(),0)); - int oldColor=image[sr][sc];
- DFS(image,book,image.size(),image[0].size(),sr,sc,oldColor,color);
- return image;
- }
- };
- int nextp[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//给定方向矩阵
- class Solution {
- public:
- int DFS(vector
int >> &grid,int row,int col,int x,int y) - {
- if(x>=row||x<0||y>=col||y<0||grid[x][y]==0) return 1;
-
- if(grid[x][y]==2) return 0;
-
- grid[x][y]=2;//标记此时位置已经搜索过
- int s=0;
- //对于此时的位置进行深度优先遍历操作
- for(int k=0;k<4;k++)
- {
- int newX=x+nextp[k][0];
- int newY=y+nextp[k][1];
-
- //对于该位置进行深度优先遍历求出此时的位置有效的边界长度
- s+=DFS(grid,row,col,newX,newY);
- }
- return s;
- }
- int islandPerimeter(vector
int >>& grid) { - if(grid.empty())
- return 0;
-
- int row=grid.size();
- int col=grid[0].size();
- int c=0;
-
- for(int i=0;i
|
- {
- for(int j=0;j
- {
- //对于陆地进行深度优先遍历操作
- if(grid[i][j]==1)
- c+=DFS(grid,row,col,i,j);
- }
- }
- return c;
- }
- };
4.被围绕的区域OJ链接
- int nextp[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
- class Solution {
- public:
- void DFS(vector
char >>&board,int row,int col,int sr,int sc) - {
- board[sr][sc]='*';//标记此时为‘O’深度探索是否被
- for(int i=0;i<4;i++)
- {
- int newX=sr+nextp[i][0];//将此时的方向进行四个方向进行深度优先遍历操作
- int newY=sc+nextp[i][1];
- if(newX<0||newX>=row)//需要对此时的方向进行判断是否是越界行为,如果是越界行为则此时跳过去,进行下一个方向的深度优先遍历操作
- {
- continue;
- }
- if(newY<0||newY>=col)
- {
- continue;
- }
- if(board[newX][newY]!='*'&&board[newX][newY]!='X')//
- //继续搜索‘O’
- {
- DFS(board,row,col,newX,newY);
- }
- }
- }
- void solve(vector
char >>& board) { - if(board.empty())
- return;
- int row=board.size();
- int col=board[0].size();
- //第一行和最后一行进行深度优先遍历
- for(int j=0;j
- {
- if(board[0][j]=='O')
- DFS(board,row,col,0,j);
- if(board[row-1][j]=='O')
- DFS(board,row,col,row-1,j);
- }
- //第一列和最后一列进行深度优先遍历
- for(int i=0;i
|
- {
- if(board[i][0]=='O')
- DFS(board,row,col,i,0);
- if(board[i][col-1]=='O')
- DFS(board,row,col,i,col-1);
- }
- //最后将深度搜索的结果进行修改
- for(int i=0;i
|
- {
- for(int j=0;j
- {
- if(board[i][j]=='*')//最后将所有的'*'改为‘O’
- {
- board[i][j]='O';
- }
- else if(board[i][j]=='O')//最后将所有的'O'改为‘X’
- {
- board[i][j]='X';
- }
- }
- }
- }
- };
5.岛屿数量OJ链接
- int nextp[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//定义一个方向矩阵
- class Solution {
- public:
- void DFS(vector
char >>&grid,vectorint >>&book,int row,int col,int x,int y) - {
- book[x][y]=1;//标记此时的位置已经走过
-
- for(int k=0;k<4;k++)//对于当前x,y的位置进行方向遍历操作
- {
- int newX=x+nextp[k][0];//对于x的横向坐标
- int newY=y+nextp[k][1];//对于y的纵向坐标
-
- if(newX>=row||newX<0||newY>=col||newY<0)//考虑一下边界的问题,如果此时走到了边界应该访问下一个位置
- continue;
- if(grid[newX][newY]=='1'&&book[newX][newY]==0)
- DFS(grid,book,row,col,newX,newY);
- }
- }
- int numIslands(vector
char >>& grid) { - if(grid.empty())
- return 0;
-
- int row=grid.size();
- int col=grid[0].size();
- //标记矩阵
- vector
int>> book(row,vector<int>(col,0)); - int amount=0;
-
- for(int i=0;i
|
- for(int j=0;j
- if(grid[i][j]=='1'&&book[i][j]==0)
- {
- amount++;
- DFS(grid,book,row,col,i,j);
- }
- return amount;
- }
- };
6.岛屿的最大面积OJ链接
- int nextp[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//给定方向矩阵
- int s=0;//定义全局变量来求出每个岛屿的面积
- class Solution {
- public:
- void DFS(vector
int >>&grid,int row,int col,int x,int y) - {
- //如果此时为陆地则进行S++
- if(grid[x][y]==1)
- s++;
- grid[x][y]=2;//同时标记此时已经走过
-
- for(int k=0;k<4;k++)//从当前位置进行方向遍历
- {
- int newX=x+nextp[k][0];
- int newY=y+nextp[k][1];
- if(newX<0||newX>=row||newY<0||newY>=col)
- continue;
- if(grid[newX][newY]==1)//从陆地开始进行深度遍历
- DFS(grid,row,col,newX,newY);
- }
- }
- int maxAreaOfIsland(vector
int >>& grid) { - if(grid.empty())
- return 0;
-
- int row=grid.size();
- int col=grid[0].size();
- int maxS=0;
-
- for(int i=0;i
|
- for(int j=0;j
- if(grid[i][j]==1)//从陆地开始进行深度优先遍历操作
- //将每一个位置进行比较,找出最大那个岛屿的面积
- {
- DFS(grid,row,col,i,j);
- maxS=max(maxS,s);
- s=0;
- }
- return maxS;
- }
- };
7.电话号码的字母组合OJ链接
- map<char,string> hashTable={{'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}};
- class Solution {
- public:
- void DFS(string& digits,vector
&resultString,string curStr,int curDepth) - {
- //如果此时遍历到最后一个
- if(curDepth==digits.size())
- {
- resultString.push_back(curStr);
- return;
- }
- //利用哈希映射的方式进行
- string strMap=hashTable[digits[curDepth]];
- for(char ch:strMap)
- {
- DFS(digits,resultString,curStr+ch,curDepth+1);
- }
- }
- vector
letterCombinations(string digits) { - //将所有的结果集保存到resultString
- vector
resultString; - if(digits.empty())
- return resultString;
- //从第一个位置开始进行深度优先遍历深度优先遍历
- DFS(digits,resultString,"",0);
- return resultString;
- }
- };
8.组合总和OJ链接
- class Solution {
- public:
- void DFS(vector<int>&candidates,vector
int >>&solutions,vector<int>&solution,int curSum,int position,int target) - {
- //如果当前遍历所得到的结果刚好为所给的target值
- //那么将当前所遍历的结果集保留在solutions
- if(curSum==target)
- {
- solutions.push_back(solution);
- }
- //如果遍历出的和是大于,则说明此时条件不满足
- if(curSum>target)
- {
- return;
- }
- //从position位置开始进行深度优先遍历操作
- for(int i=position;i
size();i++) - {
- if(candidates[i]>target)//如果此时的所给出的candidats中
- // //的值比所给的target大,那么直接结束当前所在位置的优先遍历
- {
- continue;
- }
- //不满足上述条件说明此时的curSum的值小于target
- //可以将当前的candidates[i]放入到当前的结果集中
- //然后接着往后进行深度优先遍历操作
- solution.push_back(candidates[i]);
- DFS(candidates,solutions,solution,curSum+candidates[i],i,target);
- solution.pop_back();
- }
- }
- vector
int>> combinationSum(vector<int>& candidates, int target) { - //定义一个二维数组保留所有结果集
- vector
int>> solutions; - //保留当前的结果集
- vector<int> solution;
- //记录当前结果集中的和
- int curSum=0;
- //进行深度优先遍历
- DFS(candidates,solutions,solution,curSum,0,target);
- return solutions;
- }
- };
9.活字印刷OJ链接
- class Solution {
- public:
- void DFS(string &tiles,string curStr,vector<int>&useString,unordered_set
&hashTable) - {
- //如果此时深度优先遍历的字符串不为空,则此时的所得到的的字符串放入当前的哈希表中,将自动进行去重的处理
- if(!curStr.empty())
- hashTable.insert(curStr);
- //深度优先遍历每一个位置
- for(int i=0;i
size();i++) - {
- if(useString[i])//如果当前的位置已经被访问过,则说明此时当前位置不需要访问
- continue;
- useString[i]=1;//标记当前位置
- DFS(tiles,curStr+tiles[i],useString,hashTable);//继续往下进行深度优先遍历的操作
- //需要进行回退的操作
- useString[i]=0;
- }
- }
- int numTilePossibilities(string tiles) {
- if(tiles.empty())
- return 0;
-
- //利用unoreded_set容器的特性,将容器内的内容自动进行去重操作
- unordered_set
hashTable; - //先将当前的tiles进行初始化的操作
- vector<int> useString(tiles.size(),0);
- //进行深度优先遍历操作
- DFS(tiles,"",useString,hashTable);
- return hashTable.size();
- }
- };
10.N皇后OJ链接
- class Solution {
- public:
- //深度优先遍历
- void DFS(vector
int ,int>>>&solutions,vectorint ,int>>&solution,int curRow,int n) - {
- //如果当前行等于n则将一种solution方案放入solutions中
- if(curRow==n)
- {
- solutions.push_back(solution);
- }
- for(int i=0;i
- {
- //判断当前皇后所在的位置是否能够满足条件
- if(isValid(solution,curRow,i))
- {
- //如果满足条件则将此时的坐标记录下来
- solution.push_back(make_pair(curRow,i));
- //继续深度优先遍历下一行
- DFS(solutions,solution,curRow+1,n);
- //第一列遍历后回溯下来遍历第二行
- solution.pop_back();
- }
- }
- }
- bool isValid(vector
int ,int>>&solution,int curRow,int col) - {
- //遍历方案中的点
- //如果此时该皇后中的纵坐标与方案中的纵坐标相同或者横坐标与纵坐标的和等于当前皇后的横纵坐标之和或者是方案中横纵坐标之差等于当前皇后的横纵坐标之差
- for(auto i:solution)
- {
- if(i.second==col||i.first+i.second==curRow+col||i.first-i.second==curRow-col)
- {
- return false;
- }
- }
- return true;
- }
- vector
> transformString(vectorint,int>>>&solutions,int n) - {
- vector
> resultString;//保留最后方案中的字符串 - for(vector
int,int>>&solution:solutions) - {
- //利用构造函数先进行所有的字符变为'.'最后再将皇后位置变为'Q'
- vector
tmpString(n,string(n,'.')) ; - for(pair<int,int> &i:solution)
- {
- tmpString[i.first][i.second]='Q';
- }
- resultString.push_back(tmpString);
- }
- return resultString;
- }
- vector
> solveNQueens(int n) { - vector
int,int>>> solutions;//将不同解法的方案存放 - vector
int,int>> solution;//存放一种方案中的n个皇后的摆放 - //将n*n的棋盘从第一个位置开始进行深度优先遍历
- DFS(solutions,solution,0,n);
- //将深度优先遍历后的每一种方案都转化为字符型
- return transformString(solutions,n);
- }
- };
11.N皇后IIOJ链接
- class Solution {
- public:
- //深度优先遍历
- int DFS(vector
int ,int>>&solution,int curRow,int n) - {
- //如果当前行等于n则将一种solution
- if(curRow==n)
- {
- return 1;
- }
- else
- {
- int count=0;
- for(int i=0;i
- {
- //判断当前皇后所在的位置是否能够满足条件
- if(isValid(solution,curRow,i))
- {
- //如果满足条件则将此时的坐标记录下来
- solution.push_back(make_pair(curRow,i));
- //继续深度优先遍历下一行
- count+=DFS(solution,curRow+1,n);
- //第一列遍历后回溯下来遍历第二行
- solution.pop_back();
- }
- }
- return count;
- }
- }
- bool isValid(vector
int ,int>>&solution,int curRow,int col) - {
- //遍历方案中的点
- //如果此时该皇后中的纵坐标与方案中的纵坐标相同或者横坐标与纵坐标的和等于当前皇后的横纵坐标之和或者是方案中横纵坐标之差等于当前皇后的横纵坐标之差
- for(auto i:solution)
- {
- if(i.second==col||i.first+i.second==curRow+col||i.first-i.second==curRow-col)
- {
- return false;
- }
- }
- return true;
- }
- int totalNQueens(int n) {
- vector
int,int>> solution; - return DFS(solution,0,n);
- }
- };
二、有关广度优先遍历的题型
1.N叉树的层序遍历OJ链接
- /*
- // Definition for a Node.
- class Node {
- public:
- int val;
- vector
children; - Node() {}
- Node(int _val) {
- val = _val;
- }
- Node(int _val, vector
_children) { - val = _val;
- children = _children;
- }
- };
- */
-
- class Solution {
- public:
- vector
int>> levelOrder(Node* root) { - if(root==nullptr) return vector
int>>(); -
- vector
int>> vv; - queue
q;//利用队列进行广度优先遍历 -
- q.push(root);//首先将头结点先入队操作
- while(!q.empty())//如果此时的队列不为空,则继续进行操作
- {
- vector<int> v;//利用一个一维数组来存储当前层的节点
- int size=q.size();//每一层的大小需要保存下来,方便下一次的遍历操作
- for(int i=0;i
- {
- Node*front=q.front();
- q.pop();
- v.push_back(front->val);
-
- for(auto child:front->children)//遍历当前节点的孩子节点,如果不为空,则将此时的孩子节点入队列
- if(child)
- q.push(child);
- }
- vv.push_back(v);
- }
- return vv;
- }
- };
2.腐烂的橘子OJ链接
- int nextp[4][2]={{1,0},{0,1},{0,-1},{-1,0}};
- class Solution {
- public:
- int orangesRotting(vector
int >>& grid) { - //利用pair来存储位置信息
- queue
int,int>> q; - int row=grid.size();
- int col=grid[0].size();
-
- //第一步将腐烂的橘子进行入队操作
- for(int i=0;i
|
- for(int j=0;j
- if(grid[i][j]==2)
- q.push(make_pair(i,j));
-
- //进行蔓延的方向
- int times=0;
- while(!q.empty()){
- int size=q.size();
- int flag=0;//定义一个flag来判断是否是有新橘子被腐烂
- for(int i=0;i
- pair<int,int> Curpos=q.front();
- q.pop();
-
- for(int i=0;i<4;i++){
- int newX=Curpos.first+nextp[i][0];
- int newY=Curpos.second+nextp[i][1];
-
-
- //如果此时的位置中存在越界或者是该位置上面的橘子已经被腐烂过则跳过此次
- if(newX>=row||newY>=col||newX<0||newY<0||grid[newX][newY]!=1)
- continue;
- flag=1;
- grid[newX][newY]=2;
- q.push(make_pair(newX,newY));
- }
- }
- if(flag)
- ++times;
- }
- //最后需要进行判断是否还存在无法腐烂的橘子
- for(int i=0;i
|
- for(int j=0;j
- if(grid[i][j]==1)
- return -1;
- return times;
- }
- };
3.单词接龙OJ链接
- class Solution {
- public:
- int ladderLength(string beginWord, string endWord, vector
& wordList) { - //利用哈希表进行查找效率比较高
- unordered_set
HashTable(wordList.begin(),wordList.end()) ; -
- //定义一个标记的哈希表来标记已经查找过的单词
- unordered_set
visitHashTable; - //讲起始的单词进行标记
- visitHashTable.insert(beginWord);
-
- //利用队列的特性进行广度优先遍历操作
- queue
q; - q.push(beginWord);
-
- int step=1;
-
- //队列不为空,继续进行转换操作
- while(!q.empty())
- {
- int size=q.size();
-
- for(int i=0;i
- {
- //用来保存队列中第一个单词进行转换操作
- string curWord=q.front();
- q.pop();
-
- for(int i=0;i
size();i++){ - //对于当前单词进行转换操作
- string newWord=curWord;
-
- for(char ch='a';ch<='z';ch++)
- {
- //替换newWord中的字符来进行转换
- newWord[i]=ch;
-
- //如果此时的单词在哈希表中没有找到,或者该单词已经被标记过
- if(!HashTable.count(newWord)||visitHashTable.count(newWord))
- continue;
-
- if(newWord==endWord)
- return step+1;
-
- //如果没有转换成功,需要进行入队操作和进行标记操作
-
- q.push(newWord);
- visitHashTable.insert(newWord);
- }
- }
- }
- step++;
- }
- return 0;
- }
- };
4.最小基因变化OJ链接
- class Solution {
- public:
- int minMutation(string startGene, string endGene, vector
& bank) { - //利用哈希表查找
- unordered_set
HashTable(bank.begin(),bank.end()) ; -
- //标记
- unordered_set
visitHashTable; - visitHashTable.insert(startGene);
-
- //利用队列进行广度优先遍历操作
- queue
q; - q.push(startGene);
-
- //对于基因有四种可能
- vector<char> Gene(4);
- Gene[0]='A';
- Gene[1]='C';
- Gene[2]='G';
- Gene[3]='T';
-
- int step=0;
- while(!q.empty())
- {
- int size=q.size();
- for(int i=0;i
- {
- string curGene=q.front();
- q.pop();
-
- //对于当前基因的位置进行广度遍历
- for(int j=0;j
size();j++) - {
- string newGene=curGene;
- for(auto ch:Gene)
- {
- newGene[j]=ch;
- //如果已经出现了当前的基因或者是该基因不存在
- if(!HashTable.count(newGene)||visitHashTable.count(newGene))
- continue;
-
- //如果此时的基因正好是等于当前的基因
- if(newGene==endGene)
- return step+1;
- //否则讲当前额的基因入队列
- visitHashTable.insert(newGene);
- q.push(newGene);
- }
- }
- }
- step++;
- }
- return -1;
- }
- };
5.打开转盘锁 OJ链接
- class Solution {
- public:
- int openLock(vector
& deadends, string target) { - //利用哈希表的查找
- unordered_set
HashTable(deadends.begin(),deadends.end()) ; -
- if(HashTable.find("0000")!=HashTable.end())
- return -1;
- //标记已经搜索过的字符串
- unordered_set
visitHashTable; - visitHashTable.insert("0000");
-
- //利用队列进行广度优先遍历
- queue
q; - q.push("0000");
-
- int step=0;
- while(!q.empty())
- {
- int size=q.size();
-
- for(int i=0;i
- {
- string curString=q.front();
- q.pop();
-
- //如果此时curStringw为目标则直接返回
- if(curString==target)
- return step;
-
-
- for(int j=0;j
size();j++) - {
- string newString1=curString;
- string newString2=curString;
- //当前位置可以向前或者向后拨动
- newString1[j]=newString1[j]=='9'?'0':newString1[j]+1;
- newString2[j]=newString2[j]=='0'?'9':newString2[j]-1;
-
- if(HashTable.find(newString1)==HashTable.end()&&visitHashTable.find(newString1)==visitHashTable.end())
- {
- q.push(newString1);
- visitHashTable.insert(newString1);
- }
-
- if(HashTable.find(newString2)==HashTable.end()&&visitHashTable.find(newString2)==visitHashTable.end())
- {
- q.push(newString2);
- visitHashTable.insert(newString2);
- }
- }
- }
- step++;
- }
- return -1;
- }
- };
-
相关阅读:
24届好未来数开笔试
SpringMVC之综合案例:参数传递,向页面传参,页面跳转
【FAQ】安防视频监控平台EasyNVR无法控制云台,该如何解决?
React知识点系列(5)-每天10个小知识
数商云SCM系统实时订单协同与信息共享推动家居建材企业数字化转型
【ArcGIS风暴】CASS建立标准分幅图框并在ArcGIS中DOM批量分幅案例教程
WebGIS----前端(二)
Linux基础知识与实操-篇七:用户身份切换与特殊控制
毕业设计--基于SSM实现的的校园订餐系统源码+数据库
2023 IDC中国数字金融论坛丨中电金信向行业分享“源启+应用重构”新范式
-
原文地址:https://blog.csdn.net/qq_67458830/article/details/127712673