diff --git a/apps/docs/content/(array)/215-kth-largest-element-in-an-array.mdx b/apps/docs/content/(array)/215-kth-largest-element-in-an-array.mdx
index 83a822f..479bb5d 100644
--- a/apps/docs/content/(array)/215-kth-largest-element-in-an-array.mdx
+++ b/apps/docs/content/(array)/215-kth-largest-element-in-an-array.mdx
@@ -21,6 +21,20 @@ sidebar:
- `1 <= k <= nums.length <= 10^5`
- `-10^4 <= nums[i] <= 10^4`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["findKthLargest(nums, k)"]) --> F{"more i in range(len(nums))?"}
+ F -- yes --> N["nums[i] = -nums[i] — negate so the min-heap acts as a max-heap"]
+ N --> F
+ F -- no --> H["heapq.heapify(nums)"]
+ H --> W{"more _ in range(k - 1)?"}
+ W -- yes --> P["heapq.heappop(nums) — discard the k-1 largest"]
+ P --> W
+ W -- no --> E(["return -heapq.heappop(nums)"])
+```
+
## Solution
```py
@@ -39,3 +53,7 @@ class Solution:
return -heapq.heappop(nums)
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(array)/271-encode-and-decode-strings.mdx b/apps/docs/content/(array)/271-encode-and-decode-strings.mdx
index eddaf65..86259c2 100644
--- a/apps/docs/content/(array)/271-encode-and-decode-strings.mdx
+++ b/apps/docs/content/(array)/271-encode-and-decode-strings.mdx
@@ -27,6 +27,27 @@ Could you write a generalized algorithm to work on any possible set of character
- `0 <= strs[i].length <= 200`
- `strs[i]` contains any possible characters out of 256 valid ASCII characters.
+## Approach
+
+::::warning
+The diagram traces the code below literally. That code does not solve the problem: both `encode` and `decode` have only a docstring for a body, so each returns `None` and `codec.decode(codec.encode(strs))` yields `None` instead of `strs`.
+::::
+
+```mermaid
+flowchart TD
+ subgraph state["state — Codec holds no fields"]
+ X["Codec() — nothing is stored between encode and decode"]
+ end
+ subgraph encode["encode(strs)"]
+ E1["docstring only — no statement runs"] --> E2(["return None"])
+ end
+ subgraph decode["decode(s)"]
+ D1["docstring only — no statement runs"] --> D2(["return None"])
+ end
+ state --> encode
+ state --> decode
+```
+
## Solution
```py
@@ -46,3 +67,7 @@ class Codec:
# codec = Codec()
# codec.decode(codec.encode(strs))
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(array)/48-rotate-image.mdx b/apps/docs/content/(array)/48-rotate-image.mdx
index 02c2657..a671b84 100644
--- a/apps/docs/content/(array)/48-rotate-image.mdx
+++ b/apps/docs/content/(array)/48-rotate-image.mdx
@@ -22,6 +22,22 @@ sidebar:
- `1 <= n <= 20`
- `-1000 <= matrix[i][j] <= 1000`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["rotate(matrix)"]) --> N["n = len(matrix)"]
+ N --> F{"more r in range(n)?"}
+ F -- yes --> G{"more c in range(r + 1, n)?"}
+ G -- yes --> T["swap matrix[r][c] and matrix[c][r] — transpose across the diagonal"]
+ T --> G
+ G -- no --> F
+ F -- no --> H{"more r in range(n)?"}
+ H -- yes --> R["matrix[r].reverse() — transpose then mirror is a clockwise turn"]
+ R --> H
+ H -- no --> E(["return None — rotated in place"])
+```
+
## Solution
```py
@@ -35,3 +51,7 @@ class Solution:
for r in range(n):
matrix[r].reverse()
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(array)/54-spiral-matrix.mdx b/apps/docs/content/(array)/54-spiral-matrix.mdx
index f9043f9..a783f61 100644
--- a/apps/docs/content/(array)/54-spiral-matrix.mdx
+++ b/apps/docs/content/(array)/54-spiral-matrix.mdx
@@ -23,6 +23,24 @@ sidebar:
- `1 <= m, n <= 10`
- `-100 <= matrix[i][j] <= 100`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["spiralOrder(matrix)"]) --> I["res = [], top = 0, bottom = len(matrix) - 1, left = 0, right = len(matrix[0]) - 1"]
+ I --> W{"top <= bottom and left <= right?"}
+ W -- no --> E(["return res"])
+ W -- yes --> T["append matrix[top][c] for c in left..right, then top += 1 — top row, shrink from above"]
+ T --> R["append matrix[r][right] for r in top..bottom, then right -= 1 — right column"]
+ R --> B{"top <= bottom?"}
+ B -- yes --> C["append matrix[bottom][c] for c in right down to left, then bottom -= 1 — bottom row"]
+ C --> L{"left <= right?"}
+ B -- no --> L
+ L -- yes --> D["append matrix[r][left] for r in bottom down to top, then left += 1 — left column"]
+ D --> W
+ L -- no --> W
+```
+
## Solution
```py
@@ -57,3 +75,7 @@ class Solution:
return res
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(array)/73-set-matrix-zeroes.mdx b/apps/docs/content/(array)/73-set-matrix-zeroes.mdx
index f2d7bec..37f9e8b 100644
--- a/apps/docs/content/(array)/73-set-matrix-zeroes.mdx
+++ b/apps/docs/content/(array)/73-set-matrix-zeroes.mdx
@@ -30,6 +30,35 @@ You must do it in place.
- A simple improvement uses O(m + n) space, but still not the best solution.
- Could you devise a constant space solution?
+## Approach
+
+```mermaid
+flowchart TD
+ S(["setZeroes(matrix)"]) --> I["m = len(matrix), n = len(matrix[0])"]
+ I --> Z["row0_zero = any(matrix[0][c] == 0), col0_zero = any(matrix[r][0] == 0) — remember before they become markers"]
+ Z --> F{"more r in range(1, m)?"}
+ F -- yes --> G{"more c in range(1, n)?"}
+ G -- yes --> Q{"matrix[r][c] == 0?"}
+ Q -- yes --> M["matrix[r][0] = 0, matrix[0][c] = 0 — row 0 and column 0 are the marker lists"]
+ M --> G
+ Q -- no --> G
+ G -- no --> F
+ F -- no --> H{"more r in range(1, m)?"}
+ H -- yes --> J{"more c in range(1, n)?"}
+ J -- yes --> K{"matrix[r][0] == 0 or matrix[0][c] == 0?"}
+ K -- yes --> W["matrix[r][c] = 0"]
+ W --> J
+ K -- no --> J
+ J -- no --> H
+ H -- no --> A{"row0_zero?"}
+ A -- yes --> B["matrix[0][c] = 0 for c in range(n)"]
+ B --> C{"col0_zero?"}
+ A -- no --> C
+ C -- yes --> D["matrix[r][0] = 0 for r in range(m)"]
+ D --> E(["return None — zeroed in place"])
+ C -- no --> E
+```
+
## Solution
```py
@@ -65,3 +94,7 @@ class Solution:
for r in range(m):
matrix[r][0] = 0
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(array)/973-k-closest-points-to-origin.mdx b/apps/docs/content/(array)/973-k-closest-points-to-origin.mdx
index 3a5d1fc..97e767d 100644
--- a/apps/docs/content/(array)/973-k-closest-points-to-origin.mdx
+++ b/apps/docs/content/(array)/973-k-closest-points-to-origin.mdx
@@ -23,6 +23,23 @@ sidebar:
- `1 <= k <= points.length <= 10^4`
- `-10^4 <= xi, yi <= 10^4`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["kClosest(points, k)"]) --> I["minHeap = []"]
+ I --> F{"more x, y in points?"}
+ F -- yes --> D["dist = x**2 + y**2 — squared distance orders the same as sqrt"]
+ D --> A["minHeap.append([dist, x, y])"]
+ A --> F
+ F -- no --> H["heapq.heapify(minHeap), res = [] — O(n) build, smallest dist on top"]
+ H --> W{"k > 0?"}
+ W -- yes --> P["dist, x, y = heapq.heappop(minHeap)"]
+ P --> R["res.append([x, y]), k -= 1"]
+ R --> W
+ W -- no --> E(["return res"])
+```
+
## Solution
```py
@@ -46,3 +63,7 @@ class Solution:
return res
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(hash-table)/208-implement-trie-prefix-tree.mdx b/apps/docs/content/(hash-table)/208-implement-trie-prefix-tree.mdx
index 5d43fbd..8914746 100644
--- a/apps/docs/content/(hash-table)/208-implement-trie-prefix-tree.mdx
+++ b/apps/docs/content/(hash-table)/208-implement-trie-prefix-tree.mdx
@@ -19,6 +19,44 @@ sidebar:
- `word` and `prefix` consist only of lowercase English letters.
- At most 3 * 10^4 calls in total will be made to insert, search, and startsWith.
+## Approach
+
+```mermaid
+flowchart TD
+ subgraph state["state — root TrieNode; every node holds a children dict and an endOfWord flag"]
+ R["root"] --- C["... children[c] ..."] --- L["endOfWord = True — a word ends here"]
+ end
+ subgraph insert["insert(word)"]
+ I1["cur = root"] --> I2{"more c in word?"}
+ I2 -- yes --> I3{"c not in cur.children?"}
+ I3 -- yes --> I4["cur.children[c] = TrieNode()"]
+ I4 --> I5["cur = cur.children[c]"]
+ I3 -- no --> I5
+ I5 --> I2
+ I2 -- no --> I6["cur.endOfWord = True"]
+ I6 --> I7(["done"])
+ end
+ subgraph search["search(word)"]
+ S1["cur = root"] --> S2{"more c in word?"}
+ S2 -- yes --> S3{"c not in cur.children?"}
+ S3 -- yes --> S4(["return False"])
+ S3 -- no --> S5["cur = cur.children[c]"]
+ S5 --> S2
+ S2 -- no --> S6(["return cur.endOfWord — the path exists, but is it a whole word?"])
+ end
+ subgraph startsWith["startsWith(prefix)"]
+ P1["cur = root"] --> P2{"more c in prefix?"}
+ P2 -- yes --> P3{"c not in cur.children?"}
+ P3 -- yes --> P4(["return False"])
+ P3 -- no --> P5["cur = cur.children[c]"]
+ P5 --> P2
+ P2 -- no --> P6(["return True — every prefix char had a path"])
+ end
+ state --> insert
+ state --> search
+ state --> startsWith
+```
+
## Solution
```py
@@ -68,3 +106,7 @@ class Trie:
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(linked-list)/2-add-two-numbers.mdx b/apps/docs/content/(linked-list)/2-add-two-numbers.mdx
index be61c28..abe7fa1 100644
--- a/apps/docs/content/(linked-list)/2-add-two-numbers.mdx
+++ b/apps/docs/content/(linked-list)/2-add-two-numbers.mdx
@@ -27,6 +27,20 @@ sidebar:
- `0 <= Node.val <= 9`
- It is guaranteed that the list represents a number that does not have leading zeros.
+## Approach
+
+```mermaid
+flowchart TD
+ S(["addTwoNumbers(l1, l2)"]) --> I["dummy = ListNode(), carry = 0, curr = dummy — dummy.next will be the head"]
+ I --> W{"l1 or l2 or carry?"}
+ W -- no --> E(["return dummy.next"])
+ W -- yes --> A["s = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry — a missing digit counts as 0"]
+ A --> D["carry, val = divmod(s, 10)"]
+ D --> N["curr.next = ListNode(val), curr = curr.next"]
+ N --> V["l1 = l1.next if l1 else None, l2 = l2.next if l2 else None"]
+ V --> W
+```
+
## Solution
```py
@@ -55,3 +69,7 @@ class Solution:
return dummy.next
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(linked-list)/23-merge-k-sorted-lists.mdx b/apps/docs/content/(linked-list)/23-merge-k-sorted-lists.mdx
index bb51333..7c78d3e 100644
--- a/apps/docs/content/(linked-list)/23-merge-k-sorted-lists.mdx
+++ b/apps/docs/content/(linked-list)/23-merge-k-sorted-lists.mdx
@@ -30,6 +30,27 @@ sidebar:
- `lists[i]` is sorted in ascending order.
- The sum of lists[i].length will not exceed 10^4.
+## Approach
+
+```mermaid
+flowchart TD
+ S(["mergeKLists(lists)"]) --> I["heap = []"]
+ I --> F{"more i, node in enumerate(lists)?"}
+ F -- yes --> C{"node?"}
+ C -- yes --> P["heapq.heappush(heap, (node.val, i, node)) — i breaks ties so nodes are never compared"]
+ P --> F
+ C -- no --> F
+ F -- no --> D["D = ListNode(), cur = D"]
+ D --> W{"heap?"}
+ W -- no --> E(["return D.next"])
+ W -- yes --> O["val, i, node = heapq.heappop(heap) — the smallest head across all lists"]
+ O --> L["cur.next = node, cur = node, node = node.next"]
+ L --> N{"node?"}
+ N -- yes --> Q["heapq.heappush(heap, (node.val, i, node))"]
+ Q --> W
+ N -- no --> W
+```
+
## Solution
```py
@@ -67,3 +88,7 @@ class Solution:
# Space: O(n)
return D.next
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(string)/211-design-add-and-search-words-data-structure.mdx b/apps/docs/content/(string)/211-design-add-and-search-words-data-structure.mdx
index 836339d..b7b7c71 100644
--- a/apps/docs/content/(string)/211-design-add-and-search-words-data-structure.mdx
+++ b/apps/docs/content/(string)/211-design-add-and-search-words-data-structure.mdx
@@ -16,6 +16,43 @@ sidebar:
- There will be at most 2 dots in `word` for search queries.
- At most 10^4 calls will be made to addWord and search.
+## Approach
+
+```mermaid
+flowchart TD
+ subgraph state["state — root TrieNode; every node holds a children dict and an endOfWord flag"]
+ R["root"] --- C["... children[c] ..."] --- L["endOfWord = True — a word ends here"]
+ end
+ subgraph addWord["addWord(word)"]
+ A1["cur = root"] --> A2{"more c in word?"}
+ A2 -- yes --> A3{"c not in cur.children?"}
+ A3 -- yes --> A4["cur.children[c] = TrieNode()"]
+ A4 --> A5["cur = cur.children[c]"]
+ A3 -- no --> A5
+ A5 --> A2
+ A2 -- no --> A6["cur.endOfWord = True"]
+ A6 --> A7(["done"])
+ end
+ subgraph search["search(word)"]
+ S1["dfs(0, root) — match from index 0 at the root"] --> S2["cur = root"]
+ S2 --> S3{"more i in range(j, len(word))?"}
+ S3 -- no --> S4(["return cur.endOfWord"])
+ S3 -- yes --> S5["c = word[i]"]
+ S5 --> S6{"c == '.'?"}
+ S6 -- yes --> S7{"more child in cur.children.values()?"}
+ S7 -- yes --> S8{"dfs(i + 1, child)? — try the rest of the word from this child"}
+ S8 -- yes --> S9(["return True"])
+ S8 -- no --> S7
+ S7 -- no --> S10(["return False — no child matched the wildcard"])
+ S6 -- no --> S11{"c not in cur.children?"}
+ S11 -- yes --> S12(["return False"])
+ S11 -- no --> S13["cur = cur.children[c]"]
+ S13 --> S3
+ end
+ state --> addWord
+ state --> search
+```
+
## Solution
```py
@@ -63,3 +100,7 @@ class WordDictionary:
# obj.addWord(word)
# param_2 = obj.search(word)
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/102-binary-tree-level-order-traversal.mdx b/apps/docs/content/(tree)/102-binary-tree-level-order-traversal.mdx
index 3f09114..590d732 100644
--- a/apps/docs/content/(tree)/102-binary-tree-level-order-traversal.mdx
+++ b/apps/docs/content/(tree)/102-binary-tree-level-order-traversal.mdx
@@ -25,6 +25,26 @@ sidebar:
- The number of nodes in the tree is in the range [0, 2000].
- `-1000 <= Node.val <= 1000`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["levelOrder(root)"]) --> I["res = [], q = deque(), q.append(root)"]
+ I --> W{"q non-empty?"}
+ W -- no --> E(["return res"])
+ W -- yes --> L["qLen = len(q), level = [] — qLen freezes the current level's size"]
+ L --> F{"more i in range(qLen)?"}
+ F -- no --> V{"level non-empty?"}
+ V -- yes --> A["res.append(level)"]
+ A --> W
+ V -- no --> W
+ F -- yes --> P["node = q.popleft()"]
+ P --> N{"node is not None?"}
+ N -- no --> F
+ N -- yes --> C["level.append(node.val), q.append(node.left), q.append(node.right) — None children are filtered on the next level"]
+ C --> F
+```
+
## Solution
```py
@@ -59,3 +79,7 @@ class Solution:
return res
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/104-maximum-depth-of-binary-tree.mdx b/apps/docs/content/(tree)/104-maximum-depth-of-binary-tree.mdx
index b9d97b6..b417349 100644
--- a/apps/docs/content/(tree)/104-maximum-depth-of-binary-tree.mdx
+++ b/apps/docs/content/(tree)/104-maximum-depth-of-binary-tree.mdx
@@ -21,6 +21,16 @@ sidebar:
- The number of nodes in the tree is in the range [0, 10^4].
- `-100 <= Node.val <= 100`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["maxDepth(root)"]) --> B{"root is None?"}
+ B -- yes --> Z(["return 0"])
+ B -- no --> L["l = maxDepth(root.left), r = maxDepth(root.right)"]
+ L --> E(["return 1 + max(l, r) — this node adds one level above its deeper subtree"])
+```
+
## Solution
```py
@@ -37,3 +47,7 @@ class Solution:
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/1448-count-good-nodes-in-binary-tree.mdx b/apps/docs/content/(tree)/1448-count-good-nodes-in-binary-tree.mdx
index c08f66d..5343e70 100644
--- a/apps/docs/content/(tree)/1448-count-good-nodes-in-binary-tree.mdx
+++ b/apps/docs/content/(tree)/1448-count-good-nodes-in-binary-tree.mdx
@@ -28,6 +28,22 @@ sidebar:
- 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].
+## Approach
+
+```mermaid
+flowchart TD
+ S(["goodNodes(root)"]) --> C["dfs(root, root.val) — the root is always good"]
+ C --> E(["return that count"])
+ subgraph dfs["dfs(node, max_val)"]
+ D1{"node is None?"} -- yes --> D2(["return 0"])
+ D1 -- no --> D3["is_good = 1 if node.val >= max_val else 0 — max_val is the largest value on the path so far"]
+ D3 --> D4["new_max = max(max_val, node.val)"]
+ D4 --> D5["l = dfs(node.left, new_max), r = dfs(node.right, new_max)"]
+ D5 --> D6(["return is_good + l + r"])
+ end
+ C --> dfs
+```
+
## Solution
```py
@@ -50,3 +66,7 @@ class Solution:
return dfs(root, root.val)
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/199-binary-tree-right-side-view.mdx b/apps/docs/content/(tree)/199-binary-tree-right-side-view.mdx
index 4c96113..bf191b2 100644
--- a/apps/docs/content/(tree)/199-binary-tree-right-side-view.mdx
+++ b/apps/docs/content/(tree)/199-binary-tree-right-side-view.mdx
@@ -29,6 +29,26 @@ sidebar:
- The number of nodes in the tree is in the range [0, 100].
- `-100 <= Node.val <= 100`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["rightSideView(root)"]) --> I["res = [], q = deque([root])"]
+ I --> W{"q non-empty?"}
+ W -- no --> E(["return res"])
+ W -- yes --> L["rightSide = None, qLen = len(q) — qLen freezes the current level's size"]
+ L --> F{"more i in range(qLen)?"}
+ F -- no --> V{"rightSide is not None?"}
+ V -- yes --> A["res.append(rightSide.val) — the last real node popped on this level is the rightmost"]
+ A --> W
+ V -- no --> W
+ F -- yes --> P["node = q.popleft()"]
+ P --> N{"node is not None?"}
+ N -- no --> F
+ N -- yes --> C["rightSide = node, q.append(node.left), q.append(node.right)"]
+ C --> F
+```
+
## Solution
```py
@@ -62,3 +82,7 @@ class Solution:
return res
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/226-invert-binary-tree.mdx b/apps/docs/content/(tree)/226-invert-binary-tree.mdx
index c2cdb4f..9f3bc89 100644
--- a/apps/docs/content/(tree)/226-invert-binary-tree.mdx
+++ b/apps/docs/content/(tree)/226-invert-binary-tree.mdx
@@ -25,6 +25,17 @@ sidebar:
- The number of nodes in the tree is in the range [0, 100].
- `-100 <= Node.val <= 100`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["invertTree(root)"]) --> B{"root is None?"}
+ B -- yes --> Z(["return None"])
+ B -- no --> L["left = invertTree(root.left), right = invertTree(root.right) — both subtrees are already inverted"]
+ L --> X["root.left = right, root.right = left"]
+ X --> E(["return root"])
+```
+
## Solution
```py
@@ -47,3 +58,7 @@ class Solution:
return root
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/230-kth-smallest-element-in-a-bst.mdx b/apps/docs/content/(tree)/230-kth-smallest-element-in-a-bst.mdx
index 5af1d7a..e71f5dc 100644
--- a/apps/docs/content/(tree)/230-kth-smallest-element-in-a-bst.mdx
+++ b/apps/docs/content/(tree)/230-kth-smallest-element-in-a-bst.mdx
@@ -26,6 +26,23 @@ If the BST is modified often (i.e., we can do insert and delete operations) and
- `1 <= k <= n <= 10^4`
- `0 <= Node.val <= 10^4`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["kthSmallest(root, k)"]) --> I["n = 0, stack = [], cur = root"]
+ I --> W{"cur or stack?"}
+ W -- no --> E(["fall off the loop — unreachable, k <= n"])
+ W -- yes --> L{"cur?"}
+ L -- yes --> P["stack.append(cur), cur = cur.left — push the whole left spine"]
+ P --> L
+ L -- no --> O["cur = stack.pop(), n += 1 — nodes pop in ascending order"]
+ O --> K{"n == k?"}
+ K -- yes --> R(["return cur.val"])
+ K -- no --> N["cur = cur.right"]
+ N --> W
+```
+
## Solution
```py
@@ -52,3 +69,7 @@ class Solution:
return cur.val
cur = cur.right
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/235-lowest-common-ancestor-of-a-binary-search-tree.mdx b/apps/docs/content/(tree)/235-lowest-common-ancestor-of-a-binary-search-tree.mdx
index 97cb5d0..16b194e 100644
--- a/apps/docs/content/(tree)/235-lowest-common-ancestor-of-a-binary-search-tree.mdx
+++ b/apps/docs/content/(tree)/235-lowest-common-ancestor-of-a-binary-search-tree.mdx
@@ -30,6 +30,22 @@ sidebar:
- `p != q`
- p and q will exist in the BST.
+## Approach
+
+```mermaid
+flowchart TD
+ S(["lowestCommonAncestor(root, p, q)"]) --> I["cur = root"]
+ I --> W{"cur?"}
+ W -- no --> E(["fall off the loop — unreachable, p and q exist in the BST"])
+ W -- yes --> A{"p.val < cur.val and q.val < cur.val?"}
+ A -- yes --> L["cur = cur.left — both sit in the left subtree"]
+ L --> W
+ A -- no --> B{"p.val > cur.val and q.val > cur.val?"}
+ B -- yes --> R["cur = cur.right — both sit in the right subtree"]
+ R --> W
+ B -- no --> C(["return cur — the paths to p and q split here"])
+```
+
## Solution
```py
@@ -54,3 +70,7 @@ class Solution:
else:
return cur
```
+
+## Explanation
+
+
diff --git a/apps/docs/content/(tree)/236-lowest-common-ancestor-of-a-binary-tree.mdx b/apps/docs/content/(tree)/236-lowest-common-ancestor-of-a-binary-tree.mdx
index 3c30213..24c7ce2 100644
--- a/apps/docs/content/(tree)/236-lowest-common-ancestor-of-a-binary-tree.mdx
+++ b/apps/docs/content/(tree)/236-lowest-common-ancestor-of-a-binary-tree.mdx
@@ -30,6 +30,21 @@ sidebar:
- `p != q`
- p and q will exist in the tree.
+## Approach
+
+```mermaid
+flowchart TD
+ S(["lowestCommonAncestor(root, p, q)"]) --> B{"not root?"}
+ B -- yes --> N(["return None"])
+ B -- no --> M{"root == p or root == q?"}
+ M -- yes --> R(["return root — a node is its own ancestor"])
+ M -- no --> L["l = self.lowestCommonAncestor(root.left, p, q)"]
+ L --> Q["r = self.lowestCommonAncestor(root.right, p, q)"]
+ Q --> D{"l and r?"}
+ D -- yes --> A(["return root — p and q were found on different sides"])
+ D -- no --> E(["return l or r — pass up whichever side found something"])
+```
+
## Solution
```py
diff --git a/apps/docs/content/(tree)/98-validate-binary-search-tree.mdx b/apps/docs/content/(tree)/98-validate-binary-search-tree.mdx
index 24fb03b..b96b0d9 100644
--- a/apps/docs/content/(tree)/98-validate-binary-search-tree.mdx
+++ b/apps/docs/content/(tree)/98-validate-binary-search-tree.mdx
@@ -22,6 +22,23 @@ sidebar:
- The number of nodes in the tree is in the range [1, 10^4].
- `-2^31 <= Node.val <= 2^31 - 1`
+## Approach
+
+```mermaid
+flowchart TD
+ S(["isValidBST(root)"]) --> C["valid(root, float('-inf'), float('inf')) — the root may hold any value"]
+ C --> E(["return that result"])
+ subgraph valid["valid(node, low, high)"]
+ V1{"not node?"}
+ V1 -- yes --> V2(["return True"])
+ V1 -- no --> V3{"not (low < node.val < high)?"}
+ V3 -- yes --> V4(["return False — node breaks a bound set by an ancestor"])
+ V3 -- no --> V5["valid(node.left, low, node.val) and valid(node.right, node.val, high) — each child tightens one bound"]
+ V5 --> V6(["return that result"])
+ end
+ C --> V1
+```
+
## Solution
```py
@@ -44,3 +61,7 @@ class Solution:
return valid(root, float("-inf"), float("inf"))
```
+
+## Explanation
+
+