[Matrix Path] I don’t know how to write backtracking? Just look at the template!

[Matrix Path] I don’t know how to write backtracking? Just look at the template!

I have done a few backtracking algorithm topics in the past few days and found that it is very important to understand the results of the key steps of recursion. I tried to find a set of templates. The way to think is to build a good framework, and then gradually think about it. Let us first look at a simple backtracking algorithm problem, step by step slowly:

1. Matrix path

Title description:

Insert picture description here
Let's analyze step by step:

First find the starting position with two for loops, the code is as follows:

 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;
    }

What is difficult to understand is the traceback function hasPathCore.

The first step is to find out the conditions for function exit (successful exit, failed exit)

Failed exit condition: out of bounds, character mismatch

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

Conditions for successful exit: find to the bottom (applicable to most backtracking algorithms)

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

The second step is to write the conditions for the next recursion

At this point, we have correctly found the position of a character, and then we can look for it up, down, left, and right. We can imagine it as a quad tree, that is, each node has 4 child nodes, and the tree traverses us The easiest thing to think of is recursion, let's take a look:

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;
}

The third step is to add logical processing

At this point, our code has a general framework. We individually called out three states to follow the code, starting state, intermediate failure state, and ending state .
Insert picture description here
Start state : find the first matching string, no problem.

Intermediate failure status : let dfs find all failed, that is, the red box.

At this time, the code logic changes back to the yellow box 1, which is the most important part of the backtracking, and then link to the yellow box 2 to execute its logic.

At this point we know to prevent it from turning back. And, logic 1 and logic 2 operations are relative, enqueue and dequeue, change and restore, understand the key point: let the code go back to the initial state of 1. We add specific information between:

 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' means [row, col] has been close to prevent it from going back next time
  • board[row][col]=word[pathLength] means that all searches around [row, col] have failed, and the state of [row, col] is restored to prevent errors in the next starting point to find this point.

End state : Find the last string and all succeed, the function returns true directly, no problem.

The fourth step, congratulations!

Complete code:

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;  
    }
};

The same is true for other topics, I hope it will help you all.

Letter combination of phone number

Insert picture description here

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();
            }
        }
        
    }
};

Guess you like

Origin blog.csdn.net/cckluv/article/details/110356481