[Algorithm Series] Recursion, Search and Backtracking (3)

Insert image description here

Preface

I have shared two articles with you before about issues related to recursion, search and backtracking, but the first two articles only covered recursion, and search and backtracking were basically not covered. Don’t worry, the following articles will share with you about them. Search and review related knowledge and topics. Today's article mainly involves the pruning problem in the recursive process.

What is binary tree pruning?

Binary tree pruning refers to the process of improving the quality of a binary tree by pruning certain subtrees. Specifically, binary tree pruning can include the following situations:

  1. Prune all empty subtrees in the binary tree: When empty subtrees exist in the binary tree, these empty subtrees will not have any impact on the performance of the entire binary tree, so they can all be pruned.
  2. Prune duplicate subtrees in a binary tree: When duplicate subtrees exist in a binary tree, these duplicate subtrees will have a negative impact on the performance of the entire binary tree, so they can all be pruned.
  3. Prune unnecessary subtrees in the binary tree: When there are some unnecessary subtrees in the binary tree, these subtrees will not have any impact on the performance of the entire binary tree, so they can all be pruned.

Through binary tree pruning, the performance and efficiency of the binary tree can be improved, making it more suitable for solving practical problems.

In fact, binary tree pruning is not difficult. We only need to make appropriate judgments during the recursive process to achieve the purpose of pruning.

1. Binary tree pruning

https://leetcode.cn/problems/binary-tree-pruning/

1.1 Question requirements

You are given the root node root of a binary tree. In addition, the value of each node in the tree is either 0 or 1.

Returns the original binary tree with all subtrees not containing 1 removed.

The subtree of node node is node itself plus all node's descendants.

Example 1:
Insert image description here

输入:root = [1,null,0,0,1]
输出:[1,null,0,null,1]
解释:
只有红色节点满足条件“所有不包含 1 的子树”。 右图为返回的答案。

Example 2:
Insert image description here

输入:root = [1,0,1,0,0,0,1]
输出:[1,null,1,null,1]

Example 3:
Insert image description here

输入:root = [1,1,0,1,1,0,1,0]
输出:[1,1,0,1,1,null,1]

hint:

树中节点的数目在范围 [1, 200] 内
Node.val 为 0 或 1
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    public TreeNode pruneTree(TreeNode root) {
    
    

    }
}

1.2 Ideas for solving questions

To do recursion well, we need to solve micro problems from a macro perspective. First, let’s determine whether the node given to us is null. If so, return null directly. If not, pass the left subtree and right subtree of the root node to the function respectively. Through this function, we do not need to know the specific details of this function. Details, we just need to believe that he will be able to help us complete the pruning operation. After the left and right subtrees of the root node have completed the pruning operation, a judgment is made. If the left and right subtrees of the root node are both null and the value of the root node is 0, then the root node can be set to null and then return to root. .

Insert image description here
Insert image description here

1.3 Code implementation

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    public TreeNode pruneTree(TreeNode root) {
    
    
        if (root == null) return null;
        root.left = pruneTree(root.left);
        root.right = pruneTree(root.right);
        if (root.left == null && root.right == null) {
    
    
            if (root.val == 0) root = null;
        }
        return root;
    }
}

Insert image description here

2. Verify binary search tree

https://leetcode.cn/problems/validate-binary-search-tree/

2.1 Question requirements

Given the root node root of a binary tree, determine whether it is a valid binary search tree.

A valid binary search tree is defined as follows:

The left subtree of the node only contains numbers less than the current node.
The right subtree of the node only contains numbers greater than the current node.
All left and right subtrees must themselves be binary search trees.

Example 1:
Insert image description here

输入:root = [2,1,3]
输出:true

Example 2:
Insert image description here

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

hint:

树中节点数目范围在[1, 104] 内
-231 <= Node.val <= 231 - 1
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    public boolean isValidBST(TreeNode root) {
    
    

    }
}

2.2 Ideas for solving questions

We all know that a binary search tree is any binary tree. If the left and right children exist, then the value of the node's left child node is less than the value of the node, and the value of the node is less than the value of the node's right child node, that is Say: If a binary search tree uses inorder traversal, it will get a number in ascending order. So in this question, how do we determine that the value of a node's left child node is less than the value of the node, and the value of the right child node is greater than the value of the node?

We can use the preorder traversal method to first find the smallest node in the binary search tree, and then use prev to record this value. When returning, we first determine whether the left subtree of the node conforms to the binary search tree. If it does not, You can directly return false. If it matches, you need to compare the value of prev with the val of root. If prev < root.val, then replace the value of prev with the value of the current node, and continue to determine the right child of the node. Whether the tree is a binary search tree.

Insert image description here
Insert image description here

2.3 Code implementation

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    long prev = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
    
    
        if (root == null) return true;
        boolean l = isValidBST(root.left);
        if (l == false) return false;
        if (root.val > prev) prev = root.val;
        else return false;
        boolean r = isValidBST(root.right);

        return l && r;
    }
}

Insert image description here

3. The kth smallest element in the binary search tree

https://leetcode.cn/problems/kth-smallest-element-in-a-bst/

3.1 Question requirements

Given the root node root of a binary search tree, and an integer k, please design an algorithm to find the kth smallest element (counting from 1).

Example 1:
Insert image description here

输入:root = [3,1,4,null,2], k = 1
输出:1

Example 2:
Insert image description here

输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3

hint:

树中的节点数为 n 。
1 <= k <= n <= 104
0 <= Node.val <= 104
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    public int kthSmallest(TreeNode root, int k) {
    
    

    }
}

3.2 Ideas for solving questions

This problem can be solved using a priority queue, but in order to enhance the use of recursion, we do not use a priority queue, but use recursion to solve this problem. According to the characteristics of the binary tree, if we want to find the kth smallest value in the binary search tree elements, we can use the method of in-order traversal of the binary search tree, and use the global variable count to record the smallest element of the currently traversed node, and use a global variable ret to record the k-th smallest element, in-order When traversing, if a node is not traversed, the count will be -. If the count is 0, it means that the element has been found.

In recursion, there are some situations where using global variables can make our code very simple.

3.3 Code implementation

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    int count, ret;
    public int kthSmallest(TreeNode root, int k) {
    
    
        count = k;
        dfs(root);
        return ret;
    }

    private void dfs(TreeNode root) {
    
    
        if (count == 0 || root == null) return;
        dfs(root.left);
        count--;
        if (count == 0) {
    
    
            ret = root.val;
            return;
        }
        dfs(root.right);
    }
}

Insert image description here

4. All paths in the binary tree

https://leetcode.cn/problems/binary-tree-paths/

4.1 Question requirements

Given the root node root of a binary tree, in any order, return all paths from the root node to the leaf nodes.

Leaf nodes refer to nodes that have no child nodes.

Example 1:
Insert image description here

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

Example 2:

输入:root = [1]
输出:["1"]

hint:

树中节点的数目在范围 [1, 100] 内
-100 <= Node.val <= 100
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    public List<String> binaryTreePaths(TreeNode root) {
    
    

    }
}

4.2 Ideas for solving questions

In this question, we can use preorder traversal to splice the values ​​of all nodes on the path to the back of the string. When a leaf node is encountered, the string is added to the set and then returned. But what about on the return? We need to remove the value of a previously added node.

Insert image description here
But that’s not all. Looking at the title, we can find that -> is also needed to connect between nodes, so when do we remove -> and the value of the previous point, and when do we just remove it? The value of the node. If the string uses a global variable, backtracking (restoring the scene) will be more troublesome. Therefore, for this question, we can pass the string as a parameter to the function, so that when returning, the parameter will be automatically returned. How it looked before.

4.3 Code implementation

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
	//全局的集合变量用来存储二叉树所有路径上的值
    List<String> list;
    public List<String> binaryTreePaths(TreeNode root) {
    
    
        list = new ArrayList<>();
        //因为String的拼接需要重新创建对象,速度比较慢,所以我们字符串拼接就使用StringBuilder
        dfs(root, new StringBuilder());
        return list;
    }

    private void dfs(TreeNode root, StringBuilder s) {
    
    
        if (root == null) return;
        //因为StringBuilder的变化不会因为函数的返回而恢复,所以这里我们创建一个临时的StringBuidler类
        StringBuilder sb = new StringBuilder(s);
        sb.append(root.val);
        if (root.left == null && root.right == null) {
    
    
            list.add(sb.toString());
            return;
        }
        //如果当前节点不是叶子节点,那么就加上->
        sb.append("->");
        dfs(root.left, sb);
        dfs(root.right, sb);
    }
}

Insert image description here

Guess you like

Origin blog.csdn.net/m0_73888323/article/details/134981933