回溯法可以看成蛮力法的升级版,他从解决问题每一步的所有可能选项里系统地选择出一个可行的解决方案。回溯法适合由多个步骤组成的问题,并且每个步骤都有多个选项。当我们在某一步选择了其中一个选项时,就进入下一步,然后又面临新的选项。重复选择,直至到达最终的状态。
用回溯法解决的问题的所有选项可以形象地用树状结构表示。在某一步有n个可能的选项,那么该步骤可以看成是树状结构中的一个节点,每个选项看成树中节点连接线,经过这些连接线到达该节点的n个子节点。树的叶节点对应着终结状态。如果在叶节点的状态满足题目的约束条件,那么找到了一个可行的解决方案。
如果叶节点的状态不满足约束条件,那么只好回溯到它的上一个节点再尝试其他的选项。如果上一个节点所有可能选项都已试过,并且不能到达满足约束条件的终结状态,则再次回溯到上一个节点。如果所有节点的所有选项都已尝试过仍然不能到达满足约束条件的终结状态,则该问题无解。
通常回溯法算法适合用递归实现代码。当我们到达某一个节点时,尝试所有可能的选项并在满足条件的前提下递归下一个节点。
矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中任意一格开始,每一步可以在矩阵中向左右上下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如, 在3*4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用下划线标出)。但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
bool hasPath(char* matrix, int rows, int cols, char* str){
if(matrix == nullptr||rows < 1||cols<1||str==nullptr)
return false;
bool* visited = new bool[rows*cols];
memset(visited, 0, rows*cols);
int pathLength = 0;
for(int row = 0;row < rows;++row) {
for(int col = 0;col < cols;++col) {
if(hasPathCore(matrix,rows,cols,row,col,str, pathLength,visited))
return true;
}
}
delete[] visited;
return false;
}
bool hasPathCore(const char* matrix,int rows,int cols,int row, int col,
const char* str,int& pathLength,bool* visited) {
if(str[pathLength] == '\0')
return true;
bool hasPath = false;
if(row>=0&&row<rows&&col>=0&&col<cols
&&matrix[row*cols+col]==str[pathLength]&&!visited[row*cols+col])
{
++pathLength;
visited[row*cols+col] = true;
hasPath = hasPathCore(matrix,rows,cols,row,col-1,str,pathLength,visited)
||hasPathCore(matrix,rows,cols,row,col+1,str,pathLength,visited)
||hasPathCore(matrix,rows,cols,row-1,col,str,pathLength,visited)
||hasPathCore(matrix,rows,cols,row+1,col,str,pathLength,visited);
if(!hasPath)
{
--pathLength;
visited[row*cols+col] = false;
}
}
return hasPath;
}
**
测试用例:
**
功能测试(多行多列矩阵中存在或不存在路径)
边界值测试(矩阵只有一行或一列;矩阵和路径中的所有字母都是相同的)
特殊输入测试(输入nullptr指针)