<p>You are given the <code>root</code> of a <strong>binary tree</strong> with <code>n</code> nodes. Each node is assigned a unique value from <code>1</code> to <code>n</code>. You are also given an array <code>queries</code> of size <code>m</code>.</p>

<p>You have to perform <code>m</code> <strong>independent</strong> queries on the tree where in the <code>i<sup>th</sup></code> query you do the following:</p>

<ul>
	<li><strong>Remove</strong> the subtree rooted at the node with the value <code>queries[i]</code> from the tree. It is <strong>guaranteed</strong> that <code>queries[i]</code> will <strong>not</strong> be equal to the value of the root.</li>
</ul>

<p>Return <em>an array </em><code>answer</code><em> of size </em><code>m</code><em> where </em><code>answer[i]</code><em> is the height of the tree after performing the </em><code>i<sup>th</sup></code><em> query</em>.</p>

<p><strong>Note</strong>:</p>

<ul>
	<li>The queries are independent, so the tree returns to its <strong>initial</strong> state after each query.</li>
	<li>The height of a tree is the <strong>number of edges in the longest simple path</strong> from the root to some node in the tree.</li>
</ul>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2022/09/07/binaryytreeedrawio-1.png" style="width: 495px; height: 281px;" />
<pre>
<strong>Input:</strong> root = [1,3,4,2,null,6,5,null,null,null,null,null,7], queries = [4]
<strong>Output:</strong> [2]
<strong>Explanation:</strong> The diagram above shows the tree after removing the subtree rooted at node with value 4.
The height of the tree is 2 (The path 1 -&gt; 3 -&gt; 2).
</pre>

<p><strong class="example">Example 2:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2022/09/07/binaryytreeedrawio-2.png" style="width: 301px; height: 284px;" />
<pre>
<strong>Input:</strong> root = [5,8,9,2,1,3,7,4,6], queries = [3,2,4,8]
<strong>Output:</strong> [3,2,3,2]
<strong>Explanation:</strong> We have the following queries:
- Removing the subtree rooted at node with value 3. The height of the tree becomes 3 (The path 5 -&gt; 8 -&gt; 2 -&gt; 4).
- Removing the subtree rooted at node with value 2. The height of the tree becomes 2 (The path 5 -&gt; 8 -&gt; 1).
- Removing the subtree rooted at node with value 4. The height of the tree becomes 3 (The path 5 -&gt; 8 -&gt; 2 -&gt; 6).
- Removing the subtree rooted at node with value 8. The height of the tree becomes 2 (The path 5 -&gt; 9 -&gt; 3).
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li>The number of nodes in the tree is <code>n</code>.</li>
	<li><code>2 &lt;= n &lt;= 10<sup>5</sup></code></li>
	<li><code>1 &lt;= Node.val &lt;= n</code></li>
	<li>All the values in the tree are <strong>unique</strong>.</li>
	<li><code>m == queries.length</code></li>
	<li><code>1 &lt;= m &lt;= min(n, 10<sup>4</sup>)</code></li>
	<li><code>1 &lt;= queries[i] &lt;= n</code></li>
	<li><code>queries[i] != root.val</code></li>
</ul>

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        pass











class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        # record heights of the nodes
        heights = {}
        depth = {}
        def traversal(node, depth):
            if not node:
                return -1
            heights[node.val] = max(traversal(node.left, depth+1), traversal(node.right, depth+1)) + 1
            depth[node.val] = depth
            return heights[node.val]
        traversal(root, 0)
        #print(heights)

        # For each query, traverse the tree in BFS
        def bfs(q):
            queue = deque([[root, None]])
            level = 0
            while queue:
                queue_next = deque()
                found_query = False
                while queue:
                    node, node_parent = queue.popleft()

                    if node.left and node.left.val == q:
                        #print('left: ', node.left.val, q)
                        found_query = True
                    elif node.right and node.right.val == q:
                        #print('right: ', node.right.val, q)
                        found_query = True

                    if node.left and node.left.val != q:
                        queue_next.append((node.left, node))
                    if node.right and node.right.val != q:
                        queue_next.append((node.right, node))
                    #print('queue_next: ', queue_next, node.val)
                    #print('queue: ', queue)
                
                if found_query:
                    max_height = -1
                    #print(queue_next)
                    while queue_next:
                        node, node_parent = queue_next.popleft()
                        max_height = max(max_height, heights[node.val])
                    #print('inside found_query: ', max_height, level)
                    return max_height + level + 1
                
                queue = queue_next
                level += 1
            return -1
            
        ans = []
        for q in queries:
            ans.append(bfs(q))
        return ans










class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        # record heights of the nodes
        heights = {}
        depths = {}
        def traversal(node, depth):
            if not node:
                return -1
            heights[node.val] = max(traversal(node.left, depth+1), traversal(node.right, depth+1)) + 1
            depths[node.val] = depth
            return heights[node.val]
        traversal(root, 0)
        #print(heights)

        depth_wise_nodes = collections.defaultdict(list)
        for node in depths:
            depth_wise_nodes[depths[node]].append((-heights[node], node))
            depth_wise_nodes[depths[node]].sort()
            if len(depth_wise_nodes[depths[node]]) > 2:
                depth_wise_nodes[depths[node]].pop()
        
        ans = []
        for q in queries:
            dep = depths[q]
            if len(depth_wise_nodes[dep]) == 1:
                ans.append(dep-1)
            elif depth_wise_nodes[dep][0][1] == q:
                ans.append(-depth_wise_nodes[dep][1][0] + dep)
            else:
                ans.append(-depth_wise_nodes[dep][0][0] + dep)
        return ans


















class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        node_depth = {}
        node_height = {}
        node_level = collections.defaultdict(list)

        def get_info(node, depth):
            if not node:
                return 0
            node_depth[node.val] = depth
            node_level[depth].append(node.val)
            node_height[node.val] = max(get_info(node.left, depth+1), get_info(node.right, depth+1))
            return node_height[node.val] + 1

        get_info(root, 0)
        ans = []
        for q in queries:
            depth = node_depth[q]
            nodes_same_level = node_level[depth]
            max_height = -1
            for node in nodes_same_level:
                if node != q:
                    max_height = max(max_height, node_height[node])
            
            ans.append(depth + max_height)
            
        return ans 


class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        node_depth = {}
        node_height = {}
        node_level = collections.defaultdict(list)

        def get_info(node, depth):
            if not node:
                return 0
            node_depth[node.val] = depth
            node_height[node.val] = max(get_info(node.left, depth+1), get_info(node.right, depth+1))
            node_level[depth].append((-node_height[node.val], node.val))
            if len(node_level[depth]) > 2:
                node_level[depth].sort()
                node_level[depth].pop()
            return node_height[node.val] + 1

        get_info(root, 0)
        ans = []
        for q in queries:
            depth = node_depth[q]
            if len(node_level[depth]) == 1:
                ans.append(depth-1)
            elif node_level[depth][0][1] == q: # query is the node with highest height
                ans.append(depth - node_level[depth][1][0])
            else:
                ans.append(depth - node_level[depth][0][0])
        return ans 

























# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        # val --> node
        node_each_level = collections.defaultdict(list)
        depths = collections.defaultdict(int)
        heights = collections.defaultdict(int)

        def recursion(node, depth):
            if not node:
                return -1
            left = recursion(node.left, depth+1)
            right = recursion(node.right, depth+1)
            heights[node.val] = max(left, right) + 1
            depths[node.val] = depth
            node_each_level[depth].append(node.val)
            return heights[node.val]

        recursion(root, 0)

        ans = []
        for q in queries:
            depth_q = depths[q]
            max_heights = -1
            for node in node_each_level[depth_q]:
                if node != q:
                    max_heights = max(max_heights, heights[node])
            ans.append(max_heights + depths[q])
        return ans




# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        '''
        heights: node.val --> height
        depths: node.val --> depth
        same_levels: depth --> list(node.val)
        '''
        heights = collections.defaultdict(int)
        depths = collections.defaultdict(int)
        same_depths = collections.defaultdict(int)

        def recursion(node, depth):
            if not node:
                return -1
            
            depths[node.val] = depth
            same_depths[depth].append(node.val)
            
            left_height = recursion(node.left, depth+1)
            right_height = recursion(node.right, depth+1)
            heights[node.val] = max(left_height, right_height) + 1
            return heights[node.val]

        recursion(root, 0)

        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for neigh in same_depths[depth_q]:
                if neigh != q:
                    max_height = max(max_height, heights[neigh])
            ans.append(max_height + depth_q)
        return ans



# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        '''
        heights: node.val --> height
        depths: node.val --> depth
        same_levels: depth --> list(node.val)
        '''
        heights = collections.defaultdict(int)
        depths = collections.defaultdict(int)
        same_depths = collections.defaultdict(list)

        def recursion(node, depth):
            if not node:
                return -1
            
            depths[node.val] = depth
            
            left_height = recursion(node.left, depth+1)
            right_height = recursion(node.right, depth+1)
            heights[node.val] = max(left_height, right_height) + 1
            same_depths[depth].append((-heights[node.val], node.val))
            if len(same_depths[depth]) > 2:
                same_depths[depth].sort()
                same_depths[depth].pop()
            return heights[node.val]

        recursion(root, 0)

        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for _, neigh in same_depths[depth_q]:
                if neigh != q:
                    max_height = max(max_height, heights[neigh])
            ans.append(max_height + depth_q)
        return ans






























# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        heights = {}
        depths = {}
        same_depths = collections.defaultdict(list)

        def recursion(node, depth):
            if not node:
                return -1
            depths[node.val] = depth
            same_depths[depth].append(node.val)
            left = recursion(node.left, depth+1)
            right = recursion(node.right, depth+1)
            heights[node.val] = max(left, right) + 1
            return heights[node.val]
        
        recursion(root, 0)

        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for node_val in same_depths[depth_q]:
                if node_val != q:
                    max_height = max(max_height, heights[node_val])
            ans.append(max_height + depth_q)
        return ans


class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        heights = {}
        depths = {}
        same_depths = collections.defaultdict(list)

        def recursion(node, depth):
            if not node:
                return -1
            depths[node.val] = depth
            
            left = recursion(node.left, depth+1)
            right = recursion(node.right, depth+1)
            heights[node.val] = max(left, right) + 1
            same_depths[depth].append((-heights[node.val], node.val))
            if len(same_depths[depth]) > 2:
                same_depths[depth].sort()
                same_depths[depth].pop()
            return heights[node.val]


        
        recursion(root, 0)

        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for h, node_val in same_depths[depth_q]:
                if node_val != q:
                    max_height = max(max_height, heights[node_val])
            ans.append(max_height + depth_q)
        return ans
























# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        '''
        depths: node.val --> depth
        heights: node.val --> height
        same_depths: depth (int) --> list(node.val)
        '''
        depths = {}
        heights = {}
        same_depths = collections.defaultdict(list)

        def traversal(node, depth):
            if not node:
                return -1
            depths[node.val] = depth
            
            left_h = traversal(node.left, depth+1)
            right_h = traversal(node.right, depth+1)
            heights[node.val] = max(left_h, right_h) + 1

            same_depths[depth].append((-heights[node.val], node.val))
            if len(same_depths[depth]) > 2:
                same_depths[depth].sort()
                same_depths[depth].pop()
                
            return heights[node.val]

        traversal(root, 0)

        ans = []
        for q in queries:
            q_depth = depths[q]
            max_height = -1
            for _, node in same_depths[q_depth]:
                if node != q:
                    max_height = max(max_height, heights[node])
            ans.append(max_height + q_depth)
        return ans






















# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        heights = {}
        depths = {}
        same_depths = collections.defaultdict(list)

        def traversal(node, depth):
            if not node:
                return -1
            depths[node.val] = depth
            same_depths[depth].append(node.val)
            left = traversal(node.left, depth+1)
            right = traversal(node.right, depth+1)
            height = max(left, right) + 1
            heights[node.val] = height
            return height
        
        traversal(root, 0)

        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for neigh_val in same_depths[depth_q]:
                if neigh_val != q:
                    max_height = max(max_height, heights[neigh_val])
            ans.append(max_height + depth_q)
        return ans


class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        heights = {}
        depths = {}
        same_depths = collections.defaultdict(list)

        def traversal(node, depth):
            if not node:
                return -1
            depths[node.val] = depth
            left = traversal(node.left, depth+1)
            right = traversal(node.right, depth+1)
            height = max(left, right) + 1
            heights[node.val] = height
            same_depths[depth].append((-height, node.val))
            if len(same_depths[depth]) > 2:
                same_depths[depth].sort()
                same_depths[depth].pop()
            return height
        
        traversal(root, 0)

        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for _, neigh_val in same_depths[depth_q]:
                if neigh_val != q:
                    max_height = max(max_height, heights[neigh_val])
            ans.append(max_height + depth_q)
        return ans



















# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        depths = {}
        heights = {}
        same_depths = collections.defaultdict(list)

        def recursion(node, depth):
            if not node:
                return -1
            depths[node.val] = depth

            left_height = recursion(node.left, depth+1)
            right_height = recursion(node.right, depth+1)
            heights[node.val] = max(left_height, right_height) + 1

            same_depths[depth].append((-heights[node.val], node.val))
            if len(same_depths[depth]) >= 3:
                same_depths[depth].sort()
                same_depths[depth].pop()
            return heights[node.val]
        
        recursion(root, 0)
        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for _, node in same_depths[depth_q]:
                if node != q:
                    max_height = max(max_height, heights[node])
            ans.append(max_height + depth_q)
        return ans





















class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        '''
        heights: val --> height
        depths: val --> depth
        same_depths: depth --> val
        '''

        heights = {}
        depths = {}
        same_depths = collections.defaultdict(list)
        def recursion(node, depth):
            if not node:
                return -1
            depths[node.val] = depth
            left = recursion(node.left, depth+1)
            right = recursion(node.right, depth+1)
            heights[node.val] = max(left, right) + 1
            same_depths[depth].append(node.val)
            return heights[node.val]
        
        recursion(root, 0)
        ans = []
        for q in queries:
            depth_q = depths[q]
            max_height = -1
            for node in same_depths[depth_q]:
                if node != q:
                    max_height = max(max_height, heights[node])
            ans.append(max_height + depth_q)
        return ans




# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        # depths_same: depth --> list(node)
        # depths_node: node --> depth
        # heights_node: node --> height
        depths_same = collections.defaultdict(list)
        depths_nodes = {}
        heights_nodes = {}

        def traversal(node, depth):
            # return height
            if not node:
                return -1
            
            depths_nodes[node.val] = depth
            height_left = traversal(node.left, depth+1)
            height_right = traversal(node.right, depth+1)
            heights_nodes[node.val] = max(height_left, height_right) + 1
            depths_same[depth].append((-heights_nodes[node.val], node.val))
            if len(depths_same[depth]) >= 3:
                depths_same[depth].sort()
                depths_same[depth].pop()
            return heights_nodes[node.val]

        traversal(root, 0)
        ans = []
        for q in queries:
            height_max = -1
            for _, node in depths_same[depths_nodes[q]]:
                if node != q:
                    height_max = max(height_max, heights_nodes[node]) 
            ans.append(height_max + depths_nodes[q])
        return ans


65181