【矩阵路径】不知道回溯怎么写?进来看模板就对了!

【矩阵路径】不知道回溯怎么写?进来看模板就对了!

这几天做了几道回溯算法的题目,发现理解递归关键步骤的结果很重要,试图摸索出一套模板,思考的方法都是搭建好框架,然后逐步细想。先让我们看看道简单的回溯算法题目,一步步慢慢来:

1.矩阵路径

题目描述:

在这里插入图片描述
我们一步步来分析:

首先用两个for循环找到起始位置,代码如下:

 bool exist(vector<vector<char>>& board, string word) {
    
    
        if(word.empty())
        return true;

        rows=board.size();
        cols=board[0].size();
        for(int i=0;i<rows;i++)
        for(int j=0;j<cols;j++)
        {
    
    
            if(hasPathCore(board,word,i,j,0))
            return true;
        }
        return false;
    }

难理解的是hasPathCore这个回溯函数。

第一步,找出函数退出的条件(成功退出,失败退出)

失败的退出条件:越界,字符不匹配

 if(row>=rows || col>=cols || row<0 || col<0 ||word[pathLength]!=board[row][col])
        return false;

成功退出的条件:查找到底部(适用于绝大多数的回溯算法)

if(pathLength==word.size()-1)//查找到最后,或者已近查找过了返回真
        return true;

第二步,写出下一步递归的条件

此时,我们已经正确查找到了以一个字符的位置,接下来对他的上下左右进行查找,我们可以把它想象为一棵4叉树,就是每个节点有4个子节点,而树的遍历我们最容易想到的就是递归,我们来大概看一下:

boolean dfs(char[][] board, char[] word, int row, int col, int pathLength) {
    
    
    if (边界条件的判断) {
    
    
        return;
    }

    一些逻辑处理

    bool haspath;
    //往右
    haspath = dfs(board, word, i + 1, j, pathLength + 1)
    //往左
    ||dfs(board, word, i - 1, j, pathLength + 1)
    //往下
   	||dfs(board, word, i, j + 1, pathLength + 1)
    //往上
    ||dfs(board, word, i, j - 1, pathLength + 1);
    //上面4个方向,只要有一个能查找到,就返回true;


	 一些逻辑处理
    

    return res;
}

第三步,添加逻辑处理

此时我们的代码已经有大致框架了,我们单独调出三种状态了来跟着代码跑一边,开始状态,中间失败状态,结束状态
在这里插入图片描述
开始状态:找到第一个匹配的字符串,没问题。

中间失败状态:让dfs查找全部失败,即红色框框中。

此时代码逻辑变成回到黄框1,这就是回溯最重要的地方,接下来链接上黄框2执行其逻辑。

扫描二维码关注公众号,回复: 12395486 查看本文章

此时我们知道要防止其走回头路。并且,逻辑1和逻辑2操作都是相对的,入队和出队,更改和还原,理解关键的一点:让代码回溯到1的初始状态。我们在之间添加具体信息:

 bool hasPathCore(vector<vector<char>>& board, string word, int row,int col,int pathLength)
    {
    
    
        if(row>=rows || col>=cols || row<0 || col<0 ||word[pathLength]!=board[row][col])
        return false;
        
        if(pathLength==word.size()-1)
        //查找到最后,或者已近查找过了返回真
        return true;


        board[row][col]='\0';   
        //与之前的if语句对应,表示下个坐标为【i,j】的情况已近走过了,直接返回,防止接下来的语句搜索过程走回头路。

        bool haspath=hasPathCore(board,word,row-1,col,pathLength+1)
        ||hasPathCore(board,word,row,col-1,pathLength+1)
        ||hasPathCore(board,word,row+1,col,pathLength+1)
        ||hasPathCore(board,word,row,col+1,pathLength+1);  
        //千万不能写pathLength++!!

        board[row][col]=word[pathLength]; 
        //因为只代表此次搜索过程中,该元素已访问过,当初始cow,col变化时,又开始了另一次搜索过程(假word=bfsa的情况辅助理解)

        return haspath;  
    }
  • board[row][col]=’\0’ 表示【row,col】已近走过,防止下一次走回头路
  • board[row][col]=word[pathLength] 表示【row,col】周围查找全部失败,将【row,col】状态还原,防止下个起点查找到该点位出错。

结束状态:查找到最后一个字符串且全部成功,函数直接返回真,没问题。

第四步,恭喜你!

完整代码:

class Solution {
    
    
public:
int rows=0;
int cols=0;

    bool exist(vector<vector<char>>& board, string word) {
    
    
        if(word.empty())
        return true;

        rows=board.size();
        cols=board[0].size();
        for(int i=0;i<rows;i++)
        for(int j=0;j<cols;j++)
        {
    
    
            if(hasPathCore(board,word,i,j,0))
            return true;
        }
        return false;
    }
    bool hasPathCore(vector<vector<char>>& board, string word, int row,int col,int pathLength)
    {
    
    
        if(row>=rows || col>=cols || row<0 || col<0 ||word[pathLength]!=board[row][col])
        return false;
        
        if(pathLength==word.size()-1)//查找到最后,或者已近查找过了返回真
        return true;


        board[row][col]='\0';   //与之前的if语句对应,表示下个坐标为【i,j】的情况已近走过了,直接返回,防止接下来的语句搜索过程走回头路。

        bool haspath=hasPathCore(board,word,row-1,col,pathLength+1)||hasPathCore(board,word,row,col-1,pathLength+1)||hasPathCore(board,word,row+1,col,pathLength+1)||hasPathCore(board,word,row,col+1,pathLength+1);  //千万不能写pathLength++!!

        board[row][col]=word[pathLength]; //因为只代表此次搜索过程中,该元素已访问过,当初始i j变化时,又开始了另一次搜索过程(假设word=bfsa的情况辅助理解)

        return haspath;  
    }
};

其他的题目也是同理,希望对大家有帮助。

电话号码的字母组合

在这里插入图片描述

class Solution {
    
    
public:
    vector<string> letterCombinations(string digits) {
    
    
        vector<string> combinations;

        if (digits.empty()) {
    
    
            return combinations;
        }

        unordered_map<char, string> phoneMap{
    
    
            {
    
    '2', "abc"},
            {
    
    '3', "def"},
            {
    
    '4', "ghi"},
            {
    
    '5', "jkl"},
            {
    
    '6', "mno"},
            {
    
    '7', "pqrs"},
            {
    
    '8', "tuv"},
            {
    
    '9', "wxyz"}
        };

        string combination;

        backtrack(combinations, phoneMap, digits, 0, combination);

        return combinations;
    }

    void backtrack(vector<string>& combinations, const unordered_map<char, string>& phoneMap, const string& digits, int index, string& combination) {
    
    
       
        if (index == digits.length()) 
        {
    
    
            combinations.push_back(combination);
        } 
        
        else 
        {
    
    
            char digit = digits[index];

            const string& letters = phoneMap.at(digit);

            for (const char& letter: letters) 
            {
    
    
                combination.push_back(letter);
                backtrack(combinations, phoneMap, digits, index + 1, combination);
                combination.pop_back();
            }
        }
        
    }
};

猜你喜欢

转载自blog.csdn.net/cckluv/article/details/110356481
今日推荐