Code Capriccio record: binary tree (Part II)

Posted by plouka on Sun, 06 Feb 2022 19:21:09 +0100

236. Nearest common ancestor of binary tree

Title Description:

Train of thought analysis:
Anyway, I'm still confused about recursion... More practice can only be. The thought of boss Carl can still be understood. It's really unexpected when he did it himself.

The code is as follows:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //We call the current method as a recursive function (post order traversal)
        //As soon as the method comes in, we judge whether it is p and q we want, or null
        //If it is one of these cases, we will return the current result upward
        if(root == p || root == q || root == null) return root;
        //Recursively look up the left tree and receive the result with a left pointer
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        //Recursively look up the right tree and receive the result with a right pointer
        TreeNode right = lowestCommonAncestor(root.right,p,q);

        //If left and right are not empty, it means that the current node is the nearest common ancestor traced back
        if(left != null && right != null) return root;
        //If left is empty and right is not empty, it means it is found in the right tree
        //vice versa
        if(left == null && right != null) return right;
        else if(left != null && right == null) return left;
        else {//If both are empty, it means that the nearest common ancestor has not been found
            return null;
        }
    }
}

235. Nearest common ancestor of binary search tree

Title Description:

Train of thought analysis:
The binary search tree is also a binary tree, so we can use the backtracking solution of the binary search tree just now. It's the same:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    //This can also be done according to the practice of ordinary binary tree
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == p || root == q || root == null) return root;

        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);

        if(left != null && right != null) return root;

        if(left != null && right == null) return left;
        else if(left == null && right != null) return right;
        else return null;
    }
}

However, because it is a binary search tree BST, we can also have another solution, which is to use the characteristic that the left tree is smaller than the root node and the right tree is larger than the root node. When the values of p and q nodes are less than the value of the current root node, we will traverse the left tree to find the nearest public node, otherwise, we can search to the right. If the values of p and q nodes are greater than the value of the current node, it means that they are the nearest public node and directly return the result. The recursive method is as follows:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //Directly return the result of recursive function
        return fun(root,p,q);
    }
    //Recursive function
    public TreeNode fun(TreeNode root,TreeNode p,TreeNode q){
        //If you enter, it will be judged as empty. If root is empty, it will directly return null
        if(root == null) return null;
        //Because the binary search tree is ordered, we can carry out unilateral recursion according to this feature
        //If the current node value is greater than the values of p and q, we will recurse to the left
        if(root.val > p.val && root.val > q.val){
            TreeNode left = fun(root.left,p,q);
            //If left is not empty, you can return the answer directly
            if(left != null) return left;
        }
        //If the current node value is less than the values of p and q, we will recurse to the right
        if(root.val < p.val && root.val < q.val){
            TreeNode right = fun(root.right,p,q);
            //If right is not empty, we can return the answer directly if we find it
            if(right != null) return right;
        }
        //If neither of the above conditions is satisfied
        //This indicates that the value of the pq node and the value of the current node root are larger or smaller
        //In this case, pq one is in the left subtree and the other is in the right subtree
        //Then the current node must be the nearest public ancestor
        //Directly return to the current node root
        return root;
    }
}

The iterative method is as follows:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //Because the topic says that pq nodes must exist, so there must be public ancestors recently
        //Therefore, we do not need to judge whether the left and right nodes are empty
        //Because we can find the answer before we meet the empty node
        while(true){
            //The values of p and q are less than the value of the current node root
            if(root.val > p.val && root.val > q.val){
                //So let's iterate to the left
                root = root.left;
            }else if(root.val < p.val && root.val < q.val){
                root = root.right;
            }else {//If there is only one large and one small case left, the current node is the nearest common ancestor
                //Terminate cycle
                break;
            }
        }
        //Returns the current node as the answer
        return root;
    }
}

701. Insert operation in binary search tree

Title Description:

Train of thought analysis:
We can redraw (construct) the binary tree, and then insert the value we want to insert when we encounter an empty node (hanging at the end is the most convenient method). It will be easier to understand the specific code.

The code is as follows:

/**
 * 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 insertIntoBST(TreeNode root, int val) {
        //According to the characteristics of binary search tree
        //It's kind of like reconstructing this binary tree once
        //If a null value is encountered during construction, the upward return will be inserted into the
        //Hanging in the newly reconstructed binary tree
        if(root == null){
            TreeNode node = new TreeNode(val);
            return node;
        }
        //If the current node value is greater than the inserted value, recurse to the left
        if(root.val > val) root.left = insertIntoBST(root.left,val); 
        //If the current node value is less than the inserted value, recurse to the right
        if(root.val < val) root.right = insertIntoBST(root.right,val);
        //Returns the current node
        return root;
    }
}

450. Delete nodes in binary search tree

Title Description:

Train of thought analysis:
Boss Carl said it in detail. Just look at it. The comments in my code are also very clear.

The code is as follows:

/**
 * 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 deleteNode(TreeNode root, int key) {
        //Air judgment
        if(root == null) return null;
        //If the value of the current node is equal to the value of the node to be deleted
        if(root.val == key){
            //If the left subtree of the current node is empty, we return the right subtree to the upper layer
            //In this way, when the upper function reconstructs the binary tree, it will directly hang a tree
            if(root.left == null) return root.right;
            //If the right subtree of the current node is empty, we return the left subtree to the upper layer
            else if(root.right == null) return root.left;
            //Otherwise, the left and right subtrees are not empty
            else{
                //Record the right subtree of the current node with cur pointer
                TreeNode cur = root.right;
                //Loop through the leftmost leaf node of the right subtree of the current node
                //Because you have to hang a subtree up when you go back
                while(cur.left != null){
                    cur = cur.left;
                }
                //Then we hang the left tree of the current node to the right tree of the current node
                //At the bottom of the left (combined with the moving picture of boss Carl)
                cur.left = root.left;
                //Finally, let the current node point to its right tree
                root = root.right;
                //Return the current node, and then the upper function will hang it in the
                //We are on the regenerated tree
                return root;
            }
        }
        //The following is the case of not equal to, so you can continue recursive processing
        //If the value of the current node is greater than the target deletion value, the left recursive deletion is performed
        if(root.val > key) root.left = deleteNode(root.left,key);
        //If the value of the current node is less than the target deletion value, right recursive deletion is performed
        if(root.val < key) root.right = deleteNode(root.right,key);
            
        //Return the current node, and the upper function is also used to hang on the tree
        //The last returned node is the root node of the new tree we reconstructed
        return root;
    }
}

669. Pruning binary search tree

Title Description:

Train of thought analysis:
You can understand it by reading the code.

The code is as follows:

/**
 * 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 trimBST(TreeNode root, int low, int high) {
        //Air judgment
        if(root == null) return null;
        
        //The following two IFS are equivalent to deleting nodes that do not meet the requirements
        //If the value of the current node is less than low, it will recursively search the right tree with large value
        if(root.val < low){
            //Because there may be qualified values in the right subtree of the current node, we have to get them in
            return trimBST(root.right,low,high);
        }
        //If the value of the current node is greater than high, the left tree with smaller value will be searched recursively
        if(root.val > high){
            //Because the left subtree of the current node may have values that meet the conditions, we have to get them in
            return trimBST(root.left,low,high);
        }
        //Processing normal nodes
        root.left = trimBST(root.left,low,high);
        root.right = trimBST(root.right,low,high);
        //Return the current node to the previous function call and hang it on the new tree
        return root;
    }
}

108. Convert an ordered array into a binary search tree

Title Description:

Train of thought analysis:
You can understand it by reading the code. In fact, it is very similar to the previous topic and the implementation method is very similar.

The code is as follows:

/**
 * 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 sortedArrayToBST(int[] nums) {
        //Do it recursively according to the rules of middle order traversal
        return fun(nums,0,nums.length-1);//Generally, the array length is - 1, and the same is true for bisection
    }
    //Recursive function
    public TreeNode fun(int[] nums,int left,int right){
        //If the array has no value, terminate and return null directly
        if(left > right) return null;
        //Take the intermediate value
        //The following mid writing method can avoid array out of bounds
        int mid = left + ((right - left) / 2);
        //Create root node
        TreeNode root = new TreeNode(nums[mid]);
        //Then recurse to the left
        root.left = fun(nums,left,mid-1);
        //Recursion to the right
        root.right = fun(nums,mid+1,right);
        //Return the created node to the upper function for mounting
        return root;
   }
}

538. Convert binary search tree into cumulative tree

Title Description:

Train of thought analysis:
The meaning of the question is a little confused to tell the truth. In fact, it can be understood from another point of view. It is to get the order after the middle order traversal, and then accumulate the node value in reverse and assign it to the original node.
The idea is summarized as follows: traverse the BST in reverse middle order, then accumulate the values of each node with a global variable, and then re assign the value to the node.

The code is as follows:

/**
 * 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 {
    //To tell you the truth, I'm a little confused
    //It is to get the order after the middle order traversal, and then accumulate the node value in reverse and assign it to the original node
    //Idea: traverse the BST in reverse order, and then use a global variable to accumulate the values of each node, and then assign a new value to the node
    int sum;
    public TreeNode convertBST(TreeNode root) {
        //The reverse middle order is traversed in the order of right, middle and left
        //Initialize sum
        sum = 0;
        //Call recursive function
        fun(root);
        //Returns the root of the tree
        return root;
    }
    //Recursive function
    public void fun(TreeNode root){
        //If it is blank, because there is no return value, the function can be terminated directly if it is blank
        if(root == null) return;
        
        //Right recursion
        fun(root.right);
        
        //Intermediate processing accumulation link
        sum = sum + root.val;
        root.val = sum;

        //Left recursion
        fun(root.left);
    }
}

summary

So that's the end of the binary tree. Shit, recursion is too difficult. Shit.

Topics: data structure leetcode Dynamic Programming