Problem
You are given the root of a binary tree with n nodes. Each node is assigned a unique value from 1 to n. You are also given an array queries of size m.
You have to perform m independent queries on the tree where in the ith query you do the following:
- Remove the subtree rooted at the node with the value
queries[i]from the tree. It is guaranteed thatqueries[i]will not be equal to the value of the root.
Return an array answer of size m where answer[i] is the height of the tree after performing the ith query.
Note:
- The queries are independent, so the tree returns to its initial state after each query.
- The height of a tree is the number of edges in the longest simple path from the root to some node in the tree.
Examples
Example 1:
---
title: Input tree
---
graph TD;
A1((1))
B3((3))
C4((4)):::query
D2((2))
E6((6))
F5((5))
G7((7))
Null3_1(("null")):::null
Null5_1(("null")):::null
A1 --> B3
A1 --> C4
B3 --> D2
B3 --> Null3_1
C4 --> E6
C4 --> F5
F5 --> Null5_1
F5 --> G7
classDef query fill:#f96,stroke:#333,stroke-width:4px;
classDef null fill:#eee,stroke:#999,stroke-width:2px,stroke-dasharray: 5,5;
---
title: Tree after removing query node
---
graph TD;
A1((1))
B3((3))
D2((2))
Null1_1(("null")):::null
Null3_1(("null")):::null
A1 --> B3 & Null1_1
B3 --> D2 & Null3_1
classDef null fill:#eee,stroke:#999,stroke-width:2px,stroke-dasharray: 5,5;
| |
Example 2:
graph TD;
A5((5))
B8((8)):::query
C9((9))
D2((2)):::query
E1((1))
F3((3)):::query
G7((7))
H4((4)):::query
I6((6))
A5 --> B8
A5 --> C9
B8 --> D2
B8 --> E1
C9 --> F3
C9 --> G7
D2 --> H4
D2 --> I6
classDef query fill:#f96,stroke:#333,stroke-width:4px;
| |
Solution
Video explanation
Here is the video explaining below methods in detail. Please check it out:
Method 1 - DFS
We can perform DFS to compute the height of the tree effectively. Here is the approach:
- Calculate the initial height of the complete tree using Depth-First Search (DFS).
- For each query:
- Perform DFS again, but skip the subtree rooted at the specified node for that query.
- Compute the height of the modified tree.
- Restore the tree’s state to its original configuration since queries are independent.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n * m), wherenis the number of nodes, andmis the number of queries.- Calculating the height of the tree using DFS takes
O(n). - For each query, DFS traversal ignoring the subtree rooted at the queried node also takes
O(n). - Thus, for
mqueries, it isO(n * m).
- Calculating the height of the tree using DFS takes
- 🧺 Space complexity:
O(n)for storing the tree structure and the recursion stack during DFS.
Method 2 - Precompute depth and height of each node
To improve the efficiency of the solution, we need to avoid repeatedly calculating the height of the entire tree for each query. Instead, we can precompute the height of the tree with and without each possible subtree.
Each node has a depth and a height, and the longest path passing through it is the sum of these two values, i.e. depth + height.
For eg. for node 4 below, longest height is 4. For node 7, it is 3.
When a node, say node 4 is removed, its children are also removed, stopping all paths that pass through it. However, if node 4 has peers (i.e. siblings or cousins), the paths through these peers will likely be longer, so we need to find the longest path through this node’s peers, which means locating the peer with the greatest height.
For node 4, that peer is node 7, resulting in height of 3.
To facilitate this, we store nodes by depth, and for those with the same depth, we sort them by height, retaining only the top two heights. In above tree for depth 2: We get [4, 5, 6, 7] but we retain nodes nodes [4,7] as they have max height.
When a node is ‘removed’ according to the queries, we find its peers and identify the one with the highest height to complete the task.
If there is only one node at that depth, it indicates that the removed node had no peers, so the longest remaining path is depth - 1. Otherwise, we determine the maximum height among the remaining nodes at that depth.
So, here is the approach:
- DFS Traversal: Compute the depth and height of each node using a DFS traversal.
- Storing Node Heights: Store nodes according to their depth and maintain a list of the highest and second highest nodes in terms of height for each depth level.
- Query Processing: For each query, determine the new height of the tree after removing the node by considering these pre-stored heights.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n + m)- DFS traversal to compute height and depth of each node takes
O(n) - Then for each node, we iterate through all nodes and insert them into a data structure (like a list or priority queue) that maintains the top k heights. Even though inserting and maintaining the order in such a structure can take
O(log k)time, sincekis a constant (2 in our case), it simplifies toO(1), hence overall this step incursO(n)complexity in practice. - Finally, answering queries take
O(m)time
- DFS traversal to compute height and depth of each node takes
- 🧺 Space complexity:
O(n + m)O(n)for using recursion stack in dfs, heights, depths, and the sorted lists of nodes at each depth level is proportional to the number of nodesn.O(m)for storing queries