An indispensable binary tree interview question in the interview

Preorder traversal and inorder traversal restore binary tree

Link: pre order + mid order restore binary tree


Idea:

  • Determine the root node of the binary tree in the preorder traversal result (reason: the preorder traversal first traverses the root node, traverses the left subtree of the root, and then traverses the right subtree of the root);
  • Find the position pos of the root in the middle order traversal result. Take pos as the dividing point. The left is the position of the left subtree element of the root, and the right is the position of the right subtree element of the root;
  • Restore the root node;
  • Recursively restore the left subtree of the root;
  • Recursive reduction of right subtree of root

Code implementation:

class Solution {
    int index=0; //Used to mark the position of the root in the preorder traversal
    TreeNode rebuilderTree(int[] preorder,int[] inorder,int left, int right){
        if(index>=preorder.length || left >= right){
            return null;
        }
        //Determine the position elements of the left and right subtrees of the root from the middle order traversal results
        int pos=left;
        while(pos<right){
            if(inorder[pos]==preorder[index]){
                //Indicates that the root node was found
                break;
            }
            pos++;
        }
        // Determine the root from the preorder traversal result, that is, the position of the index. preorder[index] is the position of the root
        //Restore root node of binary tree
        TreeNode root=new TreeNode(preorder[index]);
         index++;

        //Recursively restore the left subtree of the root
        root.left=rebuilderTree(preorder,inorder,left,pos);

        //Recursive reduction of right subtree of root
        root.right=rebuilderTree(preorder,inorder,pos+1,right);

        return root;

    }
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        index=0;
        return rebuilderTree(preorder, inorder,0,inorder.length);
    }
}

Middle order traversal and post order traversal restore binary tree

Link: middle order + post order restore binary tree

Idea:

  • Determine the root node of the binary tree in the post order traversal result (reason: the post order traversal first traverses the left subtree of the root, then the right subtree of the root, and finally the root node);
  • Find the position pos of the root in the middle order traversal result. Take pos as the dividing point. The left is the position of the left subtree element of the root, and the right is the position of the right subtree element of the root;
  • Restore the root node;
  • Recursively restore the right subtree of the root;
  • Recursively restore the left subtree of the root

Code implementation:

class Solution {
        int index=0; //Used to mark the position of the root in the preorder traversal
    TreeNode rebuilderTree(int[] postorder,int[] inorder,int left, int right){
        if(index<0 || left >= right){
            return null;
        }
        //Determine the position elements of the left and right subtrees of the root from the middle order traversal results
        int pos=left;
        while(pos<right){
            if(inorder[pos]==postorder[index]){
                //Indicates that the root node was found
                break;
            }
            pos++;
        }
        // Determine the root from the preorder traversal results, that is, the position of the index. The postorder[index] is the position of the root
        //Restore root node of binary tree
        TreeNode root=new TreeNode(postorder[index]);
         index--;

        //Recursive reduction of right subtree of root
        root.right=rebuilderTree(postorder,inorder, pos+1,right);

        //Recursively restore the left subtree of the root
        root.left=rebuilderTree(postorder,inorder,left ,pos);

        return root;

    }
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        index=postorder.length-1;
        return rebuilderTree(postorder, inorder,0,inorder.length);

    }
}

Binary tree construction string

Links: binary tree construction strings

Idea:

This topic uses string. Because string is an immutable type and needs to be spliced, we use stringbuilder;

Note: the left parenthesis in the title cannot be omitted, otherwise the position of the left and right subtrees cannot be distinguished;

Code implementation:

class Solution {
    public void preorder(TreeNode root, StringBuilder str) {
          //The tree is empty
        if (root == null) {
            return;
        }
		//Convert root node (i.e. print)
        str.append(root.val);
        //Left subtree of recursive transformation root
        //There are two situations
        if (root.left != null) {
            str.append('(');
            preorder(root.left, str);
            str.append(')');
        }
        if (root.left == null && root.right != null) {
            str.append('(');
            str.append(')');
        }
        //Right subtree of recursive transformation root
        if (root.right != null) {
            str.append('(');
            preorder(root.right, str);
            str.append(')');
        }
    }

    public String tree2str(TreeNode root) {

        StringBuilder str = new StringBuilder();
        preorder(root, str);
        return str.toString();
    }
}

Find the nearest common ancestor of binary tree

Links: common ancestors

Idea:

It can be analyzed and discussed in three situations:
Case 1: the binary tree adopts parent representation or child parent representation;
Seeking the common ancestor becomes the problem of finding the intersection point of two linked lists;

Case 2:
If the binary tree is a binary search tree, the so-called binary search tree satisfies: (1) the left subtree node whose root node is greater than the root; (2) The root node is smaller than the right subtree node of the root;
There are four ways to seek common ancestors:
Suppose a given two nodes, one is x and the other is y;
(1) When x == root or y ==root, the nearest common ancestor must be the root node; (reason: the root node is the ancestor of all nodes)
(2) When x. Data < root.data & & Y.Data > root.data, or when x. Data > root.data & & Y.Data < root.data, the nearest common ancestor is also the root node (reason: X and y are on both sides of the root node);
(3) When x. Data < root. Data & & Y.Data < root. Data, X and y are both in the left subtree of the root, so the nearest common ancestor can be found directly in the left subtree of the root;
(4) When x. Data > root.data & & Y.Data > root.data, both X and y are in the right subtree of the root, so the nearest common ancestor can be found directly in the right subtree of the root;

Case 3: the binary tree is an ordinary binary tree;

Then:

  • Inspired by case 1, just find those nodes in the path from the root node to a node and save them; Because when the node in the path is found, it needs to be compared from bottom to top, so it is saved with the help of stack;

    In this case

(1) When the elements stored in the stack are not equal, let more elements out of the stack;
(2) When the value range is the same and different, the stack is displayed at the same time;
(3) Common ancestor is the element with the same number of elements in the stack and the same value range;

Code implementation:

class Solution {
    //Get all the nodes in the path from the root node to a node
    boolean getPathNode(TreeNode root,Stack<TreeNode> sPath,TreeNode node ){
        if(node ==null || root==null){
            //The tree is empty or the node is empty
            return false;
        }

        sPath.push(root);
        if(root==node){
        //Indicates the same node
            return true;
        }
        //Find in the left subtree
        if(getPathNode(root.left,sPath,node)){
            return true;
        }
          //Find in right subtree
         if(getPathNode(root.right,sPath,node)){
            return true;
        }
        sPath.pop();
        return false;
    }
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //Detect given parameters
        if(root==null ||p==null ||q==null){
            return null;
        }
      //Find all nodes contained in the corresponding path from root to p,q
      //Save to stack
      Stack<TreeNode> pPath=new Stack();
      Stack<TreeNode> qPath=new Stack();
      getPathNode(root,pPath,p);
      getPathNode(root,qPath,q);

      int pSize=pPath.size();
      int qSize=qPath.size();
      while(!pPath.empty() && !qPath.empty()){
          if(pPath.peek()==qPath.peek()){
              return pPath.peek();
          }
          if(pSize>qSize){
          //Let more out of the stack
              pPath.pop();
              pSize--;
          }else if(pSize<qSize){
              qPath.pop();
              qSize--;
          }else{
          //When two are the same, they come out of the stack at the same time
              pPath.pop();
              qPath.pop();
              pSize--;
              qSize--;
          }
      }

        return null;
    }
}
  • Similarly, inspired by situation 2:
  • If we know the position of a node in its subtree, we can also find the common ancestor;

The code is as follows:

class Solution {
    boolean isNodeInTree(TreeNode root,TreeNode node){
        //Check whether the node is in the tree
        if(root==null || node ==null){
            return false;
        }

        if(root==node){
            return true;
        }

        if(isNodeInTree(root.left,node)){
            return true;
        }

          
        return isNodeInTree(root.right,node);

    }
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //Empty tree
        if(root==null){
            return null;
        }

        //Tree is not empty
        //p and q have a position at the root, and the common ancestor is the root
        if(p == root|| q == root){
            return root;
        }

        boolean ispInLeft=false;
        boolean ispInRight=false;
        boolean isqInLeft=false;
        boolean isqInRight=false;

        //Check whether p is in the left subtree of root
        if(isNodeInTree(root.left,p)){
            ispInLeft=true;
            ispInRight=false;

        }else{
             ispInLeft=false;
             ispInRight=true;
        }

        
         //Check whether q is in the left subtree of root
        if(isNodeInTree(root.left,q)){
            isqInLeft=true;
            isqInRight=false;

        }else{
             isqInLeft=false;
             isqInRight=true;
        }
       //Check whether both p and q are in the left subtree of root
        if(ispInLeft && isqInLeft){
            return lowestCommonAncestor(root.left,p,q);
        }else if(ispInRight && isqInRight){
            return lowestCommonAncestor(root.right,p,q);

        }else{
            return root;
        }
    }
}

Posted by Edwin Okli on Mon, 08 Nov 2021 09:34:19 -0800