First knowledge of "backtracking algorithm" and analysis of LeetCode examples

Keywords: Algorithm leetcode

Backtracking algorithm

1. Concept of backtracking algorithm

xxxx backtracking algorithm is also called heuristics. It is a method to systematically search the solution of the problem. The basic idea of backtracking algorithm is: go forward from one way, go in if you can, go back if you can't, and try another way.
xxxx the above text is somewhat abstract, if it is embodied. The specific description of the idea of backtracking method is that the solution space of the problem is transformed into the structural representation of graph or tree, and then traversed by using the depth first search strategy. In the process of traversal, all feasible solutions or optimal solutions are recorded and found.

2. General problem solving ideas of backtracking algorithm

1. Define a solution space that contains the solution of the problem.

2. The solution space is organized by a method suitable for search.

3. The depth first method is used to search the solution space.

4. The bound function is used to avoid moving to the subspace where the solution is impossible.

Note: the solution space of the problem is usually generated dynamically in the process of searching the solution of the problem, which is an important feature of backtracking algorithm

3. Solutions to problems

xxxx for common problems, since we convert the solution space into depth first traversal (DFS) / post order traversal of multi fork tree, the most commonly used method is recursion!!

xxxx summary: we can get the final result after continuous "trying". The process of trying is to test each situation by enumerating and find out the results that meet the requirements of the topic.

Example 1: a path with a certain value in a binary tree

(1) Title Description

(2) Topic analysis

In fact, the problem of xxxx is relatively simple, because it is directly embodied or itself is a binary tree. Therefore, we can directly operate the binary tree without abstracting it into a concrete image and transforming a thing into a binary tree. For this problem, we need to start from the root node and follow the pre ordered traversal sequence of [root, left subtree, right subtree]. In preorder traversal, when we deal with the root, the processing content is the sum of the path values when we arrive at this node. Only when the node is a leaf node and the sum of path values is equal to the target value is one of the answers.

(3) Code implementation

class Solution {
	//Recursive subfunction
	//Parameter ret, because ret needs to change in real time, we need to pass a reference
	//Parameter ans, because each time we reach a node, we have to push the value into ans, but whether it is correct can be judged only when we reach the leaf node. When we backtrack, the previous path is not the next path. When we recurse back, the value should not change, so we have to pass the value
	//The parameter sum, the path value sum after reaching a node, is the same as ans and cannot be changed in real time
	//The target value is fixed, whether to use reference or not
    void pathSumHelper(TreeNode* root, vector<vector<int>>& ret, vector<int> ans, int sum, int& target)
    {
    	//If you access an empty node, you can return directly
        if(root == nullptr)
            return;
        //It is not an empty node. Handle this node (preorder traversal)
        sum += root->val;//sum + the node value
        ans.push_back(root->val);//push the node as a path to the current path ans
		//Start to judge. If it is a leaf node and the sum of path values = = target value, it is the correct result. push into ret
        if(root->left==nullptr &&root->right==nullptr && sum==target)
        {
            ret.push_back(ans);
            return;
        }
        //Deal with the left and right subtrees respectively
        pathSumHelper(root->left,ret,ans,sum,target);
        pathSumHelper(root->right,ret,ans,sum,target);
    }

public:
	//"Main function"
    vector<vector<int>> pathSum(TreeNode* root, int target) {
    	//The vector used to save the final result
        vector<vector<int>> ret;
        //Save each path, and it will follow recursion
        vector<int> ans;
        //Recursive subfunction
        //Parameters: the node to be checked, the final returned vector, each path, the sum of path values, and the sum of target paths
        pathSumHelper(root,ret,ans,0,target);
        return ret;
    }
};

Example 2: letter combination of telephone number

(1) Title Description

(2) Topic analysis

At first glance, the problem of xxxx may not be as direct as the previous problem, but it is not difficult to think that it is a backtracking algorithm.
The question of xxxx is to get all the situations, which is no different from trying one by one, but it doesn't need to judge whether it meets the conditions, just need to receive all the results of the attempt
xxxx for all situations, I think you learned "tree view" in junior high school - a tree structure used to deal with all possible situations and list all situations. This is the problem. In order to list all the situations, you can take the tree view. Let me give you an example to implement the tree view.

xxxx through this tree view, we can clearly understand that this is a tree structure. There are several numbers, which is equivalent to how many times it needs to be recursive. By traversing in the previous order, we can get all the results.

(3) Code implementation

//This is a small detail. We define a global vector < string >, and we can find all the corresponding letters through this vector
vector<string> character={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
class Solution {
public:
	//Recursive subfunction
    void _letterCombinations(const string& digits, vector<string>& ret, int k,string str)
    {
    	//If k equals the size of digits, it means that all numbers are taken and a path ends. The end of the first recursion is the exit of recursion,
        if(k == digits.size())
        {
            ret.push_back(str);
            return;
        }
        //Get number
        int num = digits[k]-'0';
        //The for loop traverses all letters of the corresponding number, and each letter corresponds to its own recursion to form a tree structure
        for(int i=0;i<character[num].size();i++)
        {
        	//Get the letter
           char ch = character[num][i];
//k+1 indicates the next number, str+ch, add the letter corresponding to this number to str and save the path
//Continue recursion_ letterCombinations(digits,ret,k+1,str+ch);
        }
    }
    //Main function
    vector<string> letterCombinations(string digits) {
    	//It also defines a string used to store each string
        string  str;
        //Store all results
        vector<string> ret;
        //If digits is empty, it returns directly
        if(digits.empty())
            return ret;
        //Parameter: digits: a string that stores numbers
        //ret stores the final result
        //0, the actual position of digits is 0
        //str, save each path, and finally push each complete path into ret
        _letterCombinations(digits,ret,0,str);
        return ret;
    }
};

Posted by thyscorpion on Tue, 21 Sep 2021 14:45:07 -0700