Figure 13-1 Search for nodes in preorder traversal
## 13.1.1 Attempt and Backtrack **The reason it is called a backtracking algorithm is that it employs "attempt" and "backtrack" strategies when searching the solution space**. When the algorithm encounters a state where it cannot continue forward or cannot find a solution that satisfies the constraints, it will undo the previous choice, return to a previous state, and try other possible choices. For Example 1, visiting each node represents an "attempt", while skipping over a leaf node or a function `return` from the parent node represents a "backtrack". It is worth noting that **backtracking is not limited to function returns alone**. To illustrate this, let's extend Example 1 slightly. !!! question "Example 2" In a binary tree, search all nodes with value $7$, **and return the paths from the root node to these nodes**. Based on the code from Example 1, we need to use a list `path` to record the visited node path. When we reach a node with value $7$, we copy `path` and add it to the result list `res`. After traversal is complete, `res` contains all the solutions. The code is as follows: === "Python" ```python title="preorder_traversal_ii_compact.py" def pre_order(root: TreeNode): """Preorder traversal: Example 2""" if root is None: return # Attempt path.append(root) if root.val == 7: # Record solution res.append(list(path)) pre_order(root.left) pre_order(root.right) # Backtrack path.pop() ``` === "C++" ```cpp title="preorder_traversal_ii_compact.cpp" /* Preorder traversal: Example 2 */ void preOrder(TreeNode *root) { if (root == nullptr) { return; } // Attempt path.push_back(root); if (root->val == 7) { // Record solution res.push_back(path); } preOrder(root->left); preOrder(root->right); // Backtrack path.pop_back(); } ``` === "Java" ```java title="preorder_traversal_ii_compact.java" /* Preorder traversal: Example 2 */ void preOrder(TreeNode root) { if (root == null) { return; } // Attempt path.add(root); if (root.val == 7) { // Record solution res.add(new ArrayList<>(path)); } preOrder(root.left); preOrder(root.right); // Backtrack path.remove(path.size() - 1); } ``` === "C#" ```csharp title="preorder_traversal_ii_compact.cs" /* Preorder traversal: Example 2 */ void PreOrder(TreeNode? root) { if (root == null) { return; } // Attempt path.Add(root); if (root.val == 7) { // Record solution res.Add(new ListFigure 13-2 Attempt and backtrack
## 13.1.2 Pruning Complex backtracking problems usually contain one or more constraints. **Constraints can typically be used for "pruning"**. !!! question "Example 3" In a binary tree, search all nodes with value $7$ and return the paths from the root node to these nodes, **but require that the paths do not contain nodes with value $3$**. To satisfy the above constraints, **we need to add pruning operations**: during the search process, if we encounter a node with value $3$, we return early and do not continue searching. The code is as follows: === "Python" ```python title="preorder_traversal_iii_compact.py" def pre_order(root: TreeNode): """Preorder traversal: Example 3""" # Pruning if root is None or root.val == 3: return # Attempt path.append(root) if root.val == 7: # Record solution res.append(list(path)) pre_order(root.left) pre_order(root.right) # Backtrack path.pop() ``` === "C++" ```cpp title="preorder_traversal_iii_compact.cpp" /* Preorder traversal: Example 3 */ void preOrder(TreeNode *root) { // Pruning if (root == nullptr || root->val == 3) { return; } // Attempt path.push_back(root); if (root->val == 7) { // Record solution res.push_back(path); } preOrder(root->left); preOrder(root->right); // Backtrack path.pop_back(); } ``` === "Java" ```java title="preorder_traversal_iii_compact.java" /* Preorder traversal: Example 3 */ void preOrder(TreeNode root) { // Pruning if (root == null || root.val == 3) { return; } // Attempt path.add(root); if (root.val == 7) { // Record solution res.add(new ArrayList<>(path)); } preOrder(root.left); preOrder(root.right); // Backtrack path.remove(path.size() - 1); } ``` === "C#" ```csharp title="preorder_traversal_iii_compact.cs" /* Preorder traversal: Example 3 */ void PreOrder(TreeNode? root) { // Pruning if (root == null || root.val == 3) { return; } // Attempt path.Add(root); if (root.val == 7) { // Record solution res.Add(new ListFigure 13-3 Pruning according to constraints
## 13.1.3 Framework Code Next, we attempt to extract the main framework of backtracking's "attempt, backtrack, and pruning", to improve code generality. In the following framework code, `state` represents the current state of the problem, and `choices` represents the choices available in the current state: === "Python" ```python title="" def backtrack(state: State, choices: list[choice], res: list[state]): """Backtracking algorithm framework""" # Check if it is a solution if is_solution(state): # Record the solution record_solution(state, res) # Stop searching return # Traverse all choices for choice in choices: # Pruning: check if the choice is valid if is_valid(state, choice): # Attempt: make a choice and update the state make_choice(state, choice) backtrack(state, choices, res) # Backtrack: undo the choice and restore to the previous state undo_choice(state, choice) ``` === "C++" ```cpp title="" /* Backtracking algorithm framework */ void backtrack(State *state, vector