Loading problems...
TL;DR: By precomputing the depth and subtree height of every node using Postorder Traversal, we can determine the new tree height in by checking the two largest subtree heights at the removed node's depth level.
You are provided with the root of a binary tree and a list of independent queries. Each query specifies a node value whose subtree must be temporarily removed. For each query, you must calculate the height of the remaining tree. The tree resets to its original state after every query. This is a popular interview question that tests your ability to optimize tree operations beyond naive simulation.
The naive approach is to simulate the removal process literally for every query.
q:
2. Create a deep copy of the tree (or use a temporary flag to ignore the subtree at q).
3. Traverse the modified tree (using DFS or BFS) to calculate its maximum depth.
4. Store the result.
5. Reset the tree.python# Pseudo-code for Brute Force def solve_naive(root, queries): answers = [] for q_val in queries: # 1. Mark node q_val as "removed" # 2. Run DFS from root to find max depth, ignoring "removed" paths current_height = calculate_height(root, ignore=q_val) answers.append(current_height) return answers
Why this fails: The time complexity is , where is the number of queries and is the number of nodes. With constraints up to for both and , this results in roughly operations, which inevitably causes a Time Limit Exceeded (TLE) error. We need a solution that answers each query in time after preprocessing.
The key intuition is to realize that the height of the tree is determined by the longest path from the root to a leaf. If we remove a subtree rooted at node u, we break all paths passing through u. The new height of the tree will be determined by the longest path that does not go through u.
Every path from the root to a leaf passes through exactly one node at any specific depth level (up to the path's end). Therefore, if we remove a node u at depth d, the new maximum height is determined by the "cousins" of u—other nodes residing at the same depth d.
Specifically, for any depth level, we only care about the nodes that produce the longest paths.
depth of every node and the height of the subtree rooted at every node.u at depth depth[u], the new tree height is the maximum of:
depth[u].By storing the two largest subtree heights for every depth level, we can answer queries instantly. If the node being removed holds the largest height at its level, the answer is the second largest. Otherwise, the answer remains the largest.
Visual Description:
Imagine the recursion tree. As the Postorder Traversal bubbles up from the leaves, each node calculates its height as 1 + max(left_height, right_height). We essentially "annotate" every horizontal level of the tree with the subtree heights found at that level. When a query cuts a branch, we simply look horizontally at that level to see which other branch is longest.

Data Structures:
depth_map: Stores the depth (distance from root) of each node.height_map: Stores the height (distance to deepest leaf) of the subtree rooted at each node.level_maxes: A hash map (or array) where keys are depths. For each depth, we store the two largest subtree heights found at that depth.Preprocessing (DFS):
u at depth d:
height[u] = 1 + max(height[left], height[right]).depth[u] = d.level_maxes[d] with height[u]. Keep only the top 2 distinct values (associated with their node IDs to handle ties correctly).Query Processing:
q:
d = depth_map[q] and h = height_map[q].level_maxes[d].q is the node responsible for the maximum height at this level, the new tree height is d + second_max_height_at_level.d + max_height_at_level.depth + 1.depth + 1.1 + max(left_sub_height, right_sub_height).depth and height for the current node.depth. If the current height is larger than the stored maximums, shift them down.queries.L of the query node.L.L, use the secondary longest path.Result = L + selected_subtree_height.The height of the binary tree is defined as the maximum number of edges from the root to any leaf. Any path from the root to a leaf can be decomposed into two parts relative to a specific depth : the path from Root to a node at depth (length ), and the path from to a leaf (length ).
The global height is . Since we are removing a subtree at node (where ), we are effectively removing the term from the set of all path lengths. Because all paths reaching depth greater than must pass through some node at depth , the new maximum height must be formed by a path passing through a sibling or cousin of at depth . Therefore, gives the correct new height.
cpp#include <vector> #include <unordered_map> #include <algorithm> #include <iostream> using namespace std; /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { // Map to store the depth of each node: node_val -> depth unordered_map<int, int> depthMap; // Map to store the height of the subtree rooted at each node: node_val -> height unordered_map<int, int> heightMap; // Stores the top 2 largest heights for each depth level // levelMaxes[d] = { {height1, node1}, {height2, node2} } unordered_map<int, vector<pair<int, int>>> levelMaxes; int computeHeights(TreeNode* node, int depth) { if (!node) return -1; depthMap[node->val] = depth; int leftH = computeHeights(node->left, depth + 1); int rightH = computeHeights(node->right, depth + 1); int currentH = 1 + max(leftH, rightH); heightMap[node->val] = currentH; // Update top 2 heights for this depth auto& top2 = levelMaxes[depth]; top2.push_back({currentH, node->val}); sort(top2.rbegin(), top2.rend()); // Sort descending if (top2.size() > 2) { top2.pop_back(); // Keep only top 2 } return currentH; } public: vector<int> treeQueries(TreeNode* root, vector<int>& queries) { depthMap.clear(); heightMap.clear(); levelMaxes.clear(); // 1. Precompute depths and subtree heights using Postorder DFS computeHeights(root, 0); vector<int> answer; answer.reserve(queries.size()); // 2. Answer queries in O(1) for (int q : queries) { int d = depthMap[q]; int h = heightMap[q]; // Retrieve top heights at this depth auto& tops = levelMaxes[d]; // If the node being removed is the one with the largest height at this level if (tops[0].second == q) { if (tops.size() == 2) { // Use the second largest height answer.push_back(d + tops[1].first); } else { // No other node at this depth extends further; max path ends at level d-1 answer.push_back(d - 1); } } else { // The node removed is not the largest, so global max remains unchanged answer.push_back(d + tops[0].first); } } return answer; } };
javaimport java.util.*; /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { // node val -> depth private Map<Integer, Integer> depthMap = new HashMap<>(); // node val -> subtree height private Map<Integer, Integer> heightMap = new HashMap<>(); // depth -> list of top 2 pairs (height, nodeVal) private Map<Integer, List<int[]>> levelMaxes = new HashMap<>(); public int[] treeQueries(TreeNode root, int[] queries) { // 1. Precompute depths and heights computeHeights(root, 0); int[] answer = new int[queries.length]; // 2. Process queries for (int i = 0; i < queries.length; i++) { int q = queries[i]; int d = depthMap.get(q); List<int[]> tops = levelMaxes.get(d); // Check if the removed node holds the max height at this level if (tops.get(0)[1] == q) { if (tops.size() > 1) { answer[i] = d + tops.get(1)[0]; } else { // No cousins, path stops at parent answer[i] = d - 1; } } else { // Max height is determined by a different node answer[i] = d + tops.get(0)[0]; } } return answer; } private int computeHeights(TreeNode node, int depth) { if (node == null) return -1; depthMap.put(node.val, depth); int leftH = computeHeights(node.left, depth + 1); int rightH = computeHeights(node.right, depth + 1); int currentH = 1 + Math.max(leftH, rightH); heightMap.put(node.val, currentH); // Manage top 2 heights for this level levelMaxes.putIfAbsent(depth, new ArrayList<>()); List<int[]> tops = levelMaxes.get(depth); tops.add(new int[]{currentH, node.val}); // Sort descending by height tops.sort((a, b) -> b[0] - a[0]); if (tops.size() > 2) { tops.remove(2); } return currentH; } }
python# 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]: depth_map = {} height_map = {} # Stores top 2 heights at each level: depth -> [(height, node_val), ...] level_maxes = collections.defaultdict(list) def compute_heights(node, depth): if not node: return -1 depth_map[node.val] = depth left_h = compute_heights(node.left, depth + 1) right_h = compute_heights(node.right, depth + 1) current_h = 1 + max(left_h, right_h) height_map[node.val] = current_h # Update top 2 for this level level_maxes[depth].append((current_h, node.val)) level_maxes[depth].sort(key=lambda x: x[0], reverse=True) if len(level_maxes[depth]) > 2: level_maxes[depth].pop() return current_h # 1. Precompute compute_heights(root, 0) answers = [] # 2. Answer queries for q in queries: d = depth_map[q] tops = level_maxes[d] # If q is the node with the largest height at this level if tops[0][1] == q: if len(tops) > 1: answers.append(d + tops[1][0]) else: answers.append(d - 1) else: answers.append(d + tops[0][0]) return answers
level_maxes array stores up to 2 values per depth level (max depth is ).The Tree DFS - Recursive Postorder Traversal pattern used here is fundamental for many tree problems.
1 + max(left, right) logic to find height.By mastering this pattern, you can solve almost any problem requiring "bottom-up" information flow in a tree.
Why did I get TLE with the brute force approach?
Why is my answer wrong for nodes with no siblings?
depth.Why did I get an "off-by-one" error in the height calculation?
depth + height becomes incorrect. Ensure height of a single node is 0 (or leaf is 0) and depth of root is 0 consistently.