Brush notes: binary tree

Keywords: data structure linked list

101. Symmetric binary tree


To judge whether the mirror image is symmetrical, make a dotted line in the middle to judge whether the half fold coincides. For example, the root node depends on whether its left child node and right child node are the same, and then the left child node of the left child node and the right child node of the right child node...
1. Recursion

    public boolean isSymmetric(TreeNode root) {
        return isMirror(root,root);
    }
    private boolean isMirror(TreeNode left,TreeNode right){
    	//true if both are empty at this time
        if(left==null&&right==null)return true;
        //false if only one is null or the values are unequal
        if((left==null||right==null)||(right.val!=left.val))return false;
		//Recursively view the following nodes
        return isMirror(left.right,right.left)&&isMirror(left.left,right.right);
    }

2. Iteration

    public boolean isSymmetric(TreeNode root) {
        if(root==null) return true;
        Deque<TreeNode> queue=new LinkedList<>();
        queue.add(root.left);
        queue.add(root.right);
        while(!queue.isEmpty()){
            TreeNode left=queue.poll();
            TreeNode right=queue.poll();
            if(left==null&&right==null)continue;
            if(left==null||right==null||left.val!=right.val) return false;
            queue.add(left.left);
            queue.add(right.right);
            queue.add(left.right);
            queue.add(right.left);
        }
        return true;
    }

104. Maximum depth of binary tree


The depth of the binary tree is the maximum value from the root node to the leaf nodes of the left and right subtrees. If this layer is not empty, its depth is + 1. So we find the depth of the left and right subtrees, and then take the maximum, plus the depth of our own layer (that is, 1).
1. Recursion

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

2. Iteration

    public int maxDepth(TreeNode root) {
        Deque<TreeNode> queue=new LinkedList<>();
        int res=0;
        if(root==null) return 0;
        queue.add(root);
        while(!queue.isEmpty()){
            res++;
            for(int i=queue.size();i>0;i--){
                TreeNode node=queue.poll();
                if(node.left!=null){
                    queue.add(node.left);
                }
                if(node.right!=null){
                    queue.add(node.right);
                }
            }
        }
        return res;
    }

However, the problem of iterative efficiency is relatively low, and the interview needs to be able to.

105. Construct binary tree from preorder and inorder traversal sequences


Just handle the boundary. The drawing is very clear and can be seen

    Map<Integer,Integer> map=new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return dfs(preorder,inorder,0,preorder.length-1,0,preorder.length-1);
    }
    private TreeNode dfs(int[] preorder, int[] inorder,int pl,int pr,int il,int ir){
        if(pl>pr||il>ir)return null;
        int root=preorder[pl];
        //Get the location of root
        int index=map.get(root);
        //The left subtree of root has k nodes
        int k=index-il;
        TreeNode node=new TreeNode(root);
        node.left=dfs(preorder,inorder,pl+1,pl+k,il,index-1);
        node.right=dfs(preorder,inorder,pl+k+1,pr,index+1,ir);
        return node;
    }

106. Construct binary tree from middle order and post order traversal sequences


If you can understand the above figure, you can use the above figure. If you can't understand it, you can read my notes below. The same is true

    Map<Integer,Integer> map=new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return dfs(postorder,inorder,0,postorder.length-1,0,postorder.length-1);
    }
    private TreeNode dfs(int[] postorder,int[] inorder,int pl,int pr,int il,int ir){
        if(pl>pr||il>ir)return null;
        int root=postorder[pr];
        //Get the location of root
        int index=map.get(root);
        //The left subtree of root has k elements
        int k=index-il;        
        TreeNode node=new TreeNode(root);
        node.left=dfs(postorder,inorder,pl,pl+k-1,il,index-1);
        node.right=dfs(postorder,inorder,pl+k,pr-1,index+1,ir);
        return node;
    }

112. Path sum


Recursive left and right subtree

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

116. Populate the next right node pointer for each node



It's easy to think of iterative solution

    public Node connect(Node root) {
        if(root==null) return null;
        Deque<Node> queue=new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            Node pre=queue.poll();
            int size=queue.size();
            if(pre.left!=null)queue.add(pre.left);
            if(pre.right!=null)queue.add(pre.right);
            for(int i=0;i<size;i++){
                Node cur=queue.poll();
                pre.next=cur;
                if(cur.left!=null)queue.add(cur.left);
                if(cur.right!=null)queue.add(cur.right);   
                pre=cur;             
            }
            pre.next=null;
        }
        return root;
    }

However, this space complexity does not meet the requirements. Optimize it and do not use queue storage

class Solution {
	public Node connect(Node root) {
		if(root==null) return null;
		Node pre = root;
		while(pre.left!=null) {
			Node tmp = pre;
			while(tmp!=null) {
				tmp.left.next = tmp.right;
				if(tmp.next!=null) {
					tmp.right.next = tmp.next.left;
				}
				tmp = tmp.next;
			}
			pre = pre.left;
		}
		return root;
	}
}

recursion

class Solution {
	public Node connect(Node root) {
		dfs(root);
		return root;
	}
	
	private void dfs(Node root) {
		if(root==null) {
			return;
		}
		Node left = root.left;
		Node right = root.right;
		while(left!=null) {
			left.next = right;
			left = left.right;
			right = right.left;
		}
		dfs(root.left);
		dfs(root.right);
	}
}

Reference from: Animation demonstration + three implementations

117. Fill in the next right node pointer II of each node



Iteration:

    public Node connect(Node root) {
    	if(root==null)return null;
    	Node cur=root;
    	while(cur!=null){
			Node dummy=new Node();
			Node pre=dummy;
			while(cur!=null){
				//First connect your own subtree
				if(cur.left!=null){
					pre.next=cur.left;
					pre=pre.next;
				}
				if(cur.right!=null){
					pre.next=cur.right;
					pre=pre.next;
				} 
				//Then connect the on the right across the subtree
				cur=cur.next;
			}
			//Next, start with the leftmost subtree (depending on the connection of the previous layer)
			cur=dummy.next;
		}
		return root;
	}

After reading the problem solution of recursion, I don't understand it. I think this is suitable for iteration. (I'm too good, woo woo)

199. Right view of binary tree


Level traversal, queue in turn, and take the last element of the queue each time

    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res=new LinkedList<>();
        if(root==null) return res;        
        Deque<TreeNode> q=new LinkedList<>();
        q.add(root);
        while(!q.isEmpty()){
            for(int i=q.size();i>0;i--){
                TreeNode node=q.poll();
                if(i==1){
                    res.add(node.val);
                }
                if(node.left!=null){
                    q.add(node.left);
                }
                if(node.right!=null){
                    q.add(node.right);
                }
            }
        }
        return res;
    }

235. Nearest common ancestor of binary search tree


Because this problem is a binary search tree, we can make full use of its characteristics

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val>root.val&&q.val>root.val){
            return lowestCommonAncestor(root.right,p,q);
        }
        if(p.val<root.val&&q.val<root.val){
            return lowestCommonAncestor(root.left,p,q);
        }
        return root;
    }

236. Nearest common ancestor of binary tree


Recursively go to the left and right subtrees to find the positions of p and q, and then judge

    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;
        else if(left==null) return right;
        return left;
    }

Posted by Jayson on Mon, 01 Nov 2021 06:37:28 -0700