Trees
Phase 1 47 solutionsTrees
Problem List
| # | Problem | LC | Difficulty |
|---|---|---|---|
| 1 | Binary Tree Inorder Traversal | 94 | π’ Easy |
| 2 | Binary Tree Preorder Traversal | 144 | π’ Easy |
| 3 | Binary Tree Postorder Traversal | 145 | π’ Easy |
| 4 | Invert Binary Tree | 226 | π’ Easy |
| 5 | Maximum Depth of Binary Tree | 104 | π’ Easy |
| 6 | Diameter of Binary Tree | 543 | π’ Easy |
| 7 | Balanced Binary Tree | 110 | π’ Easy |
| 8 | Same Tree | 100 | π’ Easy |
| 9 | Subtree of Another Tree | 572 | π’ Easy |
| 10 | Lowest Common Ancestor of BST | 235 | π‘ Medium |
| 11 | Insert into BST | 701 | π‘ Medium |
| 12 | Delete Node in BST | 450 | π‘ Medium |
| 13 | Binary Tree Level Order Traversal | 102 | π‘ Medium |
| 14 | Binary Tree Right Side View | 199 | π‘ Medium |
| 15 | Construct Quad Tree | 427 | π‘ Medium |
| 16 | Count Good Nodes | 1448 | π‘ Medium |
| 17 | Validate BST | 98 | π‘ Medium |
| 18 | Kth Smallest Element in BST | 230 | π‘ Medium |
| 19 | Construct Tree from Preorder and Inorder | 105 | π‘ Medium |
| 20 | House Robber III | 337 | π‘ Medium |
| 21 | Delete Leaves With Given Value | 1325 | π‘ Medium |
| 22 | Binary Tree Maximum Path Sum | 124 | π΄ Hard |
| 23 | Serialize And Deserialize Binary Tree | 297 | π΄ Hard |
| 24 | Zigzag Level Order Traversal | 103 | π‘ Medium |
| 25 | Boundary Traversal | 545 | π‘ Medium |
| 26 | Vertical Order Traversal | 987 | π΄ Hard |
| 27 | Top View of Binary Tree | β | π‘ Medium |
| 28 | Bottom View of Binary Tree | β | π‘ Medium |
| 29 | Symmetric Tree | 101 | π’ Easy |
| 30 | Root to Leaf Paths | 257 | π’ Easy |
| 31 | Lowest Common Ancestor of Binary Tree | 236 | π‘ Medium |
| 32 | Maximum Width of Binary Tree | 662 | π‘ Medium |
| 33 | All Nodes Distance K | 863 | π‘ Medium |
| 34 | Burn Binary Tree | 2385 | π΄ Hard |
| 35 | Count Complete Tree Nodes | 222 | π’ Easy |
| 36 | Construct BT from Postorder and Inorder | 106 | π‘ Medium |
| 37 | Morris Traversal | β | π‘ Medium |
| 38 | Flatten Binary Tree to Linked List | 114 | π‘ Medium |
| 39 | Search in BST | 700 | π’ Easy |
| 40 | Floor and Ceil in BST | β | π‘ Medium |
| 41 | Construct BST from Preorder | 1008 | π‘ Medium |
| 42 | Inorder Successor/Predecessor in BST | β | π‘ Medium |
| 43 | Merge Two BSTs | β | π‘ Medium |
| 44 | Two Sum in BST | 653 | π’ Easy |
| 45 | Recover BST | 99 | π‘ Medium |
| 46 | Largest BST in Binary Tree | 1373 | π΄ Hard |
Approach Diagrams
DFS vs BFS Decision
flowchart TD
A[Tree Problem] --> B{"Need level-by-level<br/>or shortest path?"}
B -- Yes --> C["BFS with Queue<br/>LC 102, 199"]
B -- No --> D{"Need to explore<br/>all paths?"}
D -- Yes --> E[DFS]
E --> F{Order matters?}
F -- Pre --> G["Preorder: rootβleftβright<br/>LC 144, 105"]
F -- In --> H["Inorder: leftβrootβright<br/>LC 94, 230"]
F -- Post --> I["Postorder: leftβrightβroot<br/>LC 145, 124"]
F -- No specific --> J["Any DFS<br/>LC 104, 543, 110"]
Tree Traversal Orders
flowchart LR
subgraph Preorder["Preorder (NLR)"]
P1[Visit Root] --> P2[Left Subtree] --> P3[Right Subtree]
end
subgraph Inorder["Inorder (LNR) β BST sorted order"]
I1[Left Subtree] --> I2[Visit Root] --> I3[Right Subtree]
end
subgraph Postorder["Postorder (LRN)"]
O1[Left Subtree] --> O2[Right Subtree] --> O3[Visit Root]
end
BST Operations Flow
flowchart TD
A[BST Operation] --> B{Type}
B -- Search/Insert --> C[Compare with root]
C -- val < root --> D[Go Left]
C -- val > root --> E[Go Right]
C -- val == root --> F["Found / Insert here"]
B -- Delete --> G[Find node]
G --> H{Children?}
H -- No child --> I[Remove node]
H -- One child --> J[Replace with child]
H -- Two children --> K["Replace with<br/>inorder successor<br/>min of right subtree"]
B -- Validate --> L["Pass min/max bounds<br/>down recursively"]
L --> M{node.val in range?}
M -- Yes --> N["Check children<br/>with updated bounds"]
M -- No --> O[Invalid BST]
Problem Categories
mindmap
root((Trees))
Traversal
Inorder LC94
Preorder LC144
Postorder LC145
Level Order LC102
Zigzag Level Order LC103
Right Side View LC199
Morris Inorder/Preorder
View Problems
Vertical Order LC987
Top View
Bottom View
Boundary Traversal LC545
BST Operations
Insert LC701
Delete LC450
Validate LC98
Kth Smallest LC230
LCA of BST LC235
Construction
From Pre+Inorder LC105
From Post+Inorder LC106
Serialize/Deserialize LC297
Flatten to Linked List LC114
Path Problems
Max Depth LC104
Diameter LC543
Max Path Sum LC124
Count Good Nodes LC1448
Root to Leaf Paths LC257
Nodes at Distance K LC863
Structural
Invert LC226
Same Tree LC100
Subtree LC572
Balanced LC110
Symmetric LC101
Max Width LC662
Count Complete Nodes LC222
Advanced
House Robber III LC337
Burn Binary Tree LC2385
LCA of Binary Tree LC236
Delete Leaves LC1325
BST Advanced
Search in BST LC700
Floor and Ceil in BST
Construct BST from Preorder LC1008
Inorder Successor/Predecessor BST
Merge Two BSTs
Two Sum in BST LC653
Recover BST LC99
Largest BST in Binary Tree LC1373
Recursive Tree Pattern Template
flowchart TD
A[solve node] --> B{"Base case:<br/>node is None?"}
B -- Yes --> C["Return base value<br/>0, True, None, etc."]
B -- No --> D["Recurse left<br/>left = solve node.left"]
D --> E["Recurse right<br/>right = solve node.right"]
E --> F["Combine results<br/>with current node"]
F --> G["Return result<br/>up to parent"]
Complexity Summary
| Problem | Time | Space | Pattern |
|---|---|---|---|
| Inorder/Preorder/Postorder | O(n) | O(h) | DFS recursive/iterative |
| Level Order / Zigzag | O(n) | O(w) | BFS |
| Morris Traversal | O(n) | O(1) | Threading |
| Max Depth | O(n) | O(h) | DFS |
| Diameter | O(n) | O(h) | DFS, return height |
| Balanced | O(n) | O(h) | DFS, return height |
| Same Tree / Subtree | O(nΒ·m) | O(h) | DFS |
| Symmetric Tree | O(n) | O(h) | DFS mirror compare |
| Invert Tree | O(n) | O(h) | DFS/BFS |
| BST Insert/Delete/Search | O(h) | O(h) | BST property |
| Validate BST | O(n) | O(h) | DFS + bounds |
| Kth Smallest | O(h+k) | O(h) | Inorder DFS |
| LCA of BST | O(h) | O(1) | BST split point |
| LCA of Binary Tree | O(n) | O(h) | DFS both sides |
| Floor/Ceil in BST | O(h) | O(1) | BST traversal |
| Construct from Pre+Inorder | O(n) | O(n) | Preorder + hashmap |
| Construct from Post+Inorder | O(n) | O(n) | Postorder + hashmap |
| Construct BST from Preorder | O(n) | O(h) | Recursive with bounds |
| Right Side View | O(n) | O(w) | BFS last per level |
| Boundary Traversal | O(n) | O(n) | 3-pass: left + leaves + right |
| Vertical Order | O(n log n) | O(n) | BFS + sort by col |
| Top/Bottom View | O(n) | O(n) | BFS + first/last per col |
| Count Good Nodes | O(n) | O(h) | DFS + max so far |
| Max Path Sum | O(n) | O(h) | DFS, global max |
| Max Width | O(n) | O(w) | BFS + position indexing |
| Serialize/Deserialize | O(n) | O(n) | BFS or DFS |
| Flatten to Linked List | O(n) | O(1) | Morris-like |
| Nodes at Distance K | O(n) | O(n) | Parent map + BFS |
| Burn Binary Tree | O(n) | O(n) | Parent map + BFS |
| Count Complete Tree Nodes | O(logΒ²n) | O(log n) | Height comparison |
| House Robber III | O(n) | O(h) | DFS, return pair |
| Delete Leaves | O(n) | O(h) | Postorder DFS |
| Inorder Successor/Predecessor | O(h) | O(1) | BST traversal |
| Two Sum in BST | O(n) | O(n) | Inorder + two pointers |
| Recover BST | O(n) | O(1) | Morris + find violations |
| Merge Two BSTs | O(m+n) | O(m+n) | Inorder + merge + build |
| Largest BST Subtree | O(n) | O(h) | Post-order, return tuple |
h = tree height (O(log n) balanced, O(n) worst), w = max width
Study Order
flowchart LR
subgraph W1["Week 1: Basic Traversals"]
t1["inorder(94) β preorder(144) β postorder(145)"]
t2["level_order(102) β zigzag(103) β right_side_view(199)"]
end
subgraph W2["Week 2: DFS Patterns"]
t3["invert(226) β max_depth(104) β diameter(543)"]
t4["balanced(110) β same_tree(100) β subtree(572) β symmetric(101) β count_good_nodes(1448)"]
end
subgraph W3["Week 3: View Problems"]
t5["boundary_traversal β vertical_order(987)"]
t6["top_view β bottom_view β morris_traversal"]
end
subgraph W4["Week 4: BST Operations"]
t7["search(700) β insert(701) β delete(450) β validate(98)"]
t8["kth_smallest(230) β lca_bst(235) β floor_ceil β inorder_successor_predecessor"]
t9["two_sum_bst(653) β construct_bst_from_preorder(1008) β merge_two_bsts"]
end
subgraph W5["Week 5: Construction + Path Problems"]
t10["construct_pre_inorder(105) β construct_post_inorder(106)"]
t11["serialize_deserialize(297) β flatten(114) β count_complete_nodes(222)"]
t12["root_to_leaf_paths(257) β lca_binary_tree(236) β max_width(662)"]
end
subgraph W6["Week 6: Hard + Advanced"]
t13["max_path_sum(124) β burn_binary_tree(2385) β all_nodes_distance_k(863)"]
t14["recover_bst(99) β largest_bst(333) β house_robber_iii(337)"]
t15["delete_leaves(1325) β construct_quad_tree(427) β morris_traversal"]
end
W1 --> W2 --> W3 --> W4 --> W5 --> W6
Key insight: Master the recursive template first (base case β recurse left β recurse right β combine). Every tree problem is a variation of this pattern.
Trees β Pattern Notes
Core Intuition
Trees are recursive structures. Almost every tree problem reduces to: what do I need from my children to answer the question at the current node? Define that return value clearly, then trust recursion.
- DFS = process node + recurse. Choose pre/in/post based on when you need the node's value relative to children.
- BFS = level-by-level using a queue. Use when you need level info or shortest path.
- BST = left < node < right. Inorder traversal gives sorted order. Always exploit this property.
Decision Flowchart
flowchart TD
A[Tree Problem] --> B{Need level-by-level info?}
B -- Yes --> C[BFS with queue]
B -- No --> D{Is it a BST?}
D -- Yes --> E{Exploit sorted order?}
E -- Yes --> F["Inorder / BST property"]
E -- No --> G[Standard DFS]
D -- No --> H{Path problem?}
H -- Yes --> I{Path through root?}
I -- Yes --> J[Return value up + track global max]
I -- No --> K[Pass value down as parameter]
H -- No --> L{Need parent info?}
L -- Yes --> M["Pass parent/state as param"]
L -- No --> N[Pure recursive DFS]
Per-Problem Notes
1. Inorder Traversal β LC 94 | Easy
- Key insight: Left β Node β Right. Iterative uses explicit stack; push left spine, pop, go right.
- Tricky: Iterative version β remember to move
curr = curr.rightafter popping. - Common mistakes: Forgetting the right subtree in iterative; confusing with preorder.
- Time: O(n) | Space: O(h) stack
2. Preorder Traversal β LC 144 | Easy
- Key insight: Node β Left β Right. Iterative: push right then left so left is processed first.
- Tricky: Iterative push order is reversed (right before left).
- Common mistakes: Pushing left before right in iterative.
- Time: O(n) | Space: O(h)
3. Postorder Traversal β LC 145 | Easy
- Key insight: Left β Right β Node. Iterative trick: reverse of modified preorder (NodeβRightβLeft).
- Tricky: Iterative is non-obvious; use two-stack or reverse trick.
- Common mistakes: Trying to directly simulate postorder iteratively without the reverse trick.
- Time: O(n) | Space: O(h)
4. Invert Binary Tree β LC 226 | Easy
- Key insight: Swap left and right children at every node. Pure recursion.
- Tricky: Nothing β classic recursion warm-up.
- Common mistakes: Forgetting to return the node after swapping.
- Time: O(n) | Space: O(h)
5. Maximum Depth β LC 104 | Easy
- Key insight:
max(depth(left), depth(right)) + 1. Base case: null β 0. - Tricky: Off-by-one β depth of a single node is 1, not 0.
- Common mistakes: Returning 0 for leaf instead of 1.
- Time: O(n) | Space: O(h)
6. Diameter of Binary Tree β LC 543 | Easy
- Key insight: Diameter through a node = left_height + right_height. Track global max, return height upward.
- Tricky: The diameter doesn't have to pass through root. Must use a nonlocal/global variable.
- Common mistakes: Returning diameter instead of height from the recursive function.
- Time: O(n) | Space: O(h)
7. Balanced Binary Tree β LC 110 | Easy
- Key insight: Return -1 as a sentinel for "unbalanced" up the call stack. Avoids recomputation.
- Tricky: NaΓ―ve O(nΒ²) solution recomputes heights. Optimal does it in one pass.
- Common mistakes: Computing height separately for each node (O(nΒ²)).
- Time: O(n) | Space: O(h)
8. Same Tree β LC 100 | Easy
- Key insight: Two trees are same if roots equal AND left subtrees same AND right subtrees same.
- Tricky: Handle null cases: both null β true, one null β false.
- Common mistakes: Not handling the case where one is null and other isn't.
- Time: O(n) | Space: O(h)
9. Subtree of Another Tree β LC 572 | Easy
- Key insight: At each node of the big tree, check if
isSameTree(node, subRoot). Recurse if not. - Tricky: O(nΒ·m) is acceptable here. Serialization approach is O(n+m) but complex.
- Common mistakes: Not checking the current node itself, only children.
- Time: O(nΒ·m) | Space: O(h)
10. LCA of BST β LC 235 | Medium
- Key insight: If both nodes < root β go left. Both > root β go right. Otherwise root is LCA.
- Tricky: BST property makes this O(h) instead of O(n).
- Common mistakes: Using the general LCA approach (LC 236) which ignores BST property.
- Time: O(h) | Space: O(h)
11. Insert into BST β LC 701 | Medium
- Key insight: Recurse left/right based on comparison. Insert at the null spot.
- Tricky: Return the node at each step to reattach it to parent.
- Common mistakes: Not returning the node β breaks the tree linkage.
- Time: O(h) | Space: O(h)
12. Delete Node in BST β LC 450 | Medium
- Key insight: Three cases: no children (delete), one child (replace), two children (replace with inorder successor = leftmost of right subtree).
- Tricky: Two-children case. Find inorder successor, copy its value, delete successor from right subtree.
- Common mistakes: Forgetting to recursively delete the successor after copying its value.
- Time: O(h) | Space: O(h)
13. Binary Tree Level Order β LC 102 | Medium
- Key insight: BFS with queue. Track level size at start of each iteration.
- Tricky: Snapshot
len(queue)before the inner loop, not during. - Common mistakes: Not snapshotting queue size β processes multiple levels in one pass.
- Time: O(n) | Space: O(n)
14. Right Side View β LC 199 | Medium
- Key insight: BFS level order β take the last element of each level. Or DFS passing depth, update result when depth == len(result).
- Tricky: DFS approach: visit right child first so rightmost is seen first at each depth.
- Common mistakes: BFS: taking first element instead of last.
- Time: O(n) | Space: O(n)
15. Construct Quad Tree β LC 427 | Medium
- Key insight: Check if all values in region are same β leaf. Otherwise split into 4 quadrants recursively.
- Tricky: Precompute 2D prefix sums to check uniformity in O(1) per region.
- Common mistakes: Recomputing sum naively β O(nΒ² log n). Use prefix sums for O(nΒ²).
- Time: O(nΒ²) | Space: O(nΒ²)
16. Count Good Nodes β LC 1448 | Medium
- Key insight: Pass max value seen so far down the tree. Node is "good" if its value β₯ max.
- Tricky: Pass max as parameter (top-down), not bottom-up.
- Common mistakes: Trying to solve bottom-up when top-down is natural here.
- Time: O(n) | Space: O(h)
17. Validate BST β LC 98 | Medium
- Key insight: Pass (min, max) bounds down. Each node must satisfy min < node.val < max.
- Tricky: Initial bounds are (-β, +β). Use
float('-inf')andfloat('inf'). - Common mistakes: Only comparing with direct parent (fails for grandparent violations). Duplicate values β BST typically requires strict inequality.
- Time: O(n) | Space: O(h)
18. Kth Smallest in BST β LC 230 | Medium
- Key insight: Inorder traversal of BST gives sorted order. Stop at kth element.
- Tricky: Iterative inorder with early exit is more efficient than collecting all.
- Common mistakes: Collecting entire inorder list then indexing β wastes memory.
- Time: O(h + k) | Space: O(h)
19. Construct Tree from Preorder+Inorder β LC 105 | Medium
- Key insight: Preorder[0] is root. Find root in inorder β splits into left/right subtrees. Recurse.
- Tricky: Use a hashmap for O(1) inorder index lookup. Track preorder index with nonlocal/pointer.
- Common mistakes: Linear search in inorder (O(nΒ²)). Off-by-one in index slicing.
- Time: O(n) | Space: O(n)
20. House Robber III β LC 337 | Medium
- Key insight: At each node, return (rob_this, skip_this). rob_this = node.val + skip_left + skip_right. skip_this = max(rob_left, skip_left) + max(rob_right, skip_right).
- Tricky: Return a pair from each recursive call to avoid recomputation.
- Common mistakes: Using memoization with a dict β works but the pair-return approach is cleaner and O(n).
- Time: O(n) | Space: O(h)
21. Delete Leaves with Given Value β LC 1325 | Medium
- Key insight: Postorder β process children first, then check if current node became a leaf with target value.
- Tricky: After deleting children, the parent might now be a leaf with target value too.
- Common mistakes: Preorder traversal β misses nodes that become leaves after children are deleted.
- Time: O(n) | Space: O(h)
22. Binary Tree Maximum Path Sum β LC 124 | Hard
- Key insight: At each node, max gain = node.val + max(0, left_gain) + max(0, right_gain) for the global answer. But return only node.val + max(0, max(left, right)) upward (can't split).
- Tricky: The path through a node can't branch when returned to parent. Two different computations: one for global max, one for return value.
- Common mistakes: Returning the full path sum (with both children) to parent β invalid since a path can't fork.
- Time: O(n) | Space: O(h)
23. Serialize/Deserialize Binary Tree β LC 297 | Hard
- Key insight: Preorder with null markers. Serialize: "1,2,N,N,3,N,N". Deserialize: use a queue/iterator of tokens.
- Tricky: Deserialize needs a mutable index/iterator. Use
collections.dequeandpopleft(). - Common mistakes: Using a list index without making it mutable (nonlocal or iterator). Forgetting null markers.
- Time: O(n) | Space: O(n)
24. Two Sum BSTs β LC 1214 | Medium (Premium)
- Key insight: Inorder of each BST gives a sorted array β classic Two Sum II with two pointers (i from left of tree1's inorder, j from right of tree2's inorder).
- Tricky: Unlike LC 653 (single BST), you need two structures. Can't do a single-pass hash scan across both trees simultaneously.
- Alternative: For each node in tree1, BST-search for
target - valin tree2. O(nΒ·log m) but O(h) space. - Common mistakes: Using LC 653's hash-set approach β works but doesn't exploit the two-BST structure optimally.
- Time: O(n+m) inorder approach | Space: O(n+m)
Edge Cases to Watch For
- Null/empty tree: Always handle
if not root: return ...first. - Single node tree: Diameter = 0, depth = 1, it's both a leaf and root.
- Skewed trees: Stack overflow risk in deep recursion. Height = n for a linked-list tree.
- BST with duplicates: Clarify if duplicates are allowed and which side they go.
- Negative values: Max path sum β don't assume all values are positive. Use
max(0, gain)to ignore negative branches. - Path problems: "Path" usually means no revisiting nodes. Clarify if it must go root-to-leaf or any node-to-node.
- Integer overflow: Max path sum with large values β use Python (no overflow) or long in Java/C++.
| # | Problem | LC | Difficulty |
|---|---|---|---|
| 1 | Binary Tree Inorder Traversal | 94 | π’ Easy |
| 2 | Binary Tree Preorder Traversal | 144 | π’ Easy |
| 3 | Binary Tree Postorder Traversal | 145 | π’ Easy |
| 4 | Invert Binary Tree | 226 | π’ Easy |
| 5 | Maximum Depth of Binary Tree | 104 | π’ Easy |
| 6 | Diameter of Binary Tree | 543 | π’ Easy |
| 7 | Balanced Binary Tree | 110 | π’ Easy |
| 8 | Same Tree | 100 | π’ Easy |
| 9 | Subtree of Another Tree | 572 | π’ Easy |
| 10 | Lowest Common Ancestor of BST | 235 | π‘ Medium |
| 11 | Insert into BST | 701 | π‘ Medium |
| 12 | Delete Node in BST | 450 | π‘ Medium |
| 13 | Binary Tree Level Order Traversal | 102 | π‘ Medium |
| 14 | Binary Tree Right Side View | 199 | π‘ Medium |
| 15 | Construct Quad Tree | 427 | π‘ Medium |
| 16 | Count Good Nodes | 1448 | π‘ Medium |
| 17 | Validate BST | 98 | π‘ Medium |
| 18 | Kth Smallest Element in BST | 230 | π‘ Medium |
| 19 | Construct Tree from Preorder and Inorder | 105 | π‘ Medium |
| 20 | House Robber III | 337 | π‘ Medium |
| 21 | Delete Leaves With Given Value | 1325 | π‘ Medium |
| 22 | Binary Tree Maximum Path Sum | 124 | π΄ Hard |
| 23 | Serialize And Deserialize Binary Tree | 297 | π΄ Hard |
| 24 | Zigzag Level Order Traversal | 103 | π‘ Medium |
| 25 | Boundary Traversal | 545 | π‘ Medium |
| 26 | Vertical Order Traversal | 987 | π΄ Hard |
| 27 | Top View of Binary Tree | β | π‘ Medium |
| 28 | Bottom View of Binary Tree | β | π‘ Medium |
| 29 | Symmetric Tree | 101 | π’ Easy |
| 30 | Root to Leaf Paths | 257 | π’ Easy |
| 31 | Lowest Common Ancestor of Binary Tree | 236 | π‘ Medium |
| 32 | Maximum Width of Binary Tree | 662 | π‘ Medium |
| 33 | All Nodes Distance K | 863 | π‘ Medium |
| 34 | Burn Binary Tree | 2385 | π΄ Hard |
| 35 | Count Complete Tree Nodes | 222 | π’ Easy |
| 36 | Construct BT from Postorder and Inorder | 106 | π‘ Medium |
| 37 | Morris Traversal | β | π‘ Medium |
| 38 | Flatten Binary Tree to Linked List | 114 | π‘ Medium |
| 39 | Search in BST | 700 | π’ Easy |
| 40 | Floor and Ceil in BST | β | π‘ Medium |
| 41 | Construct BST from Preorder | 1008 | π‘ Medium |
| 42 | Inorder Successor/Predecessor in BST | β | π‘ Medium |
| 43 | Merge Two BSTs | β | π‘ Medium |
| 44 | Two Sum in BST | 653 | π’ Easy |
| 45 | Recover BST | 99 | π‘ Medium |
| 46 | Largest BST in Binary Tree | 1373 | π΄ Hard |
All Nodes Distance K βΌ expand
"""
LC 863 - All Nodes Distance K in Binary Tree
Problem: Find all nodes at distance k from a target node.
Example:
Input: [3,5,1,6,2,0,8,null,null,7,4], target=5, k=2
Output: [7,4,1]
Constraints:
- Number of nodes: [1, 500], all unique values
- 0 <= k <= 1000
"""
from typing import List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def distanceK(self, root: TreeNode, target: TreeNode, k: int) -> List[int]:
# Approach: build parent map so we can traverse upward,
# then BFS from target in all 3 directions (left, right, parent) β O(n)
parent = {}
def build_parents(node, par=None):
if not node: return
parent[node] = par
build_parents(node.left, node)
build_parents(node.right, node)
build_parents(root)
# BFS from target, avoid revisiting
queue = deque([(target, 0)])
visited = {target}
result = []
while queue:
node, dist = queue.popleft()
if dist == k:
result.append(node.val)
continue
for neighbor in (node.left, node.right, parent[node]):
if neighbor and neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, dist + 1))
return result
if __name__ == "__main__":
sol = Solution()
root = build_tree([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4])
def find(node, val):
if not node: return None
if node.val == val: return node
return find(node.left, val) or find(node.right, val)
assert sorted(sol.distanceK(root, find(root, 5), 2)) == sorted([7, 4, 1])
assert sol.distanceK(root, find(root, 5), 0) == [5]
print("All tests passed.") Balanced Binary Tree βΌ expand
"""
# LC 110: Balanced Binary Tree
Given a binary tree, determine if it is height-balanced β every node's left and
right subtrees differ in height by at most 1.
## Examples
```text
Input: root = [3, 9, 20, null, null, 15, 7]
Output: true
Input: root = [1, 2, 2, 3, 3, null, null, 4, 4]
Output: false
Input: root = []
Output: true
```
## Constraints
- The number of nodes in the tree is in the range [0, 5000].
- -10^4 <= Node.val <= 10^4
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 110. Balanced Binary Tree
Given a binary tree, determine if it is height-balanced.
A height-balanced binary tree is a binary tree in which the depth of the two subtrees
of every node never differs by more than one.
### Examples
```text
Input: root = [3, 9, 20, null, null, 15, 7]
Output: true
Input: root = [1, 2, 2, 3, 3, null, null, 4, 4]
Output: false
Input: root = []
Output: true
```
### Constraints
- The number of nodes in the tree is in the range `[0, 5000]`.
- `-10^4 <= Node.val <= 10^4`
"""
def isBalanced_topdown(self, root: Optional[TreeNode]) -> bool:
# Time: O(n^2), Space: O(n)
def height(node):
if not node:
return 0
return 1 + max(height(node.left), height(node.right))
if not root:
return True
if abs(height(root.left) - height(root.right)) > 1:
return False
return self.isBalanced_topdown(root.left) and self.isBalanced_topdown(root.right)
def isBalanced(self, root: Optional[TreeNode]) -> bool:
# Return height if balanced, -1 if not; propagate -1 upward on imbalance
# A node is balanced if both subtrees are balanced and heights differ by <= 1
# Time: O(n), Space: O(n)
def check(node):
if not node:
return 0
left = check(node.left)
if left == -1:
return -1
right = check(node.right)
if right == -1:
return -1
if abs(left - right) > 1:
return -1
return 1 + max(left, right)
return check(root) != -1
if __name__ == '__main__':
s = Solution()
assert s.isBalanced(build_tree([3, 9, 20, None, None, 15, 7])) is True
assert s.isBalanced_topdown(build_tree([3, 9, 20, None, None, 15, 7])) is True
assert s.isBalanced(build_tree([1, 2, 2, 3, 3, None, None, 4, 4])) is False
assert s.isBalanced(None) is True
print("All tests passed.") Binary Tree Inorder Traversal βΌ expand
"""
# LC 94: Binary Tree Inorder Traversal
Return the inorder traversal (left -> root -> right) of a binary tree's node values.
## Examples
```text
Input: root = [1, null, 2, 3]
Output: [1, 3, 2]
Input: root = []
Output: []
Input: root = [1]
Output: [1]
```
## Constraints
- The number of nodes in the tree is in the range [0, 100].
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 94. Binary Tree Inorder Traversal
Given the `root` of a binary tree, return the inorder traversal of its nodes' values.
### Examples
```text
Input: root = [1, null, 2, 3]
Output: [1, 3, 2]
Input: root = []
Output: []
Input: root = [1]
Output: [1]
```
### Constraints
- The number of nodes in the tree is in the range `[0, 100]`.
- `-100 <= Node.val <= 100`
"""
def inorderTraversal_recursive(self, root: Optional[TreeNode]) -> List[int]:
# Time: O(n), Space: O(n)
result = []
def dfs(node):
if not node:
return
dfs(node.left)
result.append(node.val)
dfs(node.right)
dfs(root)
return result
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# Recursive in-order: left subtree, root, right subtree
# Time: O(n), Space: O(n)
result, stack = [], []
curr = root
while curr or stack:
while curr:
stack.append(curr)
curr = curr.left
curr = stack.pop()
result.append(curr.val)
curr = curr.right
return result
if __name__ == '__main__':
s = Solution()
root = build_tree([1, None, 2, 3])
assert s.inorderTraversal(root) == [1, 3, 2]
assert s.inorderTraversal_recursive(build_tree([1, None, 2, 3])) == [1, 3, 2]
assert s.inorderTraversal(None) == []
assert s.inorderTraversal(build_tree([1])) == [1]
print("All tests passed.") Binary Tree Level Order Traversal βΌ expand
"""
# LC 102: Binary Tree Level Order Traversal
Return the level-order traversal of a binary tree as a list of lists (one list per
level, left to right).
## Examples
```text
Input: root = [3, 9, 20, null, null, 15, 7]
Output: [[3], [9, 20], [15, 7]]
Input: root = [1]
Output: [[1]]
Input: root = []
Output: []
```
## Constraints
- The number of nodes in the tree is in the range [0, 2000].
- -1000 <= Node.val <= 1000
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 102. Binary Tree Level Order Traversal
Given the `root` of a binary tree, return the level order traversal of its nodes' values
(i.e., from left to right, level by level).
### Examples
```text
Example 1:
Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]
Example 2:
Input: root = [1]
Output: [[1]]
Example 3:
Input: root = []
Output: []
```
### Constraints
- The number of nodes in the tree is in the range `[0, 2000]`.
- `-1000 <= Node.val <= 1000`
"""
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
# BFS with a queue; process all nodes at current level before moving to next
# Time: O(n), Space: O(n) β process nodes level by level using a queue
if not root:
return []
result, queue = [], deque([root])
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
def levelOrder_dfs(self, root: Optional[TreeNode]) -> List[List[int]]:
# DFS with depth index; extend result list when reaching a new depth level
# Time: O(n), Space: O(n) β use recursion depth as level index
result = []
def dfs(node: Optional[TreeNode], depth: int) -> None:
if not node:
return
if depth == len(result):
result.append([])
result[depth].append(node.val)
dfs(node.left, depth + 1)
dfs(node.right, depth + 1)
dfs(root, 0)
return result
if __name__ == '__main__':
sol = Solution()
root = build_tree([3, 9, 20, None, None, 15, 7])
assert sol.levelOrder(root) == [[3], [9, 20], [15, 7]]
assert sol.levelOrder_dfs(root) == [[3], [9, 20], [15, 7]]
assert sol.levelOrder(build_tree([1])) == [[1]]
assert sol.levelOrder(None) == []
print("All tests passed.") Binary Tree Maximum Path Sum βΌ expand
"""
# LC 124: Binary Tree Maximum Path Sum
Find the maximum sum of any path in a binary tree. A path is any sequence of nodes
connected by edges; it does not need to pass through the root.
## Examples
```text
Input: root = [1, 2, 3]
Output: 6
Input: root = [-10, 9, 20, null, null, 15, 7]
Output: 42
```
## Constraints
- The number of nodes in the tree is in the range [1, 3 * 10^4].
- -1000 <= Node.val <= 1000
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 124. Binary Tree Maximum Path Sum
A **path** in a binary tree is a sequence of nodes where each pair of adjacent nodes in
the sequence has an edge connecting them. A node can only appear in the sequence **at most
once**. Note that the path does not need to pass through the root.
The **path sum** of a path is the sum of the node's values in the path.
Given the `root` of a binary tree, return the maximum path sum of any **non-empty** path.
---
### Examples
```text
Example 1:
Input: root = [1, 2, 3]
Output: 6
Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.
Example 2:
Input: root = [-10, 9, 20, null, null, 15, 7]
Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.
```
---
### Constraints
- The number of nodes in the tree is in the range [1, 3 * 10^4].
- -1000 <= Node.val <= 1000
"""
def maxPathSum(self, root: Optional[TreeNode]) -> int: # O(n) time, O(h) space
# For each node, max path through it = node.val + max(0, left) + max(0, right)
# Update global max; return node.val + max(0, best single branch) to parent
self.max_sum = float('-inf')
def dfs(node):
if not node:
return 0
left = max(dfs(node.left), 0)
right = max(dfs(node.right), 0)
self.max_sum = max(self.max_sum, node.val + left + right)
return node.val + max(left, right)
dfs(root)
return self.max_sum
if __name__ == '__main__':
s = Solution()
root1 = build_tree([1, 2, 3])
assert s.maxPathSum(root1) == 6
root2 = build_tree([-10, 9, 20, None, None, 15, 7])
assert s.maxPathSum(root2) == 42
root3 = build_tree([-3])
assert s.maxPathSum(root3) == -3
print("All tests passed.") Binary Tree Postorder Traversal βΌ expand
"""
# LC 145: Binary Tree Postorder Traversal
Return the postorder traversal (left -> right -> root) of a binary tree's node values.
## Examples
```text
Input: root = [1, null, 2, 3]
Output: [3, 2, 1]
Input: root = []
Output: []
Input: root = [1]
Output: [1]
```
## Constraints
- The number of nodes in the tree is in the range [0, 100].
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 145. Binary Tree Postorder Traversal
Given the `root` of a binary tree, return the postorder traversal of its nodes' values.
### Examples
```text
Input: root = [1, null, 2, 3]
Output: [3, 2, 1]
Input: root = []
Output: []
Input: root = [1]
Output: [1]
```
### Constraints
- The number of nodes in the tree is in the range `[0, 100]`.
- `-100 <= Node.val <= 100`
"""
def postorderTraversal_recursive(self, root: Optional[TreeNode]) -> List[int]:
# Time: O(n), Space: O(n)
result = []
def dfs(node):
if not node:
return
dfs(node.left)
dfs(node.right)
result.append(node.val)
dfs(root)
return result
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# Recursive post-order: left subtree, right subtree, root
# Time: O(n), Space: O(n)
if not root:
return []
stack1, stack2 = [root], []
while stack1:
node = stack1.pop()
stack2.append(node.val)
if node.left:
stack1.append(node.left)
if node.right:
stack1.append(node.right)
return stack2[::-1]
if __name__ == '__main__':
s = Solution()
assert s.postorderTraversal(build_tree([1, None, 2, 3])) == [3, 2, 1]
assert s.postorderTraversal_recursive(build_tree([1, None, 2, 3])) == [3, 2, 1]
assert s.postorderTraversal(None) == []
assert s.postorderTraversal(build_tree([1])) == [1]
print("All tests passed.") Binary Tree Preorder Traversal βΌ expand
"""
# LC 144: Binary Tree Preorder Traversal
Return the preorder traversal (root -> left -> right) of a binary tree's node values.
## Examples
```text
Input: root = [1, null, 2, 3]
Output: [1, 2, 3]
Input: root = []
Output: []
Input: root = [1]
Output: [1]
```
## Constraints
- The number of nodes in the tree is in the range [0, 100].
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 144. Binary Tree Preorder Traversal
Given the `root` of a binary tree, return the preorder traversal of its nodes' values.
### Examples
```text
Input: root = [1, null, 2, 3]
Output: [1, 2, 3]
Input: root = []
Output: []
Input: root = [1]
Output: [1]
```
### Constraints
- The number of nodes in the tree is in the range `[0, 100]`.
- `-100 <= Node.val <= 100`
"""
def preorderTraversal_recursive(self, root: Optional[TreeNode]) -> List[int]:
# Time: O(n), Space: O(n)
result = []
def dfs(node):
if not node:
return
result.append(node.val)
dfs(node.left)
dfs(node.right)
dfs(root)
return result
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# Recursive pre-order: root, left subtree, right subtree
# Time: O(n), Space: O(n)
if not root:
return []
result, stack = [], [root]
while stack:
node = stack.pop()
result.append(node.val)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return result
if __name__ == '__main__':
s = Solution()
assert s.preorderTraversal(build_tree([1, None, 2, 3])) == [1, 2, 3]
assert s.preorderTraversal_recursive(build_tree([1, None, 2, 3])) == [1, 2, 3]
assert s.preorderTraversal(None) == []
assert s.preorderTraversal(build_tree([1])) == [1]
print("All tests passed.") Binary Tree Right Side View βΌ expand
"""
# LC 199: Binary Tree Right Side View
Return the values of nodes visible when looking at the tree from the right side,
ordered top to bottom.
## Examples
```text
Input: root = [1, 2, 3, null, 5, null, 4]
Output: [1, 3, 4]
Input: root = [1, null, 3]
Output: [1, 3]
Input: root = []
Output: []
```
## Constraints
- The number of nodes in the tree is in the range [0, 100].
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 199. Binary Tree Right Side View
Given the `root` of a binary tree, imagine yourself standing on the right side of it.
Return the values of the nodes you can see ordered from top to bottom.
### Examples
```text
Example 1:
Input: root = [1,2,3,null,5,null,4]
Output: [1,3,4]
Example 2:
Input: root = [1,null,3]
Output: [1,3]
Example 3:
Input: root = []
Output: []
```
### Constraints
- The number of nodes in the tree is in the range `[0, 100]`.
- `-100 <= Node.val <= 100`
"""
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
# BFS level by level; the last node in each level is visible from the right
# Time: O(n), Space: O(n) β take the last node of each BFS level
if not root:
return []
result, queue = [], deque([root])
while queue:
level_size = len(queue)
for i in range(level_size):
node = queue.popleft()
if i == level_size - 1:
result.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return result
def rightSideView_dfs(self, root: Optional[TreeNode]) -> List[int]:
# Time: O(n), Space: O(h) β visit right child first; first visit at each depth is visible
result = []
def dfs(node: Optional[TreeNode], depth: int) -> None:
if not node:
return
if depth == len(result):
result.append(node.val)
dfs(node.right, depth + 1)
dfs(node.left, depth + 1)
dfs(root, 0)
return result
if __name__ == '__main__':
sol = Solution()
root = build_tree([1, 2, 3, None, 5, None, 4])
assert sol.rightSideView(root) == [1, 3, 4]
assert sol.rightSideView_dfs(root) == [1, 3, 4]
root2 = build_tree([1, None, 3])
assert sol.rightSideView(root2) == [1, 3]
assert sol.rightSideView_dfs(root2) == [1, 3]
assert sol.rightSideView(None) == []
print("All tests passed.") Bottom View Of Binary Tree βΌ expand
"""
GFG - Bottom View of Binary Tree
Problem: Return the bottom view of a binary tree β the last node visible at each
horizontal distance when viewed from below.
Example:
Input: [1,2,3,4,5,6,7]
Output: [4,2,6,3,7]
Constraints:
- Number of nodes: [1, 10^5]
- 1 <= Node.val <= 10^5
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
q.append(node.right)
i += 1
return root
class Solution:
def bottomView(self, root: Optional[TreeNode]) -> List[int]:
# Approach: BFS with column tracking; keep LAST seen per column β O(n)
if not root:
return []
col_map = {} # col -> last node val seen (bottom-most)
queue = deque([(root, 0)]) # (node, col)
while queue:
node, col = queue.popleft()
col_map[col] = node.val # always overwrite = last seen = bottom view
if node.left: queue.append((node.left, col - 1))
if node.right: queue.append((node.right, col + 1))
return [col_map[c] for c in sorted(col_map)]
if __name__ == "__main__":
sol = Solution()
root = build_tree([1, 2, 3, 4, 5, 6, 7])
assert sol.bottomView(root) == [4, 2, 6, 3, 7]
root2 = build_tree([1, 2, 3])
assert sol.bottomView(root2) == [2, 1, 3]
print("All tests passed.") Boundary Traversal βΌ expand
"""
LC 545 / GFG - Boundary Traversal of Binary Tree
Problem: Return the boundary of a binary tree anticlockwise starting from root.
Boundary = left boundary (excl. leaves) + all leaves + right boundary reversed (excl. leaves).
Example:
Input: [1,2,3,4,5,6,7]
Output: [1,2,4,5,6,7,3]
Constraints:
- Number of nodes: [1, 10^4]
- -1000 <= Node.val <= 1000
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
q.append(node.right)
i += 1
return root
class Solution:
def boundaryOfBinaryTree(self, root: Optional[TreeNode]) -> List[int]:
# Approach: three passes β left boundary, leaves, right boundary β O(n)
if not root:
return []
def is_leaf(node):
return not node.left and not node.right
def left_boundary(node):
res = []
while node:
if not is_leaf(node):
res.append(node.val)
# prefer left child, fall back to right
node = node.left if node.left else node.right
return res
def leaves(node):
if not node: return []
if is_leaf(node): return [node.val]
return leaves(node.left) + leaves(node.right)
def right_boundary(node):
res = []
while node:
if not is_leaf(node):
res.append(node.val)
# prefer right child, fall back to left
node = node.right if node.right else node.left
return res[::-1] # reverse so it goes bottom-up
return (
[root.val]
+ left_boundary(root.left)
+ leaves(root.left)
+ leaves(root.right)
+ right_boundary(root.right)
)
if __name__ == "__main__":
sol = Solution()
root = build_tree([1, 2, 3, 4, 5, 6, 7])
assert sol.boundaryOfBinaryTree(root) == [1, 2, 4, 5, 6, 7, 3]
root2 = build_tree([1])
assert sol.boundaryOfBinaryTree(root2) == [1]
print("All tests passed.") Burn Binary Tree βΌ expand
"""
LC 2385 - Amount of Time for Binary Tree to Be Infected
Problem: Starting from node `start`, fire spreads to adjacent nodes each second.
Return the minimum time to burn the entire tree.
Example:
Input: [1,5,3,null,4,10,6,9,2], start=3
Output: 4
Constraints:
- Number of nodes: [1, 10^5], all unique
- 1 <= start <= 10^5
"""
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def amountOfTime(self, root: Optional[TreeNode], start: int) -> int:
# Approach: build parent map + locate start node,
# then BFS spreading in all directions β O(n)
parent = {}
start_node = None
def build(node, par=None):
nonlocal start_node
if not node: return
parent[node] = par
if node.val == start: start_node = node
build(node.left, node)
build(node.right, node)
build(root)
visited = {start_node}
queue = deque([start_node])
time = -1
while queue:
time += 1
for _ in range(len(queue)):
node = queue.popleft()
for nb in (node.left, node.right, parent[node]):
if nb and nb not in visited:
visited.add(nb)
queue.append(nb)
return time
if __name__ == "__main__":
sol = Solution()
assert sol.amountOfTime(build_tree([1, 5, 3, None, 4, 10, 6, 9, 2]), 3) == 4
assert sol.amountOfTime(build_tree([1]), 1) == 0
print("All tests passed.") Construct Binary Tree From Preorder And Inorder βΌ expand
"""
# LC 105: Construct Binary Tree from Preorder and Inorder Traversal
Given preorder and inorder traversal arrays of a binary tree (all values unique),
reconstruct and return the tree.
## Examples
```text
Input: preorder = [3, 9, 20, 15, 7], inorder = [9, 3, 15, 20, 7]
Output: [3, 9, 20, null, null, 15, 7]
Input: preorder = [-1], inorder = [-1]
Output: [-1]
```
## Constraints
- 1 <= preorder.length <= 3000
- inorder.length == preorder.length
- preorder and inorder consist of unique values.
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree_from_list(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
def tree_to_list(root):
if not root:
return []
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
while result and result[-1] is None:
result.pop()
return result
class Solution:
"""
## 105. Construct Binary Tree from Preorder and Inorder Traversal
Given two integer arrays `preorder` and `inorder` where:
- `preorder` is the preorder traversal of a binary tree, and
- `inorder` is the inorder traversal of the same tree,
construct and return the binary tree.
---
### Examples
```text
Example 1:
Input: preorder = [3, 9, 20, 15, 7], inorder = [9, 3, 15, 20, 7]
Output: [3, 9, 20, null, null, 15, 7]
Example 2:
Input: preorder = [-1], inorder = [-1]
Output: [-1]
```
---
### Constraints
- 1 <= preorder.length <= 3000
- inorder.length == preorder.length
- -3000 <= preorder[i], inorder[i] <= 3000
- `preorder` and `inorder` consist of unique values.
- Each value of `inorder` also appears in `preorder`.
- `preorder` is guaranteed to be the preorder traversal of the tree.
- `inorder` is guaranteed to be the inorder traversal of the tree.
"""
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: # O(n^2) time, O(n) space
# Preorder[0] is always the root; find it in inorder to split left/right
# Left subtree has len(left_inorder) nodes in preorder; recurse on both halves
if not preorder:
return None
root_val = preorder[0]
mid = inorder.index(root_val)
root = TreeNode(root_val)
root.left = self.buildTree(preorder[1:mid + 1], inorder[:mid])
root.right = self.buildTree(preorder[mid + 1:], inorder[mid + 1:])
return root
def buildTree_hashmap(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: # O(n) time, O(n) space
idx_map = {val: i for i, val in enumerate(inorder)}
self.pre_idx = 0
def helper(left, right):
if left > right:
return None
root_val = preorder[self.pre_idx]
self.pre_idx += 1
root = TreeNode(root_val)
mid = idx_map[root_val]
root.left = helper(left, mid - 1)
root.right = helper(mid + 1, right)
return root
return helper(0, len(inorder) - 1)
if __name__ == '__main__':
s = Solution()
pre, ino = [3, 9, 20, 15, 7], [9, 3, 15, 20, 7]
assert tree_to_list(s.buildTree(pre, ino)) == [3, 9, 20, None, None, 15, 7]
assert tree_to_list(s.buildTree_hashmap(pre, ino)) == [3, 9, 20, None, None, 15, 7]
pre2, ino2 = [-1], [-1]
assert tree_to_list(s.buildTree(pre2, ino2)) == [-1]
assert tree_to_list(s.buildTree_hashmap(pre2, ino2)) == [-1]
print("All tests passed.") Construct Bst From Preorder βΌ expand
"""
# LC 1008: Construct BST from Preorder Traversal
Given an array of integers `preorder` representing the preorder traversal of a BST,
construct and return the BST.
## Examples
```text
Input: preorder = [8, 5, 1, 7, 10, 12]
Output: [8, 5, 10, 1, 7, null, 12]
```
## Constraints
- 1 <= preorder.length <= 100
- 1 <= preorder[i] <= 1000
- All the values of preorder are unique.
"""
from __future__ import annotations
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
def tree_to_inorder(root: Optional[TreeNode]) -> list:
result = []
def dfs(node):
if node:
dfs(node.left)
result.append(node.val)
dfs(node.right)
dfs(root)
return result
class Solution:
# --- Approach 1: Recursive with upper bound O(n) ---
def bstFromPreorder_recursive(self, preorder: List[int]) -> Optional[TreeNode]:
self.idx = 0
def build(upper: float) -> Optional[TreeNode]:
# If all elements consumed or current value exceeds upper bound, stop
if self.idx == len(preorder) or preorder[self.idx] > upper:
return None
val = preorder[self.idx]
self.idx += 1
node = TreeNode(val)
node.left = build(val) # left subtree: values < val
node.right = build(upper) # right subtree: values < upper
return node
return build(float('inf'))
# --- Approach 2: Stack-based O(n) ---
def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
if not preorder:
return None
root = TreeNode(preorder[0])
stack = [root]
for val in preorder[1:]:
node = TreeNode(val)
if val < stack[-1].val:
stack[-1].left = node # smaller than top β left child
else:
# pop until we find the parent (last node smaller than val)
parent = stack[-1]
while stack and stack[-1].val < val:
parent = stack.pop()
parent.right = node # attach as right child
stack.append(node)
return root
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# Test 1: standard case
preorder = [8, 5, 1, 7, 10, 12]
expected_inorder = sorted(preorder)
t1 = sol.bstFromPreorder(preorder)
assert tree_to_inorder(t1) == expected_inorder, f"Got {tree_to_inorder(t1)}"
t2 = sol.bstFromPreorder_recursive(preorder)
assert tree_to_inorder(t2) == expected_inorder
# Test 2: single element
t = sol.bstFromPreorder([1])
assert t.val == 1 and t.left is None and t.right is None
# Test 3: sorted ascending (right-skewed)
preorder = [1, 2, 3, 4]
t = sol.bstFromPreorder(preorder)
assert tree_to_inorder(t) == [1, 2, 3, 4]
# Test 4: sorted descending (left-skewed)
preorder = [4, 3, 2, 1]
t = sol.bstFromPreorder(preorder)
assert tree_to_inorder(t) == [1, 2, 3, 4]
print("All tests passed!") Construct Bt From Postorder And Inorder βΌ expand
"""
LC 106 - Construct Binary Tree from Inorder and Postorder Traversal
Problem: Build a binary tree given its inorder and postorder traversals.
Example:
Input: inorder=[9,3,15,20,7], postorder=[9,15,7,20,3]
Output: [3,9,20,null,null,15,7]
Constraints:
- 1 <= len(inorder) <= 3000, all values unique
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
# Approach: last element of postorder is root; find it in inorder to
# split left/right subtrees. Hash map gives O(1) lookup β O(n) total.
idx_map = {val: i for i, val in enumerate(inorder)}
def helper(in_left, in_right):
if in_left > in_right: return None
root_val = postorder.pop() # rightmost = current root
root = TreeNode(root_val)
mid = idx_map[root_val]
# build right before left (postorder pops from end)
root.right = helper(mid + 1, in_right)
root.left = helper(in_left, mid - 1)
return root
return helper(0, len(inorder) - 1)
if __name__ == "__main__":
from collections import deque
def tree_to_list(root):
if not root: return []
res, q = [], deque([root])
while q:
node = q.popleft()
res.append(node.val if node else None)
if node:
q.append(node.left)
q.append(node.right)
while res and res[-1] is None: res.pop()
return res
sol = Solution()
root = sol.buildTree([9, 3, 15, 20, 7], [9, 15, 7, 20, 3])
assert tree_to_list(root) == [3, 9, 20, None, None, 15, 7]
print("All tests passed.") Construct Quad Tree βΌ expand
"""
# LC 427: Construct Quad Tree
Given an n x n binary grid (n is a power of 2), construct a Quad-Tree. Each node is a
leaf if its region is uniform (all 0s or all 1s); otherwise split into four equal
quadrants and recurse.
## Examples
```text
Input: grid = [[0, 1], [1, 0]]
Output: [[0, 1], [1, 0], [1, 1], [1, 1], [1, 0]]
Input: grid = [[1, 1], [1, 1]]
Output: [[1, 1]]
```
## Constraints
- n == grid.length == grid[i].length
- n == 2^x where 0 <= x <= 6
- grid[i][j] is 0 or 1.
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Node:
def __init__(self, val, isLeaf, topLeft=None, topRight=None, bottomLeft=None, bottomRight=None):
self.val = val
self.isLeaf = isLeaf
self.topLeft = topLeft
self.topRight = topRight
self.bottomLeft = bottomLeft
self.bottomRight = bottomRight
class Solution:
"""
## 427. Construct Quad Tree
Given a `n * n` matrix `grid` of `0`s and `1`s, return the corresponding Quad-Tree.
A Quad-Tree is a tree data structure in which each internal node has exactly four children.
Each node has two attributes:
- `val`: `True` if the node represents a grid of all `1`s, or `False` if all `0`s. For
non-leaf nodes, the value can be either `True` or `False`.
- `isLeaf`: `True` if the node is a leaf node, or `False` if it has four children.
We can construct a Quad-Tree from a 2D area using the following steps:
1. If the current grid has the same value (all `1`s or all `0`s), set `isLeaf` to `True`
and set `val` to the value of the grid and set the four children to `None`.
2. If the current grid has different values, set `isLeaf` to `False` and set `val` to any
value and divide the current grid into four sub-grids.
3. Recurse for each of the children with the proper sub-grid.
### Examples
```text
Example 1:
Input: grid = [[0,1],[1,0]]
Output: [[False,False],[True,1],[True,1],[True,1],[True,0]]
(Serialized quad-tree format)
Example 2:
Input: grid = [[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],
[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0]]
Output: [[False,False],[True,1],[True,1],[False,False],[True,1],[True,1],[True,0],[True,0],[True,0]]
```
### Constraints
- `n == grid.length == grid[i].length`
- `n == 2^x` where `0 <= x <= 6`
"""
def construct(self, grid: List[List[int]]) -> 'Node':
# Check if all values in region are the same β if so, create leaf node
# Otherwise split into 4 quadrants and recurse on each
# Time: O(n^2 log n), Space: O(log n) β recursively split grid into four quadrants
def build(r: int, c: int, size: int) -> Node:
val = grid[r][c]
all_same = all(grid[r + dr][c + dc] == val
for dr in range(size) for dc in range(size))
if all_same:
return Node(bool(val), True)
half = size // 2
return Node(
True, False,
build(r, c, half),
build(r, c + half, half),
build(r + half, c, half),
build(r + half, c + half, half),
)
return build(0, 0, len(grid))
if __name__ == '__main__':
sol = Solution()
result = sol.construct([[0, 1], [1, 0]])
assert result.isLeaf == False
assert result.topLeft.isLeaf == True and result.topLeft.val == False
assert result.topRight.isLeaf == True and result.topRight.val == True
result2 = sol.construct([[1]])
assert result2.isLeaf == True and result2.val == True
result3 = sol.construct([[0]])
assert result3.isLeaf == True and result3.val == False
print("All tests passed.") Count Complete Tree Nodes βΌ expand
"""
LC 222 - Count Complete Tree Nodes
Problem: Count nodes in a complete binary tree more efficiently than O(n).
Example:
Input: [1,2,3,4,5,6]
Output: 6
Constraints:
- Number of nodes: [0, 5 * 10^4]
- Complete binary tree guaranteed
"""
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
# Approach 1: simple DFS β O(n)
if not root: return 0
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
def countNodes_fast(self, root: Optional[TreeNode]) -> int:
# Approach 2: compare left-most and right-most heights.
# If equal, left subtree is a perfect binary tree β skip it β O(log^2 n)
if not root: return 0
left_h, right_h = 0, 0
l, r = root, root
while l: left_h += 1; l = l.left # height going left
while r: right_h += 1; r = r.right # height going right
if left_h == right_h:
return (1 << left_h) - 1 # perfect tree: 2^h - 1 nodes
# not perfect: recurse on both subtrees
return 1 + self.countNodes_fast(root.left) + self.countNodes_fast(root.right)
if __name__ == "__main__":
sol = Solution()
assert sol.countNodes(build_tree([1, 2, 3, 4, 5, 6])) == 6
assert sol.countNodes_fast(build_tree([1, 2, 3, 4, 5, 6])) == 6
assert sol.countNodes_fast(build_tree([])) == 0
assert sol.countNodes_fast(build_tree([1])) == 1
print("All tests passed.") Count Good Nodes βΌ expand
"""
# LC 1448: Count Good Nodes in Binary Tree
A node X is "good" if no node on the path from root to X has a value greater than X.
Return the count of good nodes in the tree.
## Examples
```text
Input: root = [3, 1, 4, 3, null, 1, 5]
Output: 4
Input: root = [3, 3, null, 4, 2]
Output: 3
Input: root = [1]
Output: 1
```
## Constraints
- The number of nodes in the binary tree is in the range [1, 10^5].
- Each node's value is between [-10^4, 10^4].
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 1448. Count Good Nodes in Binary Tree
Given a binary tree `root`, a node X in the tree is named **good** if in the path from root
to X there are no nodes with a value greater than X.
Return the number of good nodes in the binary tree.
### Examples
```text
Example 1:
Input: root = [3,1,4,3,null,1,5]
Output: 4
Explanation: Nodes 3 (root), 3 (left-left), 4 (right), and 5 (right-right) are good.
Example 2:
Input: root = [3,3,null,4,2]
Output: 3
Explanation: Nodes 3 (root), 3 (left), and 4 (left-left) are good.
Example 3:
Input: root = [1]
Output: 1
```
### Constraints
- The number of nodes in the binary tree is in the range `[1, 10^5]`.
- Each node's value is between `[-10^4, 10^4]`.
"""
def goodNodes(self, root: TreeNode) -> int:
# A node is 'good' if its value >= max value seen on path from root
# DFS passing the running max; count good nodes bottom-up
# Time: O(n), Space: O(h) β DFS tracking the max value seen on the path from root
def dfs(node: Optional[TreeNode], max_so_far: int) -> int:
if not node:
return 0
count = 1 if node.val >= max_so_far else 0
new_max = max(max_so_far, node.val)
return count + dfs(node.left, new_max) + dfs(node.right, new_max)
return dfs(root, root.val)
if __name__ == '__main__':
sol = Solution()
root = build_tree([3, 1, 4, 3, None, 1, 5])
assert sol.goodNodes(root) == 4
root2 = build_tree([3, 3, None, 4, 2])
assert sol.goodNodes(root2) == 3
root3 = build_tree([1])
assert sol.goodNodes(root3) == 1
print("All tests passed.") Delete Leaves With Given Value βΌ expand
"""
# LC 1325: Delete Leaves With a Given Value
Delete all leaf nodes with value equal to `target`. Repeat until no such leaves remain
(a node that becomes a leaf after deletion should also be deleted if its value equals
`target`).
## Examples
```text
Input: root = [1, 2, 3, 2, null, 2, 4], target = 2
Output: [1, null, 3, null, 4]
Input: root = [1, 3, 3, 3, 2], target = 3
Output: [1, 3, null, null, 2]
```
## Constraints
- The number of nodes in the tree is in the range [1, 3000].
- 1 <= Node.val, target <= 1000
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
def tree_to_list(root):
if not root:
return []
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
while result and result[-1] is None:
result.pop()
return result
class Solution:
"""
## 1325. Delete Leaves With a Given Value
Given a binary tree `root` and an integer `target`, delete all the **leaf nodes** with
value `target`.
Note that once you delete a leaf node with value `target`, if its parent node becomes a
leaf node and has the value `target`, it should also be deleted (you need to continue
doing this until you can't).
---
### Examples
```text
Example 1:
Input: root = [1, 2, 3, 2, null, 2, 4], target = 2
Output: [1, null, 3, null, 4]
Explanation: Leaf nodes with value 2 are removed. Node 2 (left child of root)
then becomes a leaf with value 2 and is also removed.
Example 2:
Input: root = [1, 3, 3, 3, 2], target = 3
Output: [1, 3, null, null, 2]
Example 3:
Input: root = [1, 2, null, 2, null, 2], target = 2
Output: [1]
```
---
### Constraints
- The number of nodes in the tree is in the range [1, 3000].
- 1 <= Node.val, target <= 1000
"""
def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: # O(n) time, O(h) space
# Post-order: process children first, then check if current node became a leaf with target value
if not root:
return None
root.left = self.removeLeafNodes(root.left, target)
root.right = self.removeLeafNodes(root.right, target)
if not root.left and not root.right and root.val == target:
return None
return root
if __name__ == '__main__':
s = Solution()
root1 = build_tree([1, 2, 3, 2, None, 2, 4])
assert tree_to_list(s.removeLeafNodes(root1, 2)) == [1, None, 3, None, 4]
root2 = build_tree([1, 3, 3, 3, 2])
assert tree_to_list(s.removeLeafNodes(root2, 3)) == [1, 3, None, None, 2]
root3 = build_tree([1, 2, None, 2, None, 2])
assert tree_to_list(s.removeLeafNodes(root3, 2)) == [1]
print("All tests passed.") Delete Node In Bst βΌ expand
"""
# LC 450: Delete Node in a BST
Delete the node with a given key from a BST and return the (possibly new) root while
keeping the BST property intact.
## Examples
```text
Input: root = [5, 3, 6, 2, 4, null, 7], key = 3
Output: [5, 4, 6, 2, null, null, 7]
Input: root = [5, 3, 6, 2, 4, null, 7], key = 0
Output: [5, 3, 6, 2, 4, null, 7]
Input: root = [], key = 0
Output: []
```
## Constraints
- The number of nodes in the tree is in the range [0, 10^4].
- -10^5 <= Node.val <= 10^5
- Each node has a unique value; root is a valid BST.
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
def tree_to_list(root: Optional[TreeNode]) -> List:
if not root:
return []
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
while result and result[-1] is None:
result.pop()
return result
class Solution:
"""
## 450. Delete Node in a BST
Given a root node reference of a BST and a key, delete the node with the given key in the BST.
Return the root node reference (possibly updated) of the BST.
The deletion can be divided into two stages:
1. Search for the node to remove.
2. If the node is found, delete the node.
### Examples
```text
Example 1:
Input: root = [5,3,6,2,4,null,7], key = 3
Output: [5,4,6,2,null,null,7]
Example 2:
Input: root = [5,3,6,2,4,null,7], key = 0
Output: [5,3,6,2,4,null,7]
Example 3:
Input: root = [], key = 0
Output: []
```
### Constraints
- The number of nodes in the tree is in the range `[0, 10^4]`.
- `-10^5 <= Node.val <= 10^5`
- Each node has a unique value.
- `root` is a valid binary search tree.
- `-10^5 <= key <= 10^5`
"""
def _find_min(self, node: TreeNode) -> TreeNode:
while node.left:
node = node.left
return node
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
# Find node: go left/right based on key comparison
# On deletion: no children -> None; one child -> return it
# Two children: replace with in-order successor (leftmost of right subtree)
# Time: O(h), Space: O(h) β find node then replace with in-order successor
if not root:
return None
if key < root.val:
root.left = self.deleteNode(root.left, key)
elif key > root.val:
root.right = self.deleteNode(root.right, key)
else:
if not root.left:
return root.right
if not root.right:
return root.left
successor = self._find_min(root.right)
root.val = successor.val
root.right = self.deleteNode(root.right, successor.val)
return root
if __name__ == '__main__':
sol = Solution()
root = build_tree([5, 3, 6, 2, 4, None, 7])
result = sol.deleteNode(root, 3)
vals = tree_to_list(result)
assert 3 not in vals
assert 5 in vals and 2 in vals and 4 in vals
root2 = build_tree([5, 3, 6, 2, 4, None, 7])
result2 = sol.deleteNode(root2, 0)
assert tree_to_list(result2) == tree_to_list(build_tree([5, 3, 6, 2, 4, None, 7]))
assert sol.deleteNode(None, 0) is None
print("All tests passed.") Diameter Of Binary Tree βΌ expand
"""
# LC 543: Diameter of Binary Tree
Return the length of the longest path between any two nodes in a binary tree. The path
may or may not pass through the root. Length is measured as the number of edges.
## Examples
```text
Input: root = [1, 2, 3, 4, 5]
Output: 3
Input: root = [1, 2]
Output: 1
```
## Constraints
- The number of nodes in the tree is in the range [1, 10^4].
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 543. Diameter of Binary Tree
Given the `root` of a binary tree, return the length of the diameter of the tree.
The diameter of a binary tree is the length of the longest path between any two nodes
in a tree. This path may or may not pass through the root.
The length of a path between two nodes is represented by the number of edges between them.
### Examples
```text
Input: root = [1, 2, 3, 4, 5]
Output: 3
Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].
Input: root = [1, 2]
Output: 1
```
### Constraints
- The number of nodes in the tree is in the range `[1, 10^4]`.
- `-100 <= Node.val <= 100`
"""
def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
# For each node, diameter through it = left_height + right_height
# Track global max; return height (1 + max child height) up the recursion
# Time: O(n), Space: O(n)
self.diameter = 0
def depth(node: Optional[TreeNode]) -> int:
if not node:
return 0
left = depth(node.left)
right = depth(node.right)
self.diameter = max(self.diameter, left + right)
return 1 + max(left, right)
depth(root)
return self.diameter
if __name__ == '__main__':
s = Solution()
assert s.diameterOfBinaryTree(build_tree([1, 2, 3, 4, 5])) == 3
assert s.diameterOfBinaryTree(build_tree([1, 2])) == 1
assert s.diameterOfBinaryTree(build_tree([1])) == 0
print("All tests passed.") Flatten Binary Tree To Linked List βΌ expand
"""
LC 114 - Flatten Binary Tree to Linked List
Problem: Flatten a binary tree in-place into a linked list in preorder.
Use the right pointer; set all left pointers to None.
Example:
Input: [1,2,5,3,4,null,6]
Output: [1,null,2,null,3,null,4,null,5,null,6]
Constraints:
- Number of nodes: [0, 2000]
- -100 <= Node.val <= 100
- Must be done in-place (O(1) extra space preferred)
"""
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def flatten(self, root: Optional[TreeNode]) -> None:
# Approach 1: reverse postorder (right β left β root).
# Track previously visited node; attach it as current's right child β O(n)
self.prev = None
def dfs(node):
if not node: return
dfs(node.right)
dfs(node.left)
node.right = self.prev # attach previously processed node
node.left = None
self.prev = node
dfs(root)
def flatten_iterative(self, root: Optional[TreeNode]) -> None:
# Approach 2: Morris-like iterative β O(n) time, O(1) space.
# For each node with a left child, find the rightmost node of left subtree
# and connect it to current's right; then move left subtree to right.
curr = root
while curr:
if curr.left:
# find rightmost node of left subtree
rightmost = curr.left
while rightmost.right:
rightmost = rightmost.right
rightmost.right = curr.right # connect to original right subtree
curr.right = curr.left # move left subtree to right
curr.left = None
curr = curr.right
if __name__ == "__main__":
def to_list(root):
res = []
while root:
res.append(root.val)
assert root.left is None
root = root.right
return res
sol = Solution()
root = build_tree([1, 2, 5, 3, 4, None, 6])
sol.flatten(root)
assert to_list(root) == [1, 2, 3, 4, 5, 6]
root2 = build_tree([1, 2, 5, 3, 4, None, 6])
sol.flatten_iterative(root2)
assert to_list(root2) == [1, 2, 3, 4, 5, 6]
print("All tests passed.") Floor And Ceil In Bst βΌ expand
"""
# Floor and Ceil in a BST
Given a BST and a target key, find the floor (largest value <= key) and the ceil
(smallest value >= key). Return -1 when no floor/ceil exists.
## Examples
```text
Input: BST = [8, 4, 12, 2, 6, 10, 14], key = 5
Output: floor = 4, ceil = 6
Input: BST = [8, 4, 12, 2, 6, 10, 14], key = 8
Output: floor = 8, ceil = 8
```
## Constraints
- The tree is a valid BST.
- Floor/ceil return -1 when no qualifying value exists.
"""
from __future__ import annotations
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
class Solution:
def floor(self, root: Optional[TreeNode], key: int) -> int:
"""Largest value <= key. Returns -1 if no floor exists."""
floor_val = -1
while root:
if root.val == key:
return root.val # exact match is the floor
elif root.val < key:
floor_val = root.val # current is a candidate; go right for closer value
root = root.right
else:
root = root.left # current too large; go left
return floor_val
def ceil(self, root: Optional[TreeNode], key: int) -> int:
"""Smallest value >= key. Returns -1 if no ceil exists."""
ceil_val = -1
while root:
if root.val == key:
return root.val # exact match is the ceil
elif root.val > key:
ceil_val = root.val # current is a candidate; go left for closer value
root = root.left
else:
root = root.right # current too small; go right
return ceil_val
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# BST: [8,4,12,2,6,10,14]
root = build_tree([8, 4, 12, 2, 6, 10, 14])
# Test 1: key between two nodes
assert sol.floor(root, 5) == 4, f"Expected 4, got {sol.floor(root, 5)}"
assert sol.ceil(root, 5) == 6, f"Expected 6, got {sol.ceil(root, 5)}"
# Test 2: key exactly in tree
assert sol.floor(root, 6) == 6
assert sol.ceil(root, 6) == 6
# Test 3: key smaller than all nodes
assert sol.floor(root, 1) == -1
assert sol.ceil(root, 1) == 2
# Test 4: key larger than all nodes
assert sol.floor(root, 15) == 14
assert sol.ceil(root, 15) == -1
# Test 5: empty tree
assert sol.floor(None, 5) == -1
assert sol.ceil(None, 5) == -1
print("All tests passed!") House Robber Iii βΌ expand
"""
# LC 337: House Robber III
Houses are arranged in a binary tree. Adjacent (directly linked) houses cannot both be
robbed. Return the maximum amount that can be robbed without alerting the police.
## Examples
```text
Input: root = [3, 2, 3, null, 3, null, 1]
Output: 7
Input: root = [3, 4, 5, 1, 3, null, 1]
Output: 9
```
## Constraints
- The number of nodes in the tree is in the range [1, 10^4].
- 0 <= Node.val <= 10^4
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 337. House Robber III
The thief has found himself a new place for his thievery again. There is only one entrance
to this area, called `root`.
Besides the root, each house has one and only one parent house. After a tour, the smart
thief realized that all houses in this place form a binary tree. It will automatically
contact the police if **two directly-linked houses were broken into on the same night**.
Given the `root` of the binary tree, return the maximum amount of money the thief can rob
**without alerting the police**.
---
### Examples
```text
Example 1:
Input: root = [3, 2, 3, null, 3, null, 1]
Output: 7
Explanation: Rob node 3 (root) + node 3 (left-right) + node 1 (right-right) = 3 + 3 + 1 = 7.
Example 2:
Input: root = [3, 4, 5, 1, 3, null, 1]
Output: 9
Explanation: Rob node 4 (left) + node 5 (right) = 4 + 5 = 9.
```
---
### Constraints
- The number of nodes in the tree is in the range [1, 10^4].
- 0 <= Node.val <= 10^4
"""
def rob(self, root: Optional[TreeNode]) -> int: # O(n) time, O(n) space
# For each node return (rob_this, skip_this) pair
# rob_this = node.val + skip_left + skip_right
# skip_this = max(rob_left, skip_left) + max(rob_right, skip_right)
memo = {}
def dfs(node):
if not node:
return 0
if node in memo:
return memo[node]
# rob this node: skip children, take grandchildren
rob_this = node.val
if node.left:
rob_this += dfs(node.left.left) + dfs(node.left.right)
if node.right:
rob_this += dfs(node.right.left) + dfs(node.right.right)
skip_this = dfs(node.left) + dfs(node.right)
memo[node] = max(rob_this, skip_this)
return memo[node]
return dfs(root)
def rob_pair(self, root: Optional[TreeNode]) -> int: # O(n) time, O(h) space
def dfs(node):
# returns (max if rob node, max if skip node)
if not node:
return (0, 0)
left_rob, left_skip = dfs(node.left)
right_rob, right_skip = dfs(node.right)
rob_this = node.val + left_skip + right_skip
skip_this = max(left_rob, left_skip) + max(right_rob, right_skip)
return (rob_this, skip_this)
return max(dfs(root))
if __name__ == '__main__':
s = Solution()
root1 = build_tree([3, 2, 3, None, 3, None, 1])
assert s.rob(root1) == 7
assert s.rob_pair(build_tree([3, 2, 3, None, 3, None, 1])) == 7
root2 = build_tree([3, 4, 5, 1, 3, None, 1])
assert s.rob(root2) == 9
assert s.rob_pair(build_tree([3, 4, 5, 1, 3, None, 1])) == 9
print("All tests passed.") Inorder Successor Predecessor Bst βΌ expand
"""
# LC 285 / 510: Inorder Successor and Predecessor in BST
Given a BST and a node `p`, find the inorder successor (smallest value greater than
`p.val`) and the inorder predecessor (largest value less than `p.val`). Return None
when no such node exists.
## Examples
```text
Input: BST = [5, 3, 6, 2, 4, null, null, 1], p = 3
Output: successor = 4, predecessor = 2
Input: BST = [5, 3, 6, 2, 4, null, null, 1], p = 6
Output: successor = None, predecessor = 5
```
## Constraints
- The tree is a valid BST.
- Returns None when the successor/predecessor does not exist.
"""
from __future__ import annotations
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
class Solution:
# --- Approach 1: Inorder traversal O(n) ---
def _inorder(self, root):
result = []
def dfs(node):
if node:
dfs(node.left)
result.append(node)
dfs(node.right)
dfs(root)
return result
def inorderSuccessor_naive(self, root: Optional[TreeNode], p: TreeNode) -> Optional[TreeNode]:
nodes = self._inorder(root)
for i, node in enumerate(nodes):
if node.val == p.val and i + 1 < len(nodes):
return nodes[i + 1]
return None
def inorderPredecessor_naive(self, root: Optional[TreeNode], p: TreeNode) -> Optional[TreeNode]:
nodes = self._inorder(root)
for i, node in enumerate(nodes):
if node.val == p.val and i - 1 >= 0:
return nodes[i - 1]
return None
# --- Approach 2: BST property O(h) ---
def inorderSuccessor(self, root: Optional[TreeNode], p: TreeNode) -> Optional[TreeNode]:
successor = None
while root:
if p.val < root.val:
successor = root # root could be successor; go left for closer one
root = root.left
else:
root = root.right # p is >= root; successor must be in right subtree
return successor
def inorderPredecessor(self, root: Optional[TreeNode], p: TreeNode) -> Optional[TreeNode]:
predecessor = None
while root:
if p.val > root.val:
predecessor = root # root could be predecessor; go right for closer one
root = root.right
else:
root = root.left # p is <= root; predecessor must be in left subtree
return predecessor
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# BST: [5,3,6,2,4,null,null,1]
root = build_tree([5, 3, 6, 2, 4, None, None, 1])
p3 = TreeNode(3)
succ = sol.inorderSuccessor(root, p3)
pred = sol.inorderPredecessor(root, p3)
assert succ is not None and succ.val == 4, f"Expected successor=4, got {succ}"
assert pred is not None and pred.val == 2, f"Expected predecessor=2, got {pred}"
# Test: successor of max node
p6 = TreeNode(6)
assert sol.inorderSuccessor(root, p6) is None
# Test: predecessor of min node
p1 = TreeNode(1)
assert sol.inorderPredecessor(root, p1) is None
# Test: root node
p5 = TreeNode(5)
assert sol.inorderSuccessor(root, p5).val == 6
assert sol.inorderPredecessor(root, p5).val == 4
# Verify naive approach matches
assert sol.inorderSuccessor_naive(root, p3).val == 4
assert sol.inorderPredecessor_naive(root, p3).val == 2
print("All tests passed!") Insert Into Bst βΌ expand
"""
# LC 701: Insert into a Binary Search Tree
Insert a value into a BST and return the root. The value is guaranteed not to already
exist in the tree; any valid BST resulting from the insertion is accepted.
## Examples
```text
Input: root = [4, 2, 7, 1, 3], val = 5
Output: [4, 2, 7, 1, 3, 5]
Input: root = [40, 20, 60, 10, 30, 50, 70], val = 25
Output: [40, 20, 60, 10, 30, 50, 70, null, null, 25]
```
## Constraints
- The number of nodes in the tree is in the range [0, 10^4].
- -10^8 <= Node.val <= 10^8
- val does not exist in the original BST.
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
def tree_to_list(root: Optional[TreeNode]) -> List:
if not root:
return []
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
while result and result[-1] is None:
result.pop()
return result
class Solution:
"""
## 701. Insert into a Binary Search Tree
You are given the `root` node of a BST and a `val` to insert into the tree.
Return the root node of the BST after the insertion. It is guaranteed that the new value
does not exist in the original BST.
There may exist multiple valid ways for the insertion, as long as the tree remains a BST
after insertion. You can return any of them.
### Examples
```text
Example 1:
Input: root = [4,2,7,1,3], val = 5
Output: [4,2,7,1,3,5]
Example 2:
Input: root = [40,20,60,10,30,50,70], val = 25
Output: [40,20,60,10,30,50,70,null,null,25]
Example 3:
Input: root = [], val = 5
Output: [5]
```
### Constraints
- The number of nodes in the tree is in the range `[0, 10^4]`.
- `-10^8 <= Node.val <= 10^8`
- All the values `Node.val` are unique.
- `-10^8 <= val <= 10^8`
- It's guaranteed that `val` does not exist in the original BST.
"""
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
# Navigate left if val < root.val, right if val > root.val
# Insert at the first None position found
# Time: O(h), Space: O(h) β recurse to the correct leaf position
if not root:
return TreeNode(val)
if val < root.val:
root.left = self.insertIntoBST(root.left, val)
else:
root.right = self.insertIntoBST(root.right, val)
return root
def insertIntoBST_iterative(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
# Time: O(h), Space: O(1) β walk to insertion point without recursion
new_node = TreeNode(val)
if not root:
return new_node
cur = root
while True:
if val < cur.val:
if not cur.left:
cur.left = new_node
break
cur = cur.left
else:
if not cur.right:
cur.right = new_node
break
cur = cur.right
return root
if __name__ == '__main__':
sol = Solution()
root = build_tree([4, 2, 7, 1, 3])
result = sol.insertIntoBST(root, 5)
assert 5 in tree_to_list(result)
root2 = build_tree([4, 2, 7, 1, 3])
result2 = sol.insertIntoBST_iterative(root2, 5)
assert 5 in tree_to_list(result2)
assert sol.insertIntoBST(None, 5).val == 5
print("All tests passed.") Invert Binary Tree βΌ expand
"""
# LC 226: Invert Binary Tree
Invert a binary tree (mirror it) and return its root.
## Examples
```text
Input: root = [4, 2, 7, 1, 3, 6, 9]
Output: [4, 7, 2, 9, 6, 3, 1]
Input: root = [2, 1, 3]
Output: [2, 3, 1]
Input: root = []
Output: []
```
## Constraints
- The number of nodes in the tree is in the range [0, 100].
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
def tree_to_list(root: Optional[TreeNode]) -> List:
if not root:
return []
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
while result and result[-1] is None:
result.pop()
return result
class Solution:
"""
## 226. Invert Binary Tree
Given the `root` of a binary tree, invert the tree, and return its root.
### Examples
```text
Input: root = [4, 2, 7, 1, 3, 6, 9]
Output: [4, 7, 2, 9, 6, 3, 1]
Input: root = [2, 1, 3]
Output: [2, 3, 1]
Input: root = []
Output: []
```
### Constraints
- The number of nodes in the tree is in the range `[0, 100]`.
- `-100 <= Node.val <= 100`
"""
def invertTree_recursive(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
# Time: O(n), Space: O(n)
if not root:
return None
root.left, root.right = self.invertTree_recursive(root.right), self.invertTree_recursive(root.left)
return root
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
# Swap left and right children, then recursively invert both subtrees
# Time: O(n), Space: O(n)
if not root:
return None
queue = deque([root])
while queue:
node = queue.popleft()
node.left, node.right = node.right, node.left
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return root
if __name__ == '__main__':
s = Solution()
assert tree_to_list(s.invertTree(build_tree([4, 2, 7, 1, 3, 6, 9]))) == [4, 7, 2, 9, 6, 3, 1]
assert tree_to_list(s.invertTree_recursive(build_tree([2, 1, 3]))) == [2, 3, 1]
assert s.invertTree(None) is None
print("All tests passed.") Kth Smallest Element In Bst βΌ expand
"""
# LC 230: Kth Smallest Element in a BST
Return the k-th smallest value (1-indexed) in a BST.
## Examples
```text
Input: root = [3, 1, 4, null, 2], k = 1
Output: 1
Input: root = [5, 3, 6, 2, 4, null, null, 1], k = 3
Output: 3
```
## Constraints
- The number of nodes in the tree is n, with 1 <= k <= n <= 10^4.
- 0 <= Node.val <= 10^4
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 230. Kth Smallest Element in a BST
Given the `root` of a binary search tree, and an integer `k`, return the
`k`th smallest value (1-indexed) of all the values of the nodes in the tree.
---
### Examples
```text
Example 1:
Input: root = [3, 1, 4, null, 2], k = 1
Output: 1
Example 2:
Input: root = [5, 3, 6, 2, 4, null, null, 1], k = 3
Output: 3
```
---
### Constraints
- The number of nodes in the tree is `n`.
- 1 <= k <= n <= 10^4
- 0 <= Node.val <= 10^4
"""
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: # O(n) time, O(n) space
# In-order traversal of BST yields sorted order; return the k-th element
result = []
def inorder(node):
if not node:
return
inorder(node.left)
result.append(node.val)
inorder(node.right)
inorder(root)
return result[k - 1]
def kthSmallest_iterative(self, root: Optional[TreeNode], k: int) -> int: # O(h+k) time, O(h) space
stack = []
node = root
count = 0
while stack or node:
while node:
stack.append(node)
node = node.left
node = stack.pop()
count += 1
if count == k:
return node.val
node = node.right
return -1
if __name__ == '__main__':
s = Solution()
root1 = build_tree([3, 1, 4, None, 2])
assert s.kthSmallest(root1, 1) == 1
assert s.kthSmallest_iterative(build_tree([3, 1, 4, None, 2]), 1) == 1
root2 = build_tree([5, 3, 6, 2, 4, None, None, 1])
assert s.kthSmallest(root2, 3) == 3
assert s.kthSmallest_iterative(build_tree([5, 3, 6, 2, 4, None, None, 1]), 3) == 3
print("All tests passed.") Largest Bst In Binary Tree βΌ expand
"""
# LC 333: Largest BST Subtree
Given the root of a binary tree, find the largest subtree that is also a valid BST and
return its size (number of nodes).
## Examples
```text
Input: root = [10, 5, 15, 1, 8, null, 7]
Output: 3
Input: root = [4, 2, 7, 2, 3, 5, null, 2, null, null, null, null, null, 1]
Output: 2
```
## Constraints
- The number of nodes in the tree is in the range [0, 10^4].
- -10^4 <= Node.val <= 10^4
"""
from __future__ import annotations
from typing import Optional, Tuple
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
class Solution:
# --- Approach 1: Check each subtree O(n^2) ---
def _is_bst(self, node, lo=float('-inf'), hi=float('inf')) -> int:
"""Returns size of BST rooted at node, or -1 if not a valid BST."""
if not node:
return 0
if not (lo < node.val < hi):
return -1
left = self._is_bst(node.left, lo, node.val)
if left == -1:
return -1
right = self._is_bst(node.right, node.val, hi)
if right == -1:
return -1
return left + right + 1
def largestBSTSubtree_naive(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
size = self._is_bst(root)
if size != -1:
return size # whole subtree is BST
return max(self.largestBSTSubtree_naive(root.left),
self.largestBSTSubtree_naive(root.right))
# --- Approach 2: Bottom-up post-order O(n) ---
def largestBSTSubtree(self, root: Optional[TreeNode]) -> int:
self.max_size = 0
def post_order(node) -> Tuple[bool, int, float, float]:
"""Returns (is_bst, size, min_val, max_val)."""
if not node:
return True, 0, float('inf'), float('-inf')
l_bst, l_size, l_min, l_max = post_order(node.left)
r_bst, r_size, r_min, r_max = post_order(node.right)
# Current node forms a valid BST if both children are BSTs
# and current value is greater than left max and less than right min
if l_bst and r_bst and l_max < node.val < r_min:
size = l_size + r_size + 1
self.max_size = max(self.max_size, size)
return True, size, min(l_min, node.val), max(r_max, node.val)
# Not a valid BST; propagate invalid signal upward
return False, 0, 0, 0
post_order(root)
return self.max_size
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# Test 1: [10,5,15,1,8,null,7] -> largest BST is [5,1,8] size=3
root = build_tree([10, 5, 15, 1, 8, None, 7])
assert sol.largestBSTSubtree(root) == 3, f"Got {sol.largestBSTSubtree(root)}"
assert sol.largestBSTSubtree_naive(root) == 3
# Test 2: entire tree is a BST
root = build_tree([4, 2, 6, 1, 3, 5, 7])
assert sol.largestBSTSubtree(root) == 7
# Test 3: single node
root = build_tree([1])
assert sol.largestBSTSubtree(root) == 1
# Test 4: empty tree
assert sol.largestBSTSubtree(None) == 0
# Test 5: [3,2,4,1,null,null,5] -> entire tree is BST size=5
root = build_tree([3, 2, 4, 1, None, None, 5])
assert sol.largestBSTSubtree(root) == 5
print("All tests passed!") Lowest Common Ancestor Of Binary Tree βΌ expand
"""
LC 236 - Lowest Common Ancestor of a Binary Tree
Problem: Find LCA of nodes p and q in a general binary tree.
Example:
Input: [3,5,1,6,2,0,8,null,null,7,4], p=5, q=1
Output: 3
Constraints:
- Number of nodes: [2, 10^5], all unique
- p != q, both exist in the tree
"""
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
# Approach: recursive β if current node is p or q return it;
# if both subtrees return non-null, current node is the LCA β O(n)
if not root or root == p or root == q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
# both sides found a target β current node is LCA
if left and right: return root
return left or right
if __name__ == "__main__":
sol = Solution()
root = build_tree([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4])
def find(node, val):
if not node: return None
if node.val == val: return node
return find(node.left, val) or find(node.right, val)
assert sol.lowestCommonAncestor(root, find(root, 5), find(root, 1)).val == 3
assert sol.lowestCommonAncestor(root, find(root, 5), find(root, 4)).val == 5
print("All tests passed.") Lowest Common Ancestor Of Bst βΌ expand
"""
# LC 235: Lowest Common Ancestor of a Binary Search Tree
Find the lowest common ancestor (LCA) of two nodes p and q in a BST. The LCA is the
deepest node that has both p and q as descendants (a node is a descendant of itself).
## Examples
```text
Input: root = [6, 2, 8, 0, 4, 7, 9, null, null, 3, 5], p = 2, q = 8
Output: 6
Input: root = [6, 2, 8, 0, 4, 7, 9, null, null, 3, 5], p = 2, q = 4
Output: 2
```
## Constraints
- The number of nodes in the tree is in the range [2, 10^5].
- All Node.val are unique; p and q exist in the BST.
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
def find_node(root: TreeNode, val: int) -> Optional[TreeNode]:
if not root:
return None
if root.val == val:
return root
return find_node(root.left, val) or find_node(root.right, val)
class Solution:
"""
## 235. Lowest Common Ancestor of a Binary Search Tree
Given a binary search tree (BST), find the lowest common ancestor (LCA) node of two given
nodes in the BST.
The LCA is defined as the lowest node in the tree that has both `p` and `q` as descendants
(a node can be a descendant of itself).
### Examples
```text
Example 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Example 2:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
Output: 2
Example 3:
Input: root = [2,1], p = 2, q = 1
Output: 2
```
### Constraints
- The number of nodes in the tree is in the range `[2, 10^5]`.
- `-10^9 <= Node.val <= 10^9`
- All `Node.val` are unique.
- `p != q`
- `p` and `q` will exist in the BST.
"""
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# In BST: if both p,q < root go left; if both > root go right; else root is LCA
# Time: O(h), Space: O(h) β recurse toward the split point using BST property
if p.val < root.val and q.val < root.val:
return self.lowestCommonAncestor(root.left, p, q)
if p.val > root.val and q.val > root.val:
return self.lowestCommonAncestor(root.right, p, q)
return root
def lowestCommonAncestor_iterative(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# Time: O(h), Space: O(1) β walk down without recursion stack
node = root
while node:
if p.val < node.val and q.val < node.val:
node = node.left
elif p.val > node.val and q.val > node.val:
node = node.right
else:
return node
if __name__ == '__main__':
sol = Solution()
root = build_tree([6, 2, 8, 0, 4, 7, 9, None, None, 3, 5])
p, q = find_node(root, 2), find_node(root, 8)
assert sol.lowestCommonAncestor(root, p, q).val == 6
assert sol.lowestCommonAncestor_iterative(root, p, q).val == 6
p2, q2 = find_node(root, 2), find_node(root, 4)
assert sol.lowestCommonAncestor(root, p2, q2).val == 2
assert sol.lowestCommonAncestor_iterative(root, p2, q2).val == 2
print("All tests passed.") Maximum Depth Of Binary Tree βΌ expand
"""
# LC 104: Maximum Depth of Binary Tree
Return the maximum depth (number of nodes along the longest root-to-leaf path) of a
binary tree.
## Examples
```text
Input: root = [3, 9, 20, null, null, 15, 7]
Output: 3
Input: root = [1, null, 2]
Output: 2
```
## Constraints
- The number of nodes in the tree is in the range [0, 10^4].
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 104. Maximum Depth of Binary Tree
Given the `root` of a binary tree, return its maximum depth.
A binary tree's maximum depth is the number of nodes along the longest path
from the root node down to the farthest leaf node.
### Examples
```text
Input: root = [3, 9, 20, null, null, 15, 7]
Output: 3
Input: root = [1, null, 2]
Output: 2
```
### Constraints
- The number of nodes in the tree is in the range `[0, 10^4]`.
- `-100 <= Node.val <= 100`
"""
def maxDepth_recursive(self, root: Optional[TreeNode]) -> int:
# Time: O(n), Space: O(n)
if not root:
return 0
return 1 + max(self.maxDepth_recursive(root.left), self.maxDepth_recursive(root.right))
def maxDepth(self, root: Optional[TreeNode]) -> int:
# Depth = 1 + max(depth of left, depth of right); base case is None -> 0
# Time: O(n), Space: O(n)
if not root:
return 0
depth, queue = 0, deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth
if __name__ == '__main__':
s = Solution()
assert s.maxDepth(build_tree([3, 9, 20, None, None, 15, 7])) == 3
assert s.maxDepth_recursive(build_tree([3, 9, 20, None, None, 15, 7])) == 3
assert s.maxDepth(build_tree([1, None, 2])) == 2
assert s.maxDepth(None) == 0
print("All tests passed.") Maximum Width Of Binary Tree βΌ expand
"""
LC 662 - Maximum Width of Binary Tree
Problem: Find the maximum width (leftmost to rightmost node, including nulls) at any level.
Example:
Input: [1,3,2,5,3,null,9]
Output: 4
Constraints:
- Number of nodes: [1, 3000]
- -100 <= Node.val <= 100
"""
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int:
# Approach: BFS with position index (left=2*i, right=2*i+1)
# Normalize index at each level to prevent integer overflow β O(n)
if not root: return 0
max_width = 0
queue = deque([(root, 0)])
while queue:
level_len = len(queue)
_, first_idx = queue[0]
for _ in range(level_len):
node, idx = queue.popleft()
idx -= first_idx # normalize to avoid overflow
if node.left: queue.append((node.left, 2 * idx))
if node.right: queue.append((node.right, 2 * idx + 1))
max_width = max(max_width, idx + 1)
return max_width
if __name__ == "__main__":
sol = Solution()
assert sol.widthOfBinaryTree(build_tree([1, 3, 2, 5, 3, None, 9])) == 4
assert sol.widthOfBinaryTree(build_tree([1, 3, 2, 5, None, None, 9, 6, None, 7])) == 7
assert sol.widthOfBinaryTree(build_tree([1])) == 1
print("All tests passed.") Merge Two Bsts βΌ expand
"""
# Merge Two BSTs into a Balanced BST
Given the roots of two BSTs, merge them into a single height-balanced BST containing
all elements from both trees.
## Examples
```text
Input: BST1 = [3, 1, 5], BST2 = [4, 2, 6]
Output: balanced BST with inorder [1, 2, 3, 4, 5, 6]
Input: BST1 = [], BST2 = [4, 2, 6]
Output: balanced BST with inorder [2, 4, 6]
```
## Constraints
- Both inputs are valid BSTs (either may be empty).
- Output must be a height-balanced BST containing every element of both inputs.
"""
from __future__ import annotations
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
def tree_to_inorder(root: Optional[TreeNode]) -> list:
result = []
def dfs(node):
if node:
dfs(node.left)
result.append(node.val)
dfs(node.right)
dfs(root)
return result
class Solution:
def _inorder(self, root: Optional[TreeNode]) -> list:
"""Extract sorted values from BST via inorder traversal."""
result = []
def dfs(node):
if node:
dfs(node.left)
result.append(node.val)
dfs(node.right)
dfs(root)
return result
def _merge_sorted(self, a: list, b: list) -> list:
"""Merge two sorted arrays into one sorted array."""
merged, i, j = [], 0, 0
while i < len(a) and j < len(b):
if a[i] <= b[j]:
merged.append(a[i]); i += 1
else:
merged.append(b[j]); j += 1
merged.extend(a[i:])
merged.extend(b[j:])
return merged
def _sorted_to_bst(self, nums: list) -> Optional[TreeNode]:
"""Build a height-balanced BST from a sorted array (mid as root)."""
if not nums:
return None
mid = len(nums) // 2
node = TreeNode(nums[mid])
node.left = self._sorted_to_bst(nums[:mid])
node.right = self._sorted_to_bst(nums[mid + 1:])
return node
def mergeBSTs(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
# Step 1: get sorted arrays from both BSTs
sorted1 = self._inorder(root1)
sorted2 = self._inorder(root2)
# Step 2: merge the two sorted arrays
merged = self._merge_sorted(sorted1, sorted2)
# Step 3: build balanced BST from merged sorted array
return self._sorted_to_bst(merged)
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# Test 1: standard case
root1 = build_tree([3, 1, 5])
root2 = build_tree([4, 2, 6])
merged = sol.mergeBSTs(root1, root2)
assert tree_to_inorder(merged) == [1, 2, 3, 4, 5, 6], f"Got {tree_to_inorder(merged)}"
# Test 2: one empty tree
root1 = build_tree([1, None, 2])
merged = sol.mergeBSTs(root1, None)
assert tree_to_inorder(merged) == [1, 2]
# Test 3: both empty
assert sol.mergeBSTs(None, None) is None
# Test 4: overlapping values
root1 = build_tree([2, 1, 3])
root2 = build_tree([2, 1, 3])
merged = sol.mergeBSTs(root1, root2)
assert tree_to_inorder(merged) == [1, 1, 2, 2, 3, 3]
print("All tests passed!") Morris Traversal βΌ expand
"""
Morris Traversal - Inorder and Preorder (O(1) space, no stack/recursion)
Problem: Traverse a binary tree inorder and preorder using Morris threading.
Temporarily links each node to its inorder predecessor to avoid a stack.
Example:
Input: [1,2,3,4,5]
Inorder: [4,2,5,1,3]
Preorder: [1,2,4,5,3]
"""
from typing import List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i]); q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i]); q.append(node.right)
i += 1
return root
class Solution:
def morrisInorder(self, root) -> List[int]:
# For each node with a left child, find its inorder predecessor
# (rightmost node in left subtree).
# If predecessor has no right child β create thread, go left.
# If predecessor's right child is current β remove thread, record, go right.
result, curr = [], root
while curr:
if not curr.left:
result.append(curr.val) # no left subtree, visit and go right
curr = curr.right
else:
pred = curr.left
while pred.right and pred.right is not curr:
pred = pred.right # find inorder predecessor
if not pred.right:
pred.right = curr # create thread to come back
curr = curr.left
else:
pred.right = None # remove thread
result.append(curr.val)
curr = curr.right
return result
def morrisPreorder(self, root) -> List[int]:
# Same threading logic but record node BEFORE going left (not after).
result, curr = [], root
while curr:
if not curr.left:
result.append(curr.val)
curr = curr.right
else:
pred = curr.left
while pred.right and pred.right is not curr:
pred = pred.right
if not pred.right:
result.append(curr.val) # record here (preorder = before left)
pred.right = curr
curr = curr.left
else:
pred.right = None
curr = curr.right
return result
if __name__ == "__main__":
sol = Solution()
root = build_tree([1, 2, 3, 4, 5])
assert sol.morrisInorder(root) == [4, 2, 5, 1, 3]
assert sol.morrisPreorder(root) == [1, 2, 4, 5, 3]
print("All tests passed.") Recover Bst βΌ expand
"""
# LC 99: Recover Binary Search Tree
Two nodes of a BST are swapped by mistake. Recover the tree without changing its
structure (fix the values in-place).
## Examples
```text
Input: root = [1, 3, null, null, 2]
Output: [3, 1, null, null, 2]
Input: root = [3, 1, 4, null, null, 2]
Output: [2, 1, 4, null, null, 3]
```
## Constraints
- The number of nodes in the tree is in the range [2, 1000].
- -2^31 <= Node.val <= 2^31 - 1
"""
from __future__ import annotations
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
def tree_to_inorder(root: Optional[TreeNode]) -> list:
result = []
def dfs(node):
if node:
dfs(node.left)
result.append(node.val)
dfs(node.right)
dfs(root)
return result
class Solution:
# --- Approach 1: Inorder to array, find swapped pair O(n) space ---
def recoverTree_naive(self, root: Optional[TreeNode]) -> None:
nodes = []
def inorder(node):
if node:
inorder(node.left)
nodes.append(node)
inorder(node.right)
inorder(root)
first = second = None
for i in range(len(nodes) - 1):
if nodes[i].val > nodes[i + 1].val:
second = nodes[i + 1] # always update second
if first is None:
first = nodes[i] # only set first once
if first and second:
first.val, second.val = second.val, first.val
# --- Approach 2: Morris traversal O(1) space ---
def recoverTree(self, root: Optional[TreeNode]) -> None:
first = second = prev = None
curr = root
while curr:
if not curr.left:
# visit curr
if prev and prev.val > curr.val:
second = curr
if first is None:
first = prev
prev = curr
curr = curr.right
else:
# find inorder predecessor
pred = curr.left
while pred.right and pred.right is not curr:
pred = pred.right
if not pred.right:
pred.right = curr # create threaded link
curr = curr.left
else:
pred.right = None # remove threaded link
# visit curr
if prev and prev.val > curr.val:
second = curr
if first is None:
first = prev
prev = curr
curr = curr.right
if first and second:
first.val, second.val = second.val, first.val
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# Test 1: adjacent swap [1,3,null,null,2] -> [3,1,null,null,2]
root = build_tree([1, 3, None, None, 2])
sol.recoverTree(root)
assert tree_to_inorder(root) == [1, 2, 3], f"Got {tree_to_inorder(root)}"
# Test 2: non-adjacent swap [3,1,4,null,null,2] -> [2,1,4,null,null,3]
root = build_tree([3, 1, 4, None, None, 2])
sol.recoverTree(root)
assert tree_to_inorder(root) == [1, 2, 3, 4], f"Got {tree_to_inorder(root)}"
# Test 3: naive approach
root = build_tree([1, 3, None, None, 2])
sol.recoverTree_naive(root)
assert tree_to_inorder(root) == [1, 2, 3]
print("All tests passed!") Root To Leaf Paths βΌ expand
"""
LC 257 - Binary Tree Paths
Problem: Return all root-to-leaf paths in a binary tree as strings joined by "->".
Example:
Input: [1,2,3,null,5]
Output: ["1->2->5","1->3"]
Constraints:
- Number of nodes: [1, 100]
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
q.append(node.right)
i += 1
return root
class Solution:
def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
# Approach: DFS with path accumulation; record on reaching a leaf β O(n)
result = []
def dfs(node, path):
if not node.left and not node.right: # leaf
result.append(path)
return
if node.left: dfs(node.left, path + "->" + str(node.left.val))
if node.right: dfs(node.right, path + "->" + str(node.right.val))
if root:
dfs(root, str(root.val))
return result
if __name__ == "__main__":
sol = Solution()
root = build_tree([1, 2, 3, None, 5])
assert sorted(sol.binaryTreePaths(root)) == sorted(["1->2->5", "1->3"])
root2 = build_tree([1])
assert sol.binaryTreePaths(root2) == ["1"]
print("All tests passed.") Same Tree βΌ expand
"""
# LC 100: Same Tree
Given the roots of two binary trees, return true if they are structurally identical and
the nodes have the same values.
## Examples
```text
Input: p = [1, 2, 3], q = [1, 2, 3]
Output: true
Input: p = [1, 2], q = [1, null, 2]
Output: false
Input: p = [1, 2, 1], q = [1, 1, 2]
Output: false
```
## Constraints
- The number of nodes in both trees is in the range [0, 100].
- -10^4 <= Node.val <= 10^4
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 100. Same Tree
Given the roots of two binary trees `p` and `q`, write a function to check if they
are the same or not.
Two binary trees are considered the same if they are structurally identical, and the
nodes have the same value.
### Examples
```text
Input: p = [1, 2, 3], q = [1, 2, 3]
Output: true
Input: p = [1, 2], q = [1, null, 2]
Output: false
Input: p = [1, 2, 1], q = [1, 1, 2]
Output: false
```
### Constraints
- The number of nodes in both trees is in the range `[0, 100]`.
- `-10^4 <= Node.val <= 10^4`
"""
def isSameTree_recursive(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
# Time: O(n), Space: O(n)
if not p and not q:
return True
if not p or not q or p.val != q.val:
return False
return self.isSameTree_recursive(p.left, q.left) and self.isSameTree_recursive(p.right, q.right)
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
# Both None: same; one None: different; values differ: different
# Recursively check left and right subtrees
# Time: O(n), Space: O(n)
queue = deque([(p, q)])
while queue:
n1, n2 = queue.popleft()
if not n1 and not n2:
continue
if not n1 or not n2 or n1.val != n2.val:
return False
queue.append((n1.left, n2.left))
queue.append((n1.right, n2.right))
return True
if __name__ == '__main__':
s = Solution()
assert s.isSameTree(build_tree([1, 2, 3]), build_tree([1, 2, 3])) is True
assert s.isSameTree_recursive(build_tree([1, 2, 3]), build_tree([1, 2, 3])) is True
assert s.isSameTree(build_tree([1, 2]), build_tree([1, None, 2])) is False
assert s.isSameTree(build_tree([1, 2, 1]), build_tree([1, 1, 2])) is False
assert s.isSameTree(None, None) is True
print("All tests passed.") Search In Bst βΌ expand
"""
# LC 700: Search in a Binary Search Tree
Given the root of a BST and an integer `val`, return the subtree rooted at the node
whose value equals `val`. If no such node exists, return None.
## Examples
```text
Input: root = [4, 2, 7, 1, 3], val = 2
Output: [2, 1, 3]
Input: root = [4, 2, 7, 1, 3], val = 5
Output: []
```
## Constraints
- The number of nodes in the tree is in the range [1, 5000].
- 1 <= Node.val <= 10^7
- root is a valid BST and 1 <= val <= 10^7.
"""
from __future__ import annotations
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
"""Build tree from level-order list; None represents missing nodes."""
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
def tree_to_list(root: Optional[TreeNode]) -> list:
"""Serialize tree to level-order list for easy comparison."""
if not root:
return []
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
# strip trailing Nones
while result and result[-1] is None:
result.pop()
return result
class Solution:
# --- Approach 1: Recursive O(h) ---
def searchBST_recursive(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if not root:
return None # val not found
if root.val == val:
return root # found it
if val < root.val:
return self.searchBST_recursive(root.left, val) # go left
return self.searchBST_recursive(root.right, val) # go right
# --- Approach 2: Iterative O(h) ---
def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
while root:
if root.val == val:
return root # found
elif val < root.val:
root = root.left # move left
else:
root = root.right # move right
return None # not found
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# Test 1: val exists in tree
root = build_tree([4, 2, 7, 1, 3])
result = sol.searchBST(root, 2)
assert tree_to_list(result) == [2, 1, 3], f"Expected [2,1,3], got {tree_to_list(result)}"
# Test 2: val not in tree
result = sol.searchBST(root, 5)
assert result is None
# Test 3: search root value
result = sol.searchBST(root, 4)
assert result is not None and result.val == 4
# Test 4: single node
single = build_tree([1])
assert sol.searchBST(single, 1).val == 1
assert sol.searchBST(single, 2) is None
# Test 5: empty tree
assert sol.searchBST(None, 1) is None
# Verify recursive gives same results
root = build_tree([4, 2, 7, 1, 3])
assert tree_to_list(sol.searchBST_recursive(root, 2)) == [2, 1, 3]
assert sol.searchBST_recursive(root, 5) is None
print("All tests passed!") Serialize And Deserialize Binary Tree βΌ expand
"""
# LC 297: Serialize and Deserialize Binary Tree
Design an algorithm to convert a binary tree to a string and reconstruct the original
tree from that string.
## Examples
```text
Input: root = [1, 2, 3, null, null, 4, 5]
Output: [1, 2, 3, null, null, 4, 5]
Input: root = []
Output: []
```
## Constraints
- The number of nodes in the tree is in the range [0, 10^4].
- -1000 <= Node.val <= 1000
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
def tree_to_list(root):
if not root:
return []
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
while result and result[-1] is None:
result.pop()
return result
class Codec:
"""
## 297. Serialize and Deserialize Binary Tree
Serialization is the process of converting a data structure or object into a sequence of
bits so that it can be stored in a file or memory buffer, or transmitted across a network
connection link to be reconstructed later in the same or another computer environment.
Design an algorithm to serialize and deserialize a binary tree. There is no restriction on
how your serialization/deserialization algorithm should work. You just need to ensure that
a binary tree can be serialized to a string and this string can be deserialized to the
original tree structure.
---
### Examples
```text
Example 1:
Input: root = [1, 2, 3, null, null, 4, 5]
Output: [1, 2, 3, null, null, 4, 5]
Explanation: Serialize to a string, then deserialize back to the same tree.
Example 2:
Input: root = []
Output: []
```
---
### Constraints
- The number of nodes in the tree is in the range [0, 10^4].
- -1000 <= Node.val <= 1000
"""
# Approach 1: BFS level-order β O(n) time, O(n) space
def serialize(self, root: Optional[TreeNode]) -> str:
# BFS level-order; encode None as 'N' to preserve structure
if not root:
return ''
result, queue = [], deque([root])
while queue:
node = queue.popleft()
if node:
result.append(str(node.val))
queue.append(node.left)
queue.append(node.right)
else:
result.append('null')
return ','.join(result)
def deserialize(self, data: str) -> Optional[TreeNode]:
# BFS reconstruction: split tokens, assign left/right children from queue
if not data:
return None
vals = data.split(',')
root = TreeNode(int(vals[0]))
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if vals[i] != 'null':
node.left = TreeNode(int(vals[i]))
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] != 'null':
node.right = TreeNode(int(vals[i]))
queue.append(node.right)
i += 1
return root
class CodecDFS:
# Approach 2: DFS preorder β O(n) time, O(n) space
def serialize(self, root: Optional[TreeNode]) -> str:
# BFS level-order; encode None as 'N' to preserve structure
parts = []
def dfs(node):
if not node:
parts.append('null')
return
parts.append(str(node.val))
dfs(node.left)
dfs(node.right)
dfs(root)
return ','.join(parts)
def deserialize(self, data: str) -> Optional[TreeNode]:
# BFS reconstruction: split tokens, assign left/right children from queue
vals = iter(data.split(','))
def dfs():
val = next(vals)
if val == 'null':
return None
node = TreeNode(int(val))
node.left = dfs()
node.right = dfs()
return node
return dfs()
if __name__ == '__main__':
codec = Codec()
codec_dfs = CodecDFS()
root1 = build_tree([1, 2, 3, None, None, 4, 5])
serialized = codec.serialize(root1)
assert tree_to_list(codec.deserialize(serialized)) == [1, 2, 3, None, None, 4, 5]
serialized_dfs = codec_dfs.serialize(build_tree([1, 2, 3, None, None, 4, 5]))
assert tree_to_list(codec_dfs.deserialize(serialized_dfs)) == [1, 2, 3, None, None, 4, 5]
assert codec.serialize(None) == ''
assert codec.deserialize('') is None
print("All tests passed.") Subtree Of Another Tree βΌ expand
"""
# LC 572: Subtree of Another Tree
Return true if `subRoot` is a subtree of `root` β i.e., there exists a node in `root`
whose subtree is identical (structure and values) to `subRoot`.
## Examples
```text
Input: root = [3, 4, 5, 1, 2], subRoot = [4, 1, 2]
Output: true
Input: root = [3, 4, 5, 1, 2, null, null, null, null, 0], subRoot = [4, 1, 2]
Output: false
```
## Constraints
- The number of nodes in root is in the range [1, 2000].
- The number of nodes in subRoot is in the range [1, 1000].
- -10^4 <= Node.val <= 10^4
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 572. Subtree of Another Tree
Given the roots of two binary trees `root` and `subRoot`, return `true` if there is a subtree
of `root` with the same structure and node values as `subRoot`, and `false` otherwise.
A subtree of a binary tree `tree` is a tree that consists of a node in `tree` and all of
this node's descendants. The tree `tree` could also be considered as a subtree of itself.
### Examples
```text
Example 1:
Input: root = [3,4,5,1,2], subRoot = [4,1,2]
Output: true
Example 2:
Input: root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
Output: false
```
### Constraints
- The number of nodes in the `root` tree is in the range `[1, 2000]`.
- The number of nodes in the `subRoot` tree is in the range `[1, 1000]`.
- `-10^4 <= root.val, subRoot.val <= 10^4`
"""
def _is_same(self, s: Optional[TreeNode], t: Optional[TreeNode]) -> bool:
if not s and not t:
return True
if not s or not t or s.val != t.val:
return False
return self._is_same(s.left, t.left) and self._is_same(s.right, t.right)
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
# Check if root itself matches subRoot, or if subRoot is in either subtree
# Time: O(m*n), Space: O(m) β check every node in root against subRoot
if not root:
return False
if self._is_same(root, subRoot):
return True
return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)
if __name__ == '__main__':
sol = Solution()
root1 = build_tree([3, 4, 5, 1, 2])
sub1 = build_tree([4, 1, 2])
assert sol.isSubtree(root1, sub1) == True
root2 = build_tree([3, 4, 5, 1, 2, None, None, None, None, 0])
sub2 = build_tree([4, 1, 2])
assert sol.isSubtree(root2, sub2) == False
assert sol.isSubtree(None, build_tree([1])) == False
print("All tests passed.") Symmetric Tree βΌ expand
"""
LC 101 - Symmetric Tree
Problem: Check whether a binary tree is a mirror of itself (symmetric around its center).
Example:
Input: [1,2,2,3,4,4,3]
Output: True
Input: [1,2,2,null,3,null,3]
Output: False
Constraints:
- Number of nodes: [1, 1000]
- -100 <= Node.val <= 100
"""
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
q.append(node.right)
i += 1
return root
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
# Approach 1: Recursive β compare mirror positions β O(n)
def is_mirror(left, right):
if not left and not right: return True
if not left or not right: return False
return (
left.val == right.val
and is_mirror(left.left, right.right) # outer pair
and is_mirror(left.right, right.left) # inner pair
)
return is_mirror(root.left, root.right)
def isSymmetric_iterative(self, root: Optional[TreeNode]) -> bool:
# Approach 2: Iterative BFS with queue of mirror pairs β O(n)
queue = deque([(root.left, root.right)])
while queue:
left, right = queue.popleft()
if not left and not right: continue
if not left or not right: return False
if left.val != right.val: return False
queue.append((left.left, right.right)) # outer pair
queue.append((left.right, right.left)) # inner pair
return True
if __name__ == "__main__":
sol = Solution()
root = build_tree([1, 2, 2, 3, 4, 4, 3])
assert sol.isSymmetric(root) is True
assert sol.isSymmetric_iterative(root) is True
root2 = build_tree([1, 2, 2, None, 3, None, 3])
assert sol.isSymmetric(root2) is False
assert sol.isSymmetric_iterative(root2) is False
print("All tests passed.") Top View Of Binary Tree βΌ expand
"""
GFG - Top View of Binary Tree
Problem: Return the top view of a binary tree β the first node visible at each
horizontal distance when viewed from above.
Example:
Input: [1,2,3,4,5,6,7]
Output: [4,2,1,3,7]
Constraints:
- Number of nodes: [1, 10^5]
- 1 <= Node.val <= 10^5
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
q.append(node.right)
i += 1
return root
class Solution:
def topView(self, root: Optional[TreeNode]) -> List[int]:
# Approach: BFS with column tracking; keep FIRST seen per column β O(n)
if not root:
return []
col_map = {} # col -> first node val seen (top-most)
queue = deque([(root, 0)]) # (node, col)
while queue:
node, col = queue.popleft()
if col not in col_map: # first time seeing this column = top view
col_map[col] = node.val
if node.left: queue.append((node.left, col - 1))
if node.right: queue.append((node.right, col + 1))
return [col_map[c] for c in sorted(col_map)]
if __name__ == "__main__":
sol = Solution()
root = build_tree([1, 2, 3, 4, 5, 6, 7])
assert sol.topView(root) == [4, 2, 1, 3, 7]
root2 = build_tree([1, 2, 3])
assert sol.topView(root2) == [2, 1, 3]
print("All tests passed.") Two Sum Bsts βΌ expand
"""
# LC 1214: Two Sum BSTs
Given the roots of two BSTs and an integer `target`, return true if there exists a node
in the first tree and a node in the second tree such that their values sum to `target`.
## Examples
```text
Input: root1 = [2, 1, 4], root2 = [1, 0, 3], target = 5
Output: true
Input: root1 = [0, -10, 10], root2 = [5, 1, 7, 0, 2], target = 18
Output: false
```
## Constraints
- The number of nodes in each tree is in the range [1, 5000].
- -10^9 <= Node.val, target <= 10^9
"""
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
# Approach 1: Inorder + Two Pointers β O(n+m) time, O(n+m) space
def twoSumBSTs_inorder(root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool:
def inorder(node):
if not node:
return []
return inorder(node.left) + [node.val] + inorder(node.right)
l1, l2 = inorder(root1), inorder(root2)
i, j = 0, len(l2) - 1
while i < len(l1) and j >= 0:
s = l1[i] + l2[j]
if s == target:
return True
elif s < target:
i += 1
else:
j -= 1
return False
# Approach 2: DFS + BST Search β O(nΒ·log m) time, O(h) space
def twoSumBSTs_bst(root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool:
def search(node, val):
if not node:
return False
if node.val == val:
return True
return search(node.left, val) if val < node.val else search(node.right, val)
def dfs(node):
if not node:
return False
return search(root2, target - node.val) or dfs(node.left) or dfs(node.right)
return dfs(root1)
if __name__ == "__main__":
# Example 1: root1 = [2, 1, 4], root2 = [1, 0, 3], target = 5 -> True (2 + 3)
t1 = TreeNode(2, TreeNode(1), TreeNode(4))
t2 = TreeNode(1, TreeNode(0), TreeNode(3))
assert twoSumBSTs_inorder(t1, t2, 5) is True
assert twoSumBSTs_bst(t1, t2, 5) is True
# Example 2: root1 = [0, -10, 10], root2 = [5, 1, 7, 0, 2], target = 18 -> False
t3 = TreeNode(0, TreeNode(-10), TreeNode(10))
t4 = TreeNode(5, TreeNode(1, TreeNode(0), TreeNode(2)), TreeNode(7))
assert twoSumBSTs_inorder(t3, t4, 18) is False
assert twoSumBSTs_bst(t3, t4, 18) is False
# Single-node trees: 3 + 4 == 7
assert twoSumBSTs_inorder(TreeNode(3), TreeNode(4), 7) is True
assert twoSumBSTs_bst(TreeNode(3), TreeNode(4), 7) is True
assert twoSumBSTs_inorder(TreeNode(3), TreeNode(4), 8) is False
assert twoSumBSTs_bst(TreeNode(3), TreeNode(4), 8) is False
print("All tests passed.") Two Sum In Bst βΌ expand
"""
# LC 653: Two Sum IV - Input is a BST
Given the root of a BST and an integer `k`, return true if there exist two distinct
elements in the BST whose values sum to `k`.
## Examples
```text
Input: root = [5, 3, 6, 2, 4, null, 7], k = 9
Output: true
Input: root = [5, 3, 6, 2, 4, null, 7], k = 28
Output: false
```
## Constraints
- The number of nodes in the tree is in the range [1, 10^4].
- -10^4 <= Node.val <= 10^4
- -10^5 <= k <= 10^5
"""
from __future__ import annotations
from typing import Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values: list) -> Optional[TreeNode]:
if not values or values[0] is None:
return None
root = TreeNode(values[0])
queue = deque([root])
i = 1
while queue and i < len(values):
node = queue.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
queue.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
queue.append(node.right)
i += 1
return root
class Solution:
# --- Approach 1: Inorder + two pointers O(n) time, O(n) space ---
def findTarget_two_pointer(self, root: Optional[TreeNode], k: int) -> bool:
nums = []
def inorder(node):
if node:
inorder(node.left)
nums.append(node.val)
inorder(node.right)
inorder(root) # sorted array from BST
lo, hi = 0, len(nums) - 1
while lo < hi:
s = nums[lo] + nums[hi]
if s == k:
return True
elif s < k:
lo += 1 # need larger sum
else:
hi -= 1 # need smaller sum
return False
# --- Approach 2: DFS + hash set O(n) time, O(n) space ---
def findTarget(self, root: Optional[TreeNode], k: int) -> bool:
seen = set()
def dfs(node) -> bool:
if not node:
return False
complement = k - node.val
if complement in seen:
return True # found a pair
seen.add(node.val)
return dfs(node.left) or dfs(node.right)
return dfs(root)
# ---------- Tests ----------
if __name__ == "__main__":
sol = Solution()
# Test 1: pair exists (2+7=9)
root = build_tree([5, 3, 6, 2, 4, None, 7])
assert sol.findTarget(root, 9) is True
assert sol.findTarget_two_pointer(root, 9) is True
# Test 2: no pair
assert sol.findTarget(root, 28) is False
assert sol.findTarget_two_pointer(root, 28) is False
# Test 3: k=11 (5+6)
assert sol.findTarget(root, 11) is True
# Test 4: single node
single = build_tree([1])
assert sol.findTarget(single, 2) is False
# Test 5: two nodes that sum to k
root2 = build_tree([1, None, 2])
assert sol.findTarget(root2, 3) is True
print("All tests passed!") Validate Binary Search Tree βΌ expand
"""
# LC 98: Validate Binary Search Tree
Determine if a binary tree is a valid BST: the left subtree contains only values
strictly less than the node, the right subtree only strictly greater, recursively.
## Examples
```text
Input: root = [2, 1, 3]
Output: true
Input: root = [5, 1, 4, null, null, 3, 6]
Output: false
```
## Constraints
- The number of nodes in the tree is in the range [1, 10^4].
- -2^31 <= Node.val <= 2^31 - 1
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals:
return None
root = TreeNode(vals[0])
queue = deque([root])
i = 1
while queue and i < len(vals):
node = queue.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
queue.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
queue.append(node.right)
i += 1
return root
class Solution:
"""
## 98. Validate Binary Search Tree
Given the `root` of a binary tree, determine if it is a valid binary search tree (BST).
A valid BST is defined as follows:
- The left subtree of a node contains only nodes with keys **less than** the node's key.
- The right subtree of a node contains only nodes with keys **greater than** the node's key.
- Both the left and right subtrees must also be binary search trees.
---
### Examples
```text
Example 1:
Input: root = [2, 1, 3]
Output: true
Example 2:
Input: root = [5, 1, 4, null, null, 3, 6]
Output: false
Explanation: The root node's value is 5, but its right child's value is 4.
```
---
### Constraints
- The number of nodes in the tree is in the range [1, 10^4].
- -2^31 <= Node.val <= 2^31 - 1
"""
def isValidBST(self, root: Optional[TreeNode]) -> bool: # O(n) time, O(h) space
# Pass valid range [lo, hi] down; each node must be strictly within range
# Going left tightens upper bound; going right tightens lower bound
def validate(node, lo, hi):
if not node:
return True
if not (lo < node.val < hi):
return False
return validate(node.left, lo, node.val) and validate(node.right, node.val, hi)
return validate(root, float('-inf'), float('inf'))
def isValidBST_inorder(self, root: Optional[TreeNode]) -> bool: # O(n) time, O(n) space
result = []
def inorder(node):
if not node:
return
inorder(node.left)
result.append(node.val)
inorder(node.right)
inorder(root)
return all(result[i] < result[i + 1] for i in range(len(result) - 1))
if __name__ == '__main__':
s = Solution()
root1 = build_tree([2, 1, 3])
assert s.isValidBST(root1) == True
assert s.isValidBST_inorder(build_tree([2, 1, 3])) == True
root2 = build_tree([5, 1, 4, None, None, 3, 6])
assert s.isValidBST(root2) == False
assert s.isValidBST_inorder(build_tree([5, 1, 4, None, None, 3, 6])) == False
root3 = build_tree([2, 2, 2])
assert s.isValidBST(root3) == False
print("All tests passed.") Vertical Order Traversal βΌ expand
"""
LC 987 - Vertical Order Traversal of a Binary Tree
Problem: Return vertical order traversal column by column, top to bottom.
Nodes at the same (row, col) are sorted by value.
Example:
Input: [3,9,20,null,null,15,7]
Output: [[9],[3,15],[20],[7]]
Constraints:
- Number of nodes: [1, 1000]
- 0 <= Node.val <= 1000
"""
from typing import Optional, List
from collections import deque, defaultdict
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
q.append(node.right)
i += 1
return root
class Solution:
def verticalTraversal(self, root: Optional[TreeNode]) -> List[List[int]]:
# Approach: BFS tracking (col, row); collect all, sort, group β O(n log n)
if not root:
return []
nodes = [] # list of (col, row, val)
queue = deque([(root, 0, 0)]) # (node, col, row)
while queue:
node, col, row = queue.popleft()
nodes.append((col, row, node.val))
if node.left: queue.append((node.left, col - 1, row + 1))
if node.right: queue.append((node.right, col + 1, row + 1))
# sort by col, then row, then val (handles ties at same position)
nodes.sort()
col_map = defaultdict(list)
for col, _, val in nodes:
col_map[col].append(val)
return [col_map[c] for c in sorted(col_map)]
if __name__ == "__main__":
sol = Solution()
root = build_tree([3, 9, 20, None, None, 15, 7])
assert sol.verticalTraversal(root) == [[9], [3, 15], [20], [7]]
root2 = build_tree([1, 2, 3, 4, 5, 6, 7])
assert sol.verticalTraversal(root2) == [[4], [2], [1, 5, 6], [3], [7]]
print("All tests passed.") Zigzag Level Order Traversal βΌ expand
"""
LC 103 - Binary Tree Zigzag Level Order Traversal
Problem: Return the zigzag level order traversal of a binary tree's values
(i.e., from left to right, then right to left for the next level, alternating).
Example:
Input: [3,9,20,null,null,15,7]
Output: [[3],[20,9],[15,7]]
Constraints:
- Number of nodes: [0, 2000]
- -100 <= Node.val <= 100
"""
from typing import Optional, List
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(vals):
if not vals: return None
root = TreeNode(vals[0])
q = deque([root])
i = 1
while q and i < len(vals):
node = q.popleft()
if i < len(vals) and vals[i] is not None:
node.left = TreeNode(vals[i])
q.append(node.left)
i += 1
if i < len(vals) and vals[i] is not None:
node.right = TreeNode(vals[i])
q.append(node.right)
i += 1
return root
class Solution:
def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
# Approach: BFS with direction flag β O(n) time, O(n) space
if not root:
return []
result = []
queue = deque([root])
left_to_right = True
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
# reverse odd levels (right-to-left direction)
result.append(level if left_to_right else level[::-1])
left_to_right = not left_to_right
return result
# Using deque in level to simplify and not change the BFS logic
def zigzagLevelOrderDeque(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
# Use left_to_right for clearer state tracking. Root level is left-to-right.
left_to_right = True
res, queue = [], deque([root])
while queue:
level = deque() # Use a deque for the level's values
for _ in range(len(queue)):
# ALWAYS process the main queue in standard BFS order
node = queue.popleft()
# Determine where to place the value in our level list
if left_to_right:
level.append(node.val)
else:
level.appendleft(node.val)
# ALWAYS append children in standard order (left then right)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
# Toggle the direction for the next level
left_to_right = not left_to_right
res.append(list(level))
return res
if __name__ == "__main__":
sol = Solution()
root = build_tree([3, 9, 20, None, None, 15, 7])
assert sol.zigzagLevelOrder(root) == [[3], [20, 9], [15, 7]]
assert sol.zigzagLevelOrder(None) == []
root2 = build_tree([1])
assert sol.zigzagLevelOrder(root2) == [[1]]
print("All tests passed.") Tree Traversal
Step through traversal orders on a binary tree
Press "Start" to begin Level Order (BFS).
CurrentVisitedUnvisited