LeetCode tree review II

Posted by bouba on Fri, 14 Jan 2022 22:43:00 +0100

236. Nearest common ancestor of fork tree

Given a binary tree, find the nearest common ancestor of two specified nodes in the tree.

Baidu Encyclopedia defines the nearest public ancestor as: "for two nodes p and q with root tree T, the nearest public ancestor is expressed as a node x, which satisfies that x is the ancestor of p and q, and the depth of X is as large as possible (a node can also be its own ancestor)."

Method 1 - storage parent node:

My idea is to traverse the tree from the root node, find two nodes pq, record all their ancestors (including themselves) from top to bottom, and then compare the ancestor sequence. From back to front, find the same is the nearest common ancestor, but unfortunately, this TLE does not take much time for dfs, and the size of the list should be at the O(logn) level, but the list is in two cycles, It may exceed the order of 10 ^ 7, and it will timeout.

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        List<TreeNode>list1=new ArrayList<>();
        List<TreeNode>list2=new ArrayList<>();
        dfs(root, p.val, list1);
        dfs(root, q.val, list2);
        for(int i=list1.size()-1;i>=0;i--){
            for(int j=list2.size()-1;j>=0;j--){
                if(list1.get(i).val==list2.get(j).val){
                    return list1.get(i);
                }
            }
        }
        return null;
    }
    void dfs(TreeNode root,int val,List<TreeNode> list){
        if(root==null)return;
        list.add(root);
        if(root.val==val)return;
        dfs(root.left, val, list);
        dfs(root.left, val, list);
        dfs(root.right, val, list);
        dfs(root.right, val, list);
        list.remove(list.size()-1);
        return;
    }
}

But this method is feasible. To reduce the complexity, we need to use the hash table to store the relationship between nodes and parents, and then traverse from bottom to top to find common points to reduce the access complexity. In this way, we can AC

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //The map records the correspondence between any number and its parent node
        Map<TreeNode, TreeNode> map = new HashMap<>();
        //set is used to record the traversal result of p. if it coincides with q, it proves that the target point is found
        Set<Integer> visited = new HashSet<Integer>();
        dfs(root,map);
        //Add all ancestors of p to the set from bottom to top, except the root node
        while(map.containsKey(p)){
            visited.add(p.val);
            p=map.get(p);
        }
        //Add all ancestors of p to the set from bottom to top, except the root node
        //If the node already exists in the set, it is the target node
        while(map.containsKey(q)){
            if(visited.contains(q.val)){
                return q;
            }
            q=map.get(q);
        }
        //Note that the map does not contain a root node, so if the previous traversal does not find it, the root node must be the root node
        return root;
    }
    //dfs, initializing map data
    void dfs(TreeNode root,Map<TreeNode, TreeNode> map){
        if(root.left!=null){
            map.put(root.left, root);
            dfs(root.left, map);
        }
        if(root.right!=null){
            map.put(root.right, root);
            dfs(root.right, map);
        }
    }
}

Method 2 - recursive lookup

If we want recursion, we need to determine the recursion idea. Binary tree recursion must pass through left and right nodes. How to determine the return value?

Boundary conditions: 1 Null returns null 2 p/q returns p/q

General conditions: 1 Both left and right are null and return null 2 If none is empty, return root 3 Return the one that is not empty

The code is as follows:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //boundary condition 
        if(root==null||root.equals(p)||root.equals(q)){
            return root;
        }
        TreeNode left=lowestCommonAncestor(root.left, p, q);
        TreeNode right=lowestCommonAncestor(root.right, p, q);
        //Four cases
        if(left!=null&&right!=null){
            return root;
        }
        if(left==null){
            return right;
        }
        if(right==null){
            return left;
        }
        return null;
    }
}

297. Serialization and deserialization of binary tree 

Serialization is the operation of converting a data structure or object into continuous bits, and then the converted data can be stored in a file or memory. At the same time, it can also be transmitted to another computer environment through the network, and the original data can be reconstructed in the opposite way.

Please design an algorithm to realize the serialization and deserialization of binary tree. There is no restriction on the execution logic of your sequence / deserialization algorithm. You only need to ensure that a binary tree can be serialized into a string and deserialize the string into the original tree structure.

Note: the input / output format is the same as that currently used by LeetCode. For details, please refer to the format of LeetCode serialized binary tree. You don't have to take this approach, you can also take other methods to solve the problem.

The easy way to think of is that we use methods such as BFS\DFS to traverse the binary tree, and then convert the traversal result into a string. During this period, we can modify the string slightly to restore it to a binary tree

Method 1: BFS

Serialization is simple, and the key to deserialization is to ensure that each node has children during serialization. If it is a leaf node, add two "X" to store it, and it can be converted to null during deserialization. The split function of regular expression is used. Then we should realize that the process of deserialization is essentially a tree stored in arr, We want the BFS tree! So it's easy to understand

public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if (root == null) {
            return "";
        }
        return bfs(root);
    }
    public String bfs(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        Deque<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if (node == null) {
                sb.append("X,");
            } else {
                sb.append(node.val + ",");
                queue.offer(node.left);
                queue.offer(node.right);
            }
        }
        return sb.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data==""){
            return null;
        }
        //Split the string into multiple nodes, X corresponds to null, and the number corresponds to ordinary nodes
        String[]arr=data.split(",");
        int index=0;
        //In fact, we have stored the whole tree structure in the arr, and then use the bfs method to traverse the "tree"
        //BFS naturally has queues
        Queue<TreeNode>queue=new LinkedList<>();
        TreeNode root=new TreeNode(Integer.parseInt(arr[index++]));
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            if(node==null)continue;
            String left=arr[index++];
            String right=arr[index++];
            if("X".equals(left)){
                node.left=null;
            }else{
                node.left=new TreeNode(Integer.parseInt(left));
            }
            if("X".equals(right)){
                node.right=null;
            }else{
                node.right=new TreeNode(Integer.parseInt(right));
            }
            queue.offer(node.left);
            queue.offer(node.right);
        }
        return root;
    }
}

Topics: Algorithm leetcode Binary tree