Binary tree topic summary

Posted by gevo12321 on Fri, 08 Oct 2021 10:11:48 +0200

1. Iterative traversal of tree

Preorder traversal

thinking

Use stack as storage structure. Each time, first put the root node in, then take out the root node, then put the right child node of the root node in, and then put the left child node. Next time, the root node of the left subtree is taken out, and then the right child node of the left subtree is put in, and then the left child node of the left subtree is put in. And so on. It is found that its processing order is about middle. Only by putting the root node in first, taking out the root node, and then putting the right child node and the left child node, can we ensure that the processing order is the order of previous traversal.

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
         if(root==null){
               return new ArrayList<Integer>();
          }
           List<Integer> res=new ArrayList<>();
           Stack<TreeNode> stack=new Stack<>();
           stack.push(root);
           while(!stack.isEmpty()){
               TreeNode node=stack.pop();
               res.add(node.val);
               if(node.right!=null){
                   stack.push(node.right);
               }
               if(node.left!=null){
                   stack.push(node.left);
               }
           }
           return res;

    }
}

Medium order traversal

thinking

Templates that cannot be traversed directly by preorder because the point taken out by preorder traversal is just the middle point and the point to be accessed. However, the point to be accessed in the middle order traversal is not the middle point. The middle point is that there is no left subtree only when the leftmost node is accessed. Then the leftmost node popped out of the stack is the middle node to be processed, and then the traversal starts to the left of the right subtree. The essence is left, middle and right

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res=new ArrayList<>();
        
        if(root==null){
            return res;
        }
        
            TreeNode cur=root;
            Stack<TreeNode> stack=new Stack<>();
            
            while(cur!=null||!stack.isEmpty()){
                if(cur!=null){
                    stack.push(cur);
                    cur=cur.left;
                }else{
                    cur=stack.peek();
                    stack.pop();
                    res.add(cur.val);
                    cur=cur.right;
                }
            }

            return res;

    }
}

Postorder traversal

thinking

The preorder traversal is around the middle, so the traversal traversal is actually the left and right sides. It only needs to change the traversal of the right left in the preorder traversal. Then the result of the whole list transformation is the result of post traversal.

expand

root may be null, which needs to be judged in advance.

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
          List<Integer> res=new ArrayList<>();
          if(root==null){
              return res;
          }

         Stack<TreeNode> stack=new Stack<>();
          stack.push(root);
          while(!stack.isEmpty()){
              TreeNode node=stack.pop();
              res.add(node.val);
              if(node.left!=null){
                  stack.push(node.left);
              }

              if(node.right!=null){
                  stack.push(node.right);
              }
          }
          Collections.reverse(res);
          return res;
    }
}

Unified traversal mode

thinking

① The marking method is to stack in the reverse order of the traversal order, and mark the intermediate nodes to be processed. Next time, as long as the tag is traversed, it will prove that this is the processing node.

② To see the essence of the template is to modify the location of the processing node (mark)

Preorder traversal template

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res=new ArrayList<>();
        if(root==null) return res;
        
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node=stack.peek();
            if(node!=null){
                stack.pop();//Do not repeat
                
                //
               //  stack.push(node); Post order
               // stack.push(null);
                if(node.right!=null) stack.push(node.right);
                
                stack.push(node);//Middle order
                stack.push(null);
                
                if(node.left!=null) stack.push(node.left);
                
                //  stack.push(node); Preamble
               // stack.push(null);
                
            }else{
                stack.pop();
                TreeNode temp=stack.peek();
                stack.pop();
                res.add(temp.val);
                
            }
        }
        return res;
    }
}

level traversal

thinking

Use the first in, first out of the queue, first in all nodes of one layer, then traverse, obtain the child nodes of all nodes, and traverse again. And so on. The question is, how do you know when the first floor is? It needs to be limited by a size. The advanced queue is the root node. Naturally, the size is 1, then pop up the root node, and then traverse the first layer and obtain the node of the second layer, that is, the child node of the root node. Finally, you will find that there are only layer 2 nodes in the queue. The same is true below. After calculating the size of each layer, you can obtain the child nodes.

Summary: sequence traversal is from beginning to end, in line with the queue of first in first out, that is, who comes first and who handles first. The size of each layer can be limited by the queue size before adding new child nodes.

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
         Queue<TreeNode> queue=new LinkedList<>();
         List<List<Integer>> res=new ArrayList<>();
         if(root==null) return res;
         
         queue.offer(root);
         
         while(!queue.isEmpty()){
             int len=queue.size();
             List<Integer> list=new ArrayList<>();
             while(len>0){
                 TreeNode node=queue.poll();
                 list.add(node.val);
                 if(node.left!=null) queue.offer(node.left);
                 if(node.right!=null) queue.offer(node.right);
                 len--;
             }
             res.add(list);
         }
         return res;
    }
}

2. Reverse binary tree

thinking

① Preorder traversal commutative left subtree and right subtree

② Postorder traversal

③ Middle order traversal will cause the tree to be exchanged twice and can not be reversed

④ Sequence traversal is also a direct exchange of left subtree and right subtree

class Solution {
    public TreeNode invertTree(TreeNode root) {
       Queue<TreeNode> queue=new LinkedList<>();
       if(root==null) return root;
       queue.add(root);
       while(!queue.isEmpty()){
           int size=queue.size();
           while(size-->0){
               TreeNode node=queue.poll();
               TreeNode temp=node.left;
               node.left=node.right;
               node.right=temp;
               if(node.left!=null) queue.offer(node.left);
               if(node.right!=null) queue.offer(node.right);
           }
       }
       
       return root;
    }
}
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null) return root;
        
        TreeNode node=root.left;
        root.left=root.right;
        root.right=node;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

3. Symmetric binary tree

recursion

thinking

① To judge the parameters, you need to traverse two trees, the left subtree and the right subtree

② The second is the termination condition

(1) The left subtree is empty, and the right is not empty (and vice versa)

(2) Left and right are empty

(3) The left and right values do not correspond

③ The processing logic is to judge whether the post order traversal of the left subtree and the right subtree is successful and symmetrical. If it is true, it is not false

class Solution {
    public boolean isSymmetric(TreeNode root) {
          return compare(root.left,root.right);
    }

    public boolean compare(TreeNode left,TreeNode right){
        if(left==null&&right!=null) return false;
        if(left!=null&&right==null) return false;
        if(left==null&&right==null) return true;
        if(left.val!=right.val) return false;

        boolean outside= compare(left.left,right.right);
        boolean inside=compare(left.right,right.left);
        return outside&&inside;
    }
}

Iterative method

thinking

It is equivalent to the comparison of breadth first traversing two trees. Put the left subtree and the right subtree into the queue at the same time, and then take out two at a time for comparison. Then, compare the nodes of the left subtree and the right subtree. The left subtree's left subtree is opposite to the right subtree's upper right subtree, and the right subtree's right subtree is opposite to the left subtree's upper right subtree's left subtree, and put them into the queue in pairs, waiting for the next comparison. Know that the queue is empty. Moreover, condition judgment needs to be made before putting it into the child node queue

① Two empty - > continue

② One side is empty - > false

③ Different values - > false

class Solution {
    public boolean isSymmetric(TreeNode root) {
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root.left);
        queue.offer(root.right);
        while(!queue.isEmpty()){
             TreeNode leftNode=queue.poll();
             TreeNode rightNode=queue.poll();
             
             if(leftNode==null&&rightNode==null){
                 continue;
             }

             if(leftNode==null||rightNode==null||leftNode.val!=rightNode.val){
                 return false;
             }

             queue.offer(leftNode.left);
             queue.offer(rightNode.right);
             queue.offer(leftNode.right);
             queue.offer(rightNode.left);
        }
        return true;
    }
}

Stack (double ended queue)

thinking

To treat a double ended queue as two stacks, you only need to ensure that the corresponding relationship is the same, that is, the two nodes out of the stack must be the comparison nodes, and the others are basically consistent with the idea of the queue.

class Solution {
    public boolean isSymmetric(TreeNode root) {
         Deque<TreeNode> deque=new LinkedList<>();
         deque.offerFirst(root.left);
         deque.offerLast(root.right);
         while(!deque.isEmpty()){
             TreeNode leftNode= deque.pollFirst();
             TreeNode rightNode= deque.pollLast();

             if(leftNode==null&&rightNode==null){continue;}

             if(leftNode==null||rightNode==null||leftNode.val!=rightNode.val){
                 return false;
             }

             deque.offerFirst(leftNode.left);
             deque.offerFirst(leftNode.right);
             deque.offerLast(rightNode.right);
             deque.offerLast(rightNode.left);
         }
         return true;
    }
}

4. Maximum depth of tree

Recursive method

thinking

① Post order traversal, recursive trilogy, parameter traversal subtree, the return value is depth, and the end condition is when traversing to an empty node. The solution logic is the maximum depth of the left subtree and the right subtree + the depth of this layer 1.

② Preorder traversal is actually to update the result every time you enter, and give the depth + 1 when entering the left and right subtrees. The judgment end condition is that both the left subtree and the right subtree are empty. If one of them is not empty, you can enter the new recursion depth + 1 and the backtracking depth - 1. Give it to the next subtree for backtracking. The key point is to note that the termination condition is that both subtrees are empty. The judgment of entering recursion is that the left subtree is not empty or the right subtree is not empty. This is the difference of preorder traversal. But they are similar in nature. It is equivalent to doing one processing in this layer every time

class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        
        int left=maxDepth(root.left);
        int right=maxDepth(root.right);
        int depth=Math.max(left,right)+1;
        return depth;

    }
}
class Solution {
    
    int result=0;
    public void getDepth(TreeNode node,int depth){
        result=depth>result?depth:result;
        if(node.left==null&&node.right==null) return;

        if(node.left!=null){
            depth++;
            getDepth(node.left,depth);
            depth--;
        }
        
        if(node.right!=null){
            depth++;
            getDepth(node.right,depth);
            depth--;
        }

        return ;
    }



    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        getDepth(root,1);
        return result;
    }
}

Iterative method

thinking

① Sequence traversal template + depth calculation.

class Solution {
    public int maxDepth(TreeNode root) {
         Queue<TreeNode> queue=new LinkedList<>();
         if(root==null) return 0;
         queue.offer(root);
         int depth=0;
         while(!queue.isEmpty()){
              int size=queue.size();
              depth++;
              while(size-->0){
                  TreeNode node=queue.poll();
                  if(node.left!=null) queue.offer(node.left);
                  if(node.right!=null) queue.offer(node.right);
              }
         }
         return depth;
    }
}

Maximum depth of n-ary tree

Recursive method

thinking

① Post order traversal. It's just a for loop. It's no different from the above

② Sequence traversal, ibid

Conclusion: if the tree of recursive traversal is balanced, the spatial complexity logn is lower than that of sequence traversal. But the time complexity is the same

class Solution {
    public int maxDepth(Node root) {
        if(root==null) return 0;
        
        int depth=0;
        for(Node node:root.children){
            depth=Math.max(depth,maxDepth(node));
        }
        depth=depth+1;
        return depth;
        
    }
}
class Solution {
    public int maxDepth(Node root) {
        Queue<Node> queue=new LinkedList<>();
        if(root==null) return 0;

        queue.offer(root);
        int depth=0;
        while(!queue.isEmpty()){
            int size=queue.size();
            depth++;
            while(size-->0){
                Node node=queue.poll();
                for(Node temp:node.children){
                    if(temp!=null){
                        queue.offer(temp);
                    }
                }
            }
        }
        return depth;
    }
}

5. Minimum depth of tree

Recursive method

thinking

① Post order traversal, but it should be noted that the problem requires the minimum depth from the root node to the leaf node. If the root node has no left or right subtree, the problem is that the final depth is 1, that is, the depth from the root node to the root node. This place needs special treatment. If it is found that the node with the minimum depth is not a leaf node, change the depth to the current node depth 1 + the depth of the next subtree. In fact, it means that this node is the root node.

class Solution {
    public int minDepth(TreeNode root) {
          if(root==null) return 0;
          
          int left=minDepth(root.left);
          int right=minDepth(root.right);
          
          if(root.left!=null&&root.right==null){
              return 1+left;
          }
          if(root.right!=null&&root.left==null){
              return 1+right;
          }
          
          int depth=Math.min(left,right)+1;
          return depth;
    }
}

Iterative method

thinking

① Hierarchy traversal template + depth auto increment + judge whether it is a leaf node, and then update the minimum result.

② However, the update does not use the characteristics of sequence traversal. As soon as the sequence traversal is found to be the smallest, it can be returned immediately, because it must be the smallest, because it traverses layer by layer. Breadth first traversal has a greater advantage of minimum depth.

Conclusion: the essence of sequence traversal remains unchanged. In fact, it is still traversal, but the difference is that the processing logic is different and the final problem is solved differently. But the essence of the problem is to let you traverse the tree and deal with it logically according to the details.

class Solution {
    public int minDepth(TreeNode root) {
         int depth=0;
         Queue<TreeNode> queue=new LinkedList<>();
         if(root==null) return 0;
         queue.offer(root);
         int res=Integer.MAX_VALUE;
         while(!queue.isEmpty()){
             int size=queue.size();
             boolean flag=true;
             depth++;
             while(size-->0){
                 TreeNode node=queue.poll();
                 if(node.left==null&&node.right==null){
                    res=Math.min(res,depth);
                 }
                 if(node.left!=null) queue.offer(node.left);
                 if(node.right!=null) queue.offer(node.right);
             }
         }

         return res;
    }
}
class Solution {
    public int minDepth(TreeNode root) {
         int depth=0;
         Queue<TreeNode> queue=new LinkedList<>();
         if(root==null) return 0;
         queue.offer(root);
         while(!queue.isEmpty()){
             int size=queue.size();
             boolean flag=true;
             depth++;
             while(size-->0){
                 TreeNode node=queue.poll();
                 if(node.left==null&&node.right==null){
                    return depth;
                 }
                 if(node.left!=null) queue.offer(node.left);
                 if(node.right!=null) queue.offer(node.right);
             }
         }

         return depth;
    }
}

6. Number of nodes of complete binary tree

thinking

① Post order traversal + (count the number of left and right subtrees + 1)

② Direct statistics of sequence traversal

class Solution {
    public int countNodes(TreeNode root) {
           if(root==null) return 0;
           int left=countNodes(root.left);
           int right=countNodes(root.right);
           int count=left+right+1;
           return count;
    }
}
class Solution {
    public int countNodes(TreeNode root) {
          Queue<TreeNode> queue=new LinkedList<>();
          if(root==null) return 0;
          queue.offer(root);
          int res=0;
          while(!queue.isEmpty()){
              int size=queue.size();
              while(size-->0){
                  TreeNode node=queue.poll();
                  res++;
                  if(node.left!=null) queue.offer(node.left);
                  if(node.right!=null) queue.offer(node.right);
              }
          }

          return res;
    }
}

7. Balanced binary tree

Recursive method

thinking

① Similar to finding the maximum depth. However, the difference is to add a processing, that is, if the height difference between left and right is greater than 1, it will directly return to the depth of - 1. Balanced binary tree is that the height difference of subtree of each node is less than or equal to 1. Grasp this point, and then the recursive trilogy returns the depth, or height. Because it is the maximum depth of the subtree, and then when you return to the root node, compare the size of the left and right subtrees, and then recurse upward. If you return - 1, you will always return upward. If there is a corresponding height return, you need to calculate their height difference.

② Height: from bottom to top, it is usually post order traversal

Depth: from top to bottom, it is usually a preorder traversal

class Solution {
    public boolean isBalanced(TreeNode root) {
         int res=getDepth(root);
         return res!=-1;
    }

    public int getDepth(TreeNode root){
        if(root==null) return 0;
        int left=getDepth(root.left);
        if(left==-1) return -1;
        int right=getDepth(root.right);
        if(right==-1) return -1;

        if(Math.abs(left-right)>1){
            return -1;
        }else{
            int depth=1+Math.max(left,right);
            return depth;
        }
        
        
        
    } 
}

Iterative method

thinking

① This is not easy to understand, and the efficiency is very poor. The maximum depth is calculated repeatedly each time.

② Depth records the depth of the current access layer. As long as this layer pops up, depth -. If you are accessing a new layer, you need depth + +;

class Solution {

    public int getDepth(TreeNode root){
        if(root==null) return 0;
        int depth=0;
        int res=0;
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node=stack.peek();
            if(node!=null){
                stack.pop();
                stack.push(node);
                stack.push(null);
                depth++;
                if(node.right!=null) stack.push(node.right);
                if(node.left!=null) stack.push(node.left);
            }else{
                stack.pop();
                TreeNode temp=stack.peek();
                stack.pop();
                depth--;
            }
            res=res>depth?res:depth;
        }
        return res;
    }


    public boolean isBalanced(TreeNode root) {
        if(root==null) return true;
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node=stack.peek();
            stack.pop();
            if(Math.abs(getDepth(node.left)-getDepth(node.right))>1){
                  return false;
            }
            if(node.right!=null) stack.push(node.right);
            if(node.left!=null) stack.push(node.left);
            
        }
        return true;
    }
}

8. All paths of binary tree

Recursive method

thinking

① Using backtracking, each time you go to the next point, judge the blank first, add the value of the next point, and remove this value when you backtrack. Finally, all the schemes are obtained.

This is a retrospective approach. The val of adding the next node first is the same as entering and adding again

class Solution {
    List<String> res=new ArrayList<>();
    List<Integer> list=new ArrayList<>();
    
    public List<String> binaryTreePaths(TreeNode root) {
         if(root==null) return res;
         list.add(root.val);
         findPath(root);
         return res;
    }

    public void findPath(TreeNode root){
        if(root.left==null&&root.right==null){
            StringBuilder sb=new StringBuilder();
            for(int i=0;i<list.size()-1;i++){
                sb.append(list.get(i));
                sb.append("->");
            }
            sb.append(list.get(list.size()-1));
            res.add(sb.toString());
        }

       
        if(root.left!=null){
            list.add(root.left.val);
            findPath(root.left);
            list.remove(list.size()-1);
        }

        if(root.right!=null){
            list.add(root.right.val);
           findPath(root.right);
           list.remove(list.size()-1);
        }
        
        
        
    }
}
class Solution {
    List<String> res=new ArrayList<>();
    List<Integer> list=new ArrayList<>();
    
    public List<String> binaryTreePaths(TreeNode root) {
         if(root==null) return res;
         findPath(root);
         return res;
    }

    public void findPath(TreeNode root){
        list.add(root.val);
        if(root.left==null&&root.right==null){
            StringBuilder sb=new StringBuilder();
            for(int i=0;i<list.size()-1;i++){
                sb.append(list.get(i));
                sb.append("->");
            }
            sb.append(list.get(list.size()-1));
            res.add(sb.toString());
        }

       
        if(root.left!=null){
            findPath(root.left);
            list.remove(list.size()-1);
        }

        if(root.right!=null){
           findPath(root.right);
           list.remove(list.size()-1);
        }
        
        
        
    }
}

9. Same tree

thinking

In fact, it is the comparison between two left subtrees and two right subtrees. Then determine the end conditions

① Two nodes are empty

② One of them is empty

③ Values are not equal

The last is a postorder traversal, because if you want to know whether it is equal, you need to know whether the structure of the subtree is the same. The idea of this problem is similar to that of symmetric tree. The processing logic is a little different, mainly due to different parameters.

Summary: post order traversal can be used to compare the structure of two trees.

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
         if(p==null&&q==null) return true;
         if(p!=null&&q==null) return false;
         if(q!=null&&p==null) return false;
         if(p.val!=q.val) return false;
         
         boolean left=isSameTree(p.left,q.left);
         boolean right=isSameTree(p.right,q.right);
         boolean res=left&&right;
         return res;
    }
}

10. Sum of left leaves

Recursive method

thinking

① Only the parent node can judge whether the left child node is a leaf node. The node itself cannot judge, so the parent node can be used to judge in advance

② When the termination condition is root==null

③ The first, middle and last order can be used, because the key is to judge whether the left child node of the current node is a leaf node

class Solution {
    int sum=0;
    public int sumOfLeftLeaves(TreeNode root) {
          if(root==null) return sum;
          tranverse(root);
          return sum;
    }

    public void tranverse(TreeNode root){
         if(root==null) return ;
          
         
          tranverse(root.left);
         
          tranverse(root.right);
            if(root.left!=null&&root.left.left==null&&root.left.right==null){
              sum+=root.left.val;
          }
          
          
    }
}

Iterative method

thinking

1. As like as two peas, the reason is that recursive nature is to stack corresponding nodes.

② Have a template in mind and transfer knowledge.

class Solution {
    int sum=0;
    public int sumOfLeftLeaves(TreeNode root) {
        if(root==null) return 0;
        
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node=stack.pop();
            if(node.left!=null&&node.left.left==null&&node.left.right==null){
                sum+=node.left.val;
            }
            
            if(node.right!=null) stack.push(node.right);
            if(node.left!=null) stack.push(node.left);
        }

        return sum;
    }
}

11. Find the value in the lower left corner of the tree

Recursive method

thinking

① How to make sure it is the last line and how to make sure it is the leftmost value. The last row can replace the maximum depth by comparing the depth of the node. The leftmost value can use the law of preorder traversal. The first leaf node traversed must be the leftmost, that is, as long as the leftmost node is traversed, the depth can be changed and the result can be updated. If not, the leftmost node of the layer cannot participate in the update results.

② The processing logic uses backtracking to update the depth of the node

expand

① maxLeftLen here can be replaced by the minimum value

② Starting from the root node, the depth len is 1, not 0.

class Solution {
    int maxLeftLen=0;
    int maxVal=0;
    int leftLen=1;
    
    public int findBottomLeftValue(TreeNode root) {
        traversal(root);
        return maxVal;
    }

    public void traversal(TreeNode root){
        if(root==null) return ;
        
        if(root.left==null&&root.right==null){
            if(leftLen>maxLeftLen){
                maxLeftLen=leftLen;
                maxVal=root.val;
            }
            return ;
        }
        
        leftLen++;
        traversal(root.left);
        leftLen--;

        leftLen++;
        traversal(root.right);
        leftLen--;
    }
}

Iterative method

thinking

① In fact, you can directly take out the first layer before traversing each layer in the queue, and then update the results

Conclusion: sequence traversal can solve the problem of finding the leftmost or rightmost.

class Solution {
    int maxVal=0;
    public int findBottomLeftValue(TreeNode root) {
          Queue<TreeNode> queue=new LinkedList<>();
          queue.offer(root);
          while(!queue.isEmpty()){
              int size=queue.size();
              TreeNode temp=queue.peek();
              maxVal=temp.val;
              while(size-->0){
                  TreeNode node=queue.poll();
                  if(node.left!=null) queue.offer(node.left);
                  if(node.right!=null) queue.offer(node.right);
              }
          }
          return maxVal;
    }
}

12. Path sum

Recursive method

thinking

① You need to return the total judgment of the path, so you need to return the value boolean.

② The end condition has two ① leaf nodes, and the sum is finally reduced to 0 ② the leaf node is not reduced to 0;

③ The condition for entering the next level of recursion is that the node cannot be empty. And the sum needs to be backtracked.

④ If you recurse directly in the first function, the final sum only needs to compare the sum with the last value

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
         if(root==null) return false;
         return traversal(root,targetSum-root.val);
    }

    public boolean traversal(TreeNode root,int targetSum){
        if(root.left==null&&root.right==null&&targetSum==0) return true;
         if(root.left==null&&root.right==null) return false;    

        
         if(root.left!=null){
             targetSum-=root.left.val;
             if(traversal(root.left,targetSum)) return true;
             targetSum+=root.left.val;
         }

         if(root.right!=null){
              targetSum-=root.right.val;
             if(traversal(root.right,targetSum)) return true;
             targetSum+=root.right.val;
         }
         

         return false;
    }
}
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
         if(root==null) return false;
         if(root.left==null&&root.right==null&&targetSum==root.val) return true;
         
         return hasPathSum(root.left,targetSum-root.val)||hasPathSum(root.right,targetSum-root.val);
    }
}

Iterative method

thinking

① The processing method is the same as that of preorder traversal, but the parameters to be saved here also include the sum of paths. Both the saving method and the saving node method are stored in a stack and follow the stack. The new node corresponds to the sum of the new paths. When traversing the leaf node, you can directly compare the sum of the paths

Conclusion: in addition to saving nodes, the iterative method can also save parameters updated at the same time as node traversal, which is convenient for final comparison.

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
         if(root==null) return false;
         Stack<TreeNode> nodeStack=new Stack<>();
         Stack<Integer> sumStack=new Stack<>();
         
         nodeStack.push(root);
         sumStack.push(root.val);

         while(!nodeStack.isEmpty()){
             TreeNode node=nodeStack.pop();
             int sum=sumStack.pop();
             if(node.left==null&&node.right==null&&sum==targetSum) return true;
             if(node.right!=null){
                  nodeStack.push(node.right);
                  sumStack.push(sum+node.right.val);
             } 

              if(node.left!=null){
                  nodeStack.push(node.left);
                  sumStack.push(sum+node.left.val);
             } 
         }

         return false;
    }
}

Path sum 2

thinking

① The idea of recursive method is basically the same, but you need to record the value of the path.

② It should be noted that a new arrayList must be created when adding to the result set, otherwise a list will be shared, and the subsequent backtracking will change the value of the list (as long as it is an object, it should be created. Pay attention to the problem of object modification caused by backtracking)

class Solution {

    List<List<Integer>> res=new ArrayList<>();
    List<Integer> path=new ArrayList<>();

    public void traversal(TreeNode root,int targetSum){
        if(root.left==null&&root.right==null&&targetSum==0){
            res.add(new ArrayList<>(path));
            return;
        }
        if(root.left==null&&root.right==null) return ;

        if(root.left!=null){
            path.add(root.left.val);
            traversal(root.left,targetSum-root.left.val);
            path.remove(path.size()-1);
        }

        if(root.right!=null){
             path.add(root.right.val);
            traversal(root.right,targetSum-root.right.val);
            path.remove(path.size()-1);
        }
    }
    
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
           if(root==null) return res;
           path.add(root.val);
           traversal(root,targetSum-root.val);
           return res;
    }
}

13. Traversing from middle order to post order to construct binary tree

thinking

① The root node is obtained in the later order, and the left and right subtrees divided in the middle order continue recursion.

② Find the split index in the middle order, and then recurse left and right. Because the left subtree of the post order is the same as that of the middle order, this feature can be used to find the range of the left subtree of the post order. The middle order can find the range and number of the left subtree by segmentation. The range of the left subtree in the post order is equivalent to left – left + number of left subtrees, and the same is true for the right subtree, left + number of left subtrees – postRihgt-1. The middle order is left – index + 1 – right, because it is closed on the left and open on the right.

③ Judge that there is no node in the range of end condition (1) < 1

(2) When there is only one node left, a new node is returned directly.

④ The return value is the node, which can also be said to be the left and right subtrees of the root tree. In this way, we can build the tree through the current layer, and then recursively let the following continue to create the tree range. Pre order traversal is used.

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder.length==0||postorder.length==0) return null;
          return buildTree1(inorder,0,inorder.length,
          postorder,0,postorder.length);
    }

    public TreeNode buildTree1(int[] inorder,int inLeft,int inRight,
        int[] postorder,int postLeft,int postRight){

            if(inRight-inLeft<1) return null;
            
            if(inRight-inLeft==1) return new TreeNode(inorder[inLeft]);
            
            //1. Find the root node of the post order and the split position of the middle order
            int rootVal=postorder[postRight-1];
            
            int index=0;
            for(int i=inLeft;i<inRight;i++){
                if(inorder[i]==rootVal){
                    index=i;
                    break;
                }
            }

            TreeNode root=new TreeNode(rootVal);
            
            root.left=buildTree1(inorder,inLeft,index,
            postorder,postLeft,postLeft+(index-inLeft));

            root.right=buildTree1(inorder,index+1,inRight,
            postorder,postLeft+(index-inLeft),postRight-1);
            return root;

      }
}

From front order to middle order

thinking

① Basically consistent

② However, it should be noted that the point to be skipped is the first one, and the number of left subtrees after skipping the first one is the segmentation position of the preamble sequence of the next paragraph.

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
         if(preorder.length<1||inorder.length<1) return null;
         
         return buildTree1(preorder,0,preorder.length,
         inorder,0,inorder.length);
    }

    public TreeNode buildTree1(int[] preorder,int preLeft,int preRight,
    int[] inorder,int inLeft,int inRight){
           if(inRight-inLeft<1) return null;
           if(inRight-inLeft==1) return new TreeNode(inorder[inLeft]);

           int rootVal=preorder[preLeft];
           
           int index=0;
           for(int i=inLeft;i<inRight;i++){
               if(inorder[i]==rootVal){
                    index=i;
                    break;
               }
           }

           TreeNode root=new TreeNode(rootVal);

           root.left=buildTree1(preorder,preLeft+1,preLeft+1+(index-inLeft),
           inorder,inLeft,index);
           root.right=buildTree1(preorder,preLeft+1+(index-inLeft),preRight,
           inorder,index+1,inRight);
           return root;

    }
}

14. Maximum binary tree

thinking

① According to the construction of binary trees traversed by middle order and post order, it is obvious that the first step is to find the root node of the post order, and then find the partition points of the left and right subtrees of the middle order. However, to change the idea here is to find the largest point as the partition point. It's simpler.

② Here, we still use left closed and right open. After finding the segmentation point, we create the root node of the segmentation point, and then construct the left subtree and right subtree.

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if(nums.length==0) return null;
        return buildTree(nums,0,nums.length);

    }

    public TreeNode buildTree(int[] nums,int left,int right){
        if(right-left<1) return null;
        if(right-left==1) return new TreeNode(nums[left]);
        
        int index=0;
        int max=Integer.MIN_VALUE;
        for(int i=left;i<right;i++){
            if(nums[i]>max){
                max=nums[i];
                index=i;
            }
        }

        TreeNode root=new TreeNode(max);
        
        root.left=buildTree(nums,left,index);
        root.right=buildTree(nums,index+1,right);
        return root;
    }
}

15. Merge binary tree

Recursive method

thinking

① The essence is to traverse two trees and then merge two nodes. If one node is empty, the other node can be returned directly

② If one node is empty and the other is not empty, the non empty node is returned because if the problem of continuing to create a node is that the other node is also a tree, several more judgments are required to continue recursion. Special treatment is required when root1 or root2 is empty, and then create a new node until the tree is traversed. Therefore, you can also directly return to another node, that is, the original tree, and you can spell it directly.

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
            if(root1==null) return root2;
            if(root2==null) return root1;
            
            TreeNode root=new TreeNode(root1.val+root2.val);
            root.left=mergeTrees(root1.left,root2.left);
            root.right=mergeTrees(root1.right,root2.right);
            return root;
    }
}

Iterative method

thinking

① Traverse two trees and distinguish the situation. The goal is to merge 2 directly into 1

② Situation:

1. The left subtree and right subtree of the two trees are not empty. Enter the queue directly to continue

2. If tree 1 is empty and tree 2 is not empty, it will be copied directly to tree 1 (left child node or right child node)

③ It should be noted that if the stack is used for processing, it should be 2 first, and then 1. When it comes out, the first one comes out is 1. If you want the order to be consistent, you can use the queue for traversal processing. Two stacks can be used for detail processing, and two traversals will be clearer

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
         if(root1==null) return root2;
         if(root2==null) return root1;
        
         Stack<TreeNode> stack=new Stack<>();
         
         stack.push(root2);
         stack.push(root1);
         while(!stack.isEmpty()){
             TreeNode node1=stack.peek();stack.pop();
             TreeNode node2=stack.peek();stack.pop();

             node1.val+=node2.val;
             if(node1.left!=null&&node2.left!=null){
                
                 stack.push(node2.left);
                  stack.push(node1.left);
                 
             }

             if(node1.right!=null&&node2.right!=null){
                 
                 stack.push(node2.right);
                 stack.push(node1.right);
             }

             if(node1.left==null&&node2.left!=null){
                 node1.left=node2.left;
             }

             if(node1.right==null&&node2.right!=null){
                 node1.right=node2.right;
             }
         }

         return root1;
         
         
    }
}

16. Search of binary search tree

Recursive method

thinking

① The characteristic of binary search tree is that all nodes on the left must be smaller than the root node, and the right node must be larger than the root node. With this property, the way to find a node in the binary search tree is directional. You can go left or right according to the size of the specific search value.

② The end condition is root==null or finding the corresponding node and returning directly without traversing the other side

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
          if(root==null||root.val==val){
              return root;
          }

          if(root.val>val){
              return searchBST(root.left,val);
          }

          if(root.val<val){
              return searchBST(root.right,val);
          }
          return null;
    }
}

Iterative method

thinking

① Binary search tree is a directional search because of its structure. The walking direction can be determined according to the size of val and the size of the current access node. If it is equal, it can be returned directly

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        //1. No backtracking is required
        if(root==null) return root;
        
        while(root!=null){
            if(root.val>val){
                root=root.left;
            }else if(root.val<val){
                root=root.right;
            }else{
                return root;
            }
        }
        return null;

    }
}

17. Validate binary search tree

thinking

① Using the characteristics of medium order traversal and binary search tree, medium order traversal must be from small to large. You only need to find a variable to record this number. If you find that any node does not meet the requirements, you will immediately return false.

② Recursive functions need to return values, usually to find a point or a path, or when creating a tree

③ It should be noted that the minimum value in this area can be integermin, that is, if we take integer_min may cause false to be returned in advance, because this value is not small enough.

class Solution {
    long max=Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {

        if(root==null) return true;

        boolean left=isValidBST(root.left);
        if(root.val>max){
            max=root.val;
        }
        else  return false;
         
        boolean right=isValidBST(root.right);
        
        boolean res=left&&right;
        return res;

    }
}

Iterative method

thinking

① In the middle order iteration template, when the point pops up, compare the size of the previous node with the current node. If the previous node > = cur, the loop will be ended.

② There are two conditions here because when the root node has only the right node, the stack may be empty. At this time, the pointer is not empty, so it can still continue to traverse and execute.

class Solution {
    public boolean isValidBST(TreeNode root) {
        if(root==null) return true;
        
        TreeNode cur=root;
        TreeNode pre=null;
        Stack<TreeNode> stack=new Stack<>();
        while(cur!=null||!stack.isEmpty()){
             if(cur!=null){
                stack.push(cur);
                cur=cur.left;
                 
             }else{
                 cur=stack.pop();
                 
                 if(pre!=null&&cur.val<=pre.val){
                    return false;
                 }
                 pre=cur;
                 cur=cur.right;
             }
        }
        return true;
    }
}

18. Minimum absolute difference of binary search tree

thinking

① Binary search tree + middle order traversal is equal to the ordered array, which is actually the minimum absolute difference of the requested ordered array. Directly calculate the adjacent difference

class Solution {

    List<Integer> res=new ArrayList<>();
    
    public void traversal(TreeNode root){
        if(root==null) return ;
        
        traversal(root.left);
        res.add(root.val);
        traversal(root.right);
    }



    public int getMinimumDifference(TreeNode root) { 
        int max=Integer.MAX_VALUE;
        if(root==null) return 0;
        traversal(root);
        for(int i=1;i<res.size();i++){
            if(Math.abs(res.get(i)-res.get(i-1))<max){
                max=Math.abs(res.get(i)-res.get(i-1));
            }
        }
        return max;

    }
}

Iterative method

class Solution {
    public int getMinimumDifference(TreeNode root) {
          if(root==null) return 0;
          int max=Integer.MAX_VALUE;
          List<Integer> res=new ArrayList<>();
          Stack<TreeNode> stack=new Stack<>();
          TreeNode cur=root;
          while(cur!=null||!stack.isEmpty()){
              if(cur!=null){
                  stack.push(cur);
                  cur=cur.left;
              }else{
                  cur=stack.pop();
                  res.add(cur.val);
                  cur=cur.right;
              }
          }

          for(int i=1;i<res.size();i++){
             int distance=Math.abs(res.get(i)-res.get(i-1));
             if(distance<max){
                 max=distance;
             }
          }

          return max;

    }
}

19. All modes of binary search tree

Recursive method

thinking

① The first scheme is to put in the map, and then traverse the map to find the maximum number. Multiple iterations are required.

② The second scheme is through middle order + binary search. Because the numbers are from small to large, the same numbers are basically next to each other. You can use pre and cur for comparison and calculate the frequency. If the frequency is greater than the original one, the result set and new frequency can be updated. If there are values equal to this frequency, they can also be added to the result set. The key is to update the result set when the frequency is higher.

class Solution {
    Set<Integer> res=new HashSet<>();
    TreeNode pre=null;
    int count=0;
    int max=Integer.MIN_VALUE;
    public int[] findMode(TreeNode root) {
        if(root==null) return new int[]{};
        traversal(root);
        int[] result=new int[res.size()];
        int index=0;
        for(int i:res){
            result[index++]=i;
        }
        return result;
    }

    public void traversal(TreeNode root){
        if(root==null) return;

        traversal(root.left);
        
        if(pre==null){
            count=1;
        }else if(pre.val==root.val){
            count++;
        }else{
            count=1;
        }
        pre=root;

        if(count==max){
            res.add(root.val);
        }

        if(count>max){
            max=count;
            res=new HashSet<>();
            res.add(root.val);
        }
        
        

        traversal(root.right);
    }
}

Violence law

It should be noted that entry is taken out of the list, and we need to compare their values instead of entry

class Solution {
    Map<Integer,Integer> map=new HashMap<>();
    public int[] findMode(TreeNode root) {
         if(root==null) return new int[]{};
         traversal(root);
         List<Integer> res=new ArrayList<>();
         List<Map.Entry<Integer,Integer>> list=map.entrySet().stream()
         .sorted((x1,x2)->x2.getValue().compareTo(x1.getValue()))
         .collect(Collectors.toList());

         res.add(list.get(0).getKey());
         
         for(int i=1;i<list.size();i++){
             if(list.get(i).getValue()==list.get(i-1).getValue()) {
                 res.add(list.get(i).getKey());
             }else{
                 break;
             }
         }
         int index=0;
         int[] result=new int[res.size()];
         for(int i=0;i<res.size();i++){
             result[i]=res.get(i);
         }
         return result;
         
    }

    void traversal(TreeNode root){
        if(root==null) return;
        
        map.put(root.val,map.getOrDefault(root.val,0)+1);
        traversal(root.left);
        traversal(root.right);
        
    }
}

20. Nearest common ancestor of binary tree

Recursive method

thinking

① How to know whether there is a corresponding node under the subtree. How do I inform the root node if there is a node to look for

You can return the corresponding node by.

② The end condition is returned directly when the corresponding node or null is encountered

③ Processing logic

There are many situations. The first is that if one tree is empty and the other is not empty, the node obtained from the tree that is not empty will be returned. The other is that if the traversal results of the left and right subtrees are not empty, the root node is returned. Because it is a post order traversal, from bottom to top, the node that finds the nearest depth must be the nearest. If both are empty, it is OK to return the left child node or the right child node.

Summary: the reason for traversing the whole tree here is that it does not only find one node, but also another node may be on the left or right, so it is necessary to traverse the whole tree.

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null||root==p||root==q) 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 right;
        if(right==null&&left!=null) return left;
        return left;
    }
}

21. Nearest common ancestor of binary search tree

thinking

① The binary search tree is ordered, that is, the common ancestor to be found must be in the range of [p,q]. It's easy to find. I just need to find the first node in the range between them, which is the corresponding nearest common ancestor

② Because only one node is found here, you can return directly without going to the other side to continue searching

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //1. The node value is between them
        if(root==null) return root;

        if(p.val<root.val&&q.val<root.val){
            if(root.left!=null){
                return lowestCommonAncestor(root.left,p,q);
            }
        }

        if(p.val>root.val&&q.val>root.val){
            if(root.right!=null){
                return lowestCommonAncestor(root.right,p,q);
            }
        }

        return root;
        
    }
}

Iterative method (better in space complexity)

thinking

① Recently, there are three situations of public ancestors

p is in the left subtree or right subtree of root

p happens to be root

② And it is a binary search tree, that is, as long as it meets P and Q, the first point of root in their range must be equal to or less than, or > =. Both P and Q account for p.val < = root. Val < = q.val, and vice versa. That is, directly exclude the case where P and Q are on the left or right of root at the same time

Summary: this while iterative template is usually used in binary search trees because it can be used to compare sizes and determine the direction of paths

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
          if(root==null) return root;
          while(root!=null){
              if(root.val>p.val&&root.val>q.val){
                  root=root.left;
              }else if(root.val<p.val&&root.val<q.val){
                  root=root.right;
              }else{
                  break;
              }
          }
          return root;
    }
}

22. Insertion of binary search tree

Recursive method

thinking

① Using the characteristics of small left and large right, find the corresponding empty position according to the comparison between val and root. And insert.

② The node returned here is equivalent to the modified node or the new node created.

③ If there is no return value, the parent node needs to be recorded, but the idea is basically the same

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
           if(root==null) return new TreeNode(val);
           
           if(root.val>val) root.left=insertIntoBST(root.left,val);
           if(root.val<val) root.right=insertIntoBST(root.right,val);
           return root;
    }
}
class Solution {
    
    TreeNode parent;

    public void traversal(TreeNode root,int val){
        if(root==null){
            if(parent.val>val) parent.left=new TreeNode(val);
            else parent.right=new TreeNode(val);
            return ;
        }
        parent=root;
        
        if(root.val>val) traversal(root.left,val);
        if(root.val<val) traversal(root.right,val);
    }

    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root==null) return new TreeNode(val);
        
        traversal(root,val);
        return root;
    }
}

Iterative method

thinking

① Binary search routine, but if and else must be used. If both if are judged to be successful, null pointer exceptions may occur because cur=cur.left has been transferred to the access location of cur. If cur=cur.right again, cur may be an empty node

② Template + record parent, find the empty node location and insert it

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
          if(root==null) return new TreeNode(val);
          
          TreeNode parent=root;
          TreeNode cur=root;
          while(cur!=null){
               parent=cur;
               if(cur.val>val) cur=cur.left;
               else cur=cur.right;
          }
          if(parent.val>val) parent.left=new TreeNode(val);
          else parent.right=new TreeNode(val);
          
          return root;
          
    }
}

23. Delete the node of binary search tree

Recursive method

thinking

① If the return value is required, return the modified node.

② There are four cases of deletion logic. The first is that the left and right subtrees are empty. At this time, return null directly and connect to the parent node. The second is that if the left subtree is empty and the right subtree is not empty, return to the right subtree and connect to the parent node instead of the current node. The third is the reverse of the previous one. Finally, if the left and right subtrees are not empty, put the left subtree to the far left of the right subtree (directly traverse the past), and then return to the right subtree.

③ Traversal needs to use the characteristics of the search tree, only one side of the search.

class Solution {

    public TreeNode deleteNode(TreeNode root, int key) {
           if(root==null) return root;
           
           if(root.val==key){
               if(root.left==null&&root.right==null) return null;
               else if(root.left!=null&&root.right==null) return root.left;
               else if (root.right!=null&&root.left==null) return root.right;
               else{
                   TreeNode temp=root.right;
                   while(temp.left!=null){
                       temp=temp.left;
                   }
                   temp.left=root.left;
                   return root.right;
               }
           }
           if(root.val>key) root.left=deleteNode(root.left,key);
           if(root.val<key) root.right=deleteNode(root.right,key);
           return root;           
    }
}

Iterative method

thinking

① Same as above

② It should be noted that all four situations must be discussed here.

class Solution {

    public TreeNode delete(TreeNode root){
        if(root==null||(root.left==null&&root.right==null)) return null;
        if(root.right==null&&root.left!=null) return root.left;
        
        TreeNode temp=root.right;
        while(temp.left!=null){
            temp=temp.left;
        }
        temp.left=root.left;
        return root.right;
    }


    public TreeNode deleteNode(TreeNode root, int key) {
         if(root==null) return root;
         
         TreeNode pre=null;
         TreeNode cur=root;
         while(cur!=null){
             if(cur.val==key) break;
             pre=cur;
             if(cur.val>key) cur=cur.left;
             else if(cur.val<key) cur=cur.right;
             
         }
         
         if(pre==null){
             return delete(cur);
         }

         if(pre.left!=null&&pre.left.val==key){
             pre.left=delete(cur);
         }

         if(pre.right!=null&&pre.right.val==key){
             pre.right=delete(cur);
         }
         return root;


    }
}

24. Prune binary tree

Recursive method

thinking

① Find a node that is not in this range and delete it directly?

Obviously not. After finding this node, if it is smaller than the range, you can go to the right subtree (there may be nodes in the range)

And what is needed is the nodes within these ranges, which are not removed.

② How do you filter out these points?

Instead of just looking at a tree, it's better to look at it locally. As long as you find nodes that do not comply with the rules, go to the location that may comply with the rules to see if there are any nodes that can be replaced and return. In fact, the previous nodes have been filtered, and it returns the following qualified trees or nodes

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
         if(root==null) return root;
         if(root.val<low){
             TreeNode right=trimBST(root.right,low,high);
             return right;
         }

         if(root.val>high){
             TreeNode left=trimBST(root.left,low,high);
             return left;
         }

         root.left=trimBST(root.left,low,high);
         root.right=trimBST(root.right,low,high);
         return root;
    }
}

25. Convert ordered array into binary search tree

Recursive method

thinking

① For an ordered array, the midpoint can be taken as the corresponding root node every time, because the node range of the left subtree and the right subtree must include the midpoint. Find the midpoint to create the node, and then continue to create recursively, assign values to the left and right subtrees, and return the created node

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if(nums.length==0) return null;
        return buildTree(nums,0,nums.length-1);
    }
    
    public TreeNode buildTree(int[] nums,int left,int right){
        if(left>right) return null;
        
        int mid=left+((right-left)/2);
        TreeNode root=new TreeNode(nums[mid]);
        root.left=buildTree(nums,left,mid-1);
        root.right=buildTree(nums,mid+1,right);
        return root;
    }
}

Iterative method

thinking

① In fact, it is similar to the recursive method, but the difference is that three queues are used here, one to save the node, one to save the left boundary and one to save the right boundary. The most important thing is to create a node and put it in the queue, and then process the value of the intermediate node when waiting for traversal. Then, judge whether the left subtree and right subtree need to be processed according to the mid value. If they need to be added to the queue, they will not be processed if they are not needed

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if(nums.length==0) return null;
        
        Queue<TreeNode> nodeQue=new LinkedList<>();
        Queue<Integer> leftQue=new LinkedList<>();
        Queue<Integer> rightQue=new LinkedList<>();
        
        leftQue.offer(0);
        rightQue.offer(nums.length-1);
        TreeNode root=new TreeNode();
        nodeQue.offer(root);
        
        while(!nodeQue.isEmpty()){
            TreeNode node=nodeQue.poll();
            int left=leftQue.poll();
            int right=rightQue.poll();
            int mid=left+(right-left)/2;
            
            node.val=nums[mid];
            
            if(left<=mid-1){
                node.left=new TreeNode(0);
                nodeQue.offer(node.left);
                leftQue.offer(left);
                rightQue.offer(mid-1);
            }

            if(right>=mid+1){
                node.right=new TreeNode(0);
                nodeQue.offer(node.right);
                leftQue.offer(mid+1);
                rightQue.offer(right);
            }
        }
        return root;
    }
}

26. Replace binary search tree with cumulative tree

Recursive method

thinking

① The binary tree is ordered. If the binary tree is ordered, it only needs to calculate the sum of all other nodes larger than root.val, including root itself. Then, the reverse is to sort from large to small. Only itself is larger than the largest Val, so it can be accumulated from the end.

class Solution {
    int sum=0;
    public TreeNode convertBST(TreeNode root) {
         if(root==null) return null;
         
         convertBST(root.right);
         sum+=root.val;
         root.val=sum;
         convertBST(root.left);
         return root;
    }
}

Iterative method

thinking

① It is also a medium order traversal, which only needs to be operated when it pops up

② There is no need to put the root into the stack in advance, because it will be put into the stack according to whether cur is null, and then start traversal.

class Solution {
    //Medium order iterative method
    public TreeNode convertBST(TreeNode root) {
        if(root==null) return null;
        
        TreeNode cur=root;
        Stack<TreeNode> stack=new Stack<>();
        int sum=0;
        
        while(cur!=null||!stack.isEmpty()){
            // TreeNode cur=stack.pop();
            if(cur!=null){
                stack.push(cur);
                cur=cur.right;
            }else{
                cur=stack.pop();
                sum+=cur.val;
                cur.val=sum;
                cur=cur.left;
            }
        }

        return root;

    }
}

Topics: Algorithm data structure