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 + +