diff --git a/apps/site/src/data/index.json b/apps/site/src/data/index.json index 3a49f2d..dd2b008 100644 --- a/apps/site/src/data/index.json +++ b/apps/site/src/data/index.json @@ -73,6 +73,18 @@ ], "difficulty": "easy" }, + { + "id": 104, + "slug": "maximum-depth-of-binary-tree", + "title": "Maximum Depth of Binary Tree", + "tags": [ + "tree", + "depth-first-search", + "breadth-first-search", + "binary-tree" + ], + "difficulty": "easy" + }, { "id": 162, "slug": "find-peak-element", @@ -114,9 +126,9 @@ "difficulty": "easy" } ], - "totalProblems": 11, + "totalProblems": 12, "difficultyCounts": { - "easy": 6, + "easy": 7, "medium": 4, "hard": 1 }, @@ -131,6 +143,10 @@ "binary-search": 7, "divide-and-conquer": 1, "stack": 1, + "tree": 1, + "depth-first-search": 1, + "breadth-first-search": 1, + "binary-tree": 1, "interactive": 2 } } \ No newline at end of file diff --git a/apps/site/src/data/problems/add-two-numbers.json b/apps/site/src/data/problems/add-two-numbers.json index c053740..4759c8f 100644 --- a/apps/site/src/data/problems/add-two-numbers.json +++ b/apps/site/src/data/problems/add-two-numbers.json @@ -72,7 +72,7 @@ } }, "passed": true, - "duration": 0.29337500000008276 + "duration": 0.3307909999999765 }, { "input": [ @@ -95,7 +95,7 @@ "next": null }, "passed": true, - "duration": 0.02333299999997962 + "duration": 0.02174999999999727 }, { "input": [ @@ -187,7 +187,7 @@ } }, "passed": true, - "duration": 0.019833999999946172 + "duration": 0.02029099999998607 } ], "totalTests": 3, diff --git a/apps/site/src/data/problems/binary-search.json b/apps/site/src/data/problems/binary-search.json index 64703ee..db561a8 100644 --- a/apps/site/src/data/problems/binary-search.json +++ b/apps/site/src/data/problems/binary-search.json @@ -33,7 +33,7 @@ "name": "Example 1", "actual": 4, "passed": true, - "duration": 0.04279199999996308 + "duration": 0.0362920000000031 }, { "input": [ @@ -51,7 +51,7 @@ "name": "Example 2", "actual": -1, "passed": true, - "duration": 0.003417000000013104 + "duration": 0.0036670000000071923 }, { "input": [ @@ -69,7 +69,7 @@ "name": "Example 3", "actual": -1, "passed": true, - "duration": 0.0036659999999528736 + "duration": 0.003708000000017364 }, { "input": [ @@ -87,7 +87,7 @@ "name": "Added negative number case", "actual": 0, "passed": true, - "duration": 0.006209000000012566 + "duration": 0.0054580000000328255 }, { "input": [ @@ -105,7 +105,7 @@ "name": "Added positive number case", "actual": 5, "passed": true, - "duration": 0.002375000000029104 + "duration": 0.0024999999999977263 } ], "totalTests": 5, diff --git a/apps/site/src/data/problems/find-first-and-last-position-of-element-in-sorted-array.json b/apps/site/src/data/problems/find-first-and-last-position-of-element-in-sorted-array.json index 0940f89..2b584f7 100644 --- a/apps/site/src/data/problems/find-first-and-last-position-of-element-in-sorted-array.json +++ b/apps/site/src/data/problems/find-first-and-last-position-of-element-in-sorted-array.json @@ -39,7 +39,7 @@ 4 ], "passed": true, - "duration": 0.056500000000028194 + "duration": 0.053124999999965894 }, { "input": [ @@ -63,7 +63,7 @@ -1 ], "passed": true, - "duration": 0.0037919999999758147 + "duration": 0.003917000000001281 }, { "input": [ @@ -80,7 +80,7 @@ -1 ], "passed": true, - "duration": 0.0027499999999918145 + "duration": 0.004958999999985281 }, { "input": [ @@ -108,7 +108,7 @@ 0 ], "passed": true, - "duration": 0.007000000000061846 + "duration": 0.004041999999969903 }, { "input": [ @@ -136,7 +136,7 @@ 9 ], "passed": true, - "duration": 0.009041999999908512 + "duration": 0.008874999999989086 }, { "input": [ @@ -156,7 +156,7 @@ 1 ], "passed": true, - "duration": 0.0024160000000392756 + "duration": 0.002417000000036751 } ], "totalTests": 6, diff --git a/apps/site/src/data/problems/find-peak-element.json b/apps/site/src/data/problems/find-peak-element.json index ba23d05..4900cdb 100644 --- a/apps/site/src/data/problems/find-peak-element.json +++ b/apps/site/src/data/problems/find-peak-element.json @@ -30,7 +30,7 @@ "name": "Example 1", "actual": 2, "passed": true, - "duration": 0.053167000000030384 + "duration": 0.04250000000001819 }, { "input": [ @@ -48,7 +48,7 @@ "name": "Example 2", "actual": 5, "passed": true, - "duration": 0.003291999999987638 + "duration": 0.004624999999975898 }, { "input": [ @@ -61,7 +61,7 @@ "name": "Extended example 1", "actual": 1, "passed": true, - "duration": 0.0014999999999645297 + "duration": 0.0016249999999899956 } ], "totalTests": 3, diff --git a/apps/site/src/data/problems/first-bad-version.json b/apps/site/src/data/problems/first-bad-version.json index 6589739..a373826 100644 --- a/apps/site/src/data/problems/first-bad-version.json +++ b/apps/site/src/data/problems/first-bad-version.json @@ -26,7 +26,7 @@ "name": "Example 1", "actual": 1, "passed": true, - "duration": 0.07633299999997689 + "duration": 0.06541600000002745 }, { "input": [ @@ -37,7 +37,7 @@ "name": "Example 2", "actual": 1, "passed": true, - "duration": 0.004916999999977634 + "duration": 0.004750000000001364 }, { "input": [ @@ -48,7 +48,7 @@ "name": "Example 3", "actual": 2, "passed": true, - "duration": 0.0027090000000953296 + "duration": 0.002708000000041011 }, { "input": [ @@ -59,7 +59,7 @@ "name": "Example 4", "actual": 1, "passed": true, - "duration": 0.003125000000068212 + "duration": 0.0036670000000071923 }, { "input": [ @@ -70,7 +70,7 @@ "name": "Example 5", "actual": 4, "passed": true, - "duration": 0.00937500000009095 + "duration": 0.007000000000005002 }, { "input": [ @@ -81,7 +81,7 @@ "name": "Example 6", "actual": 4, "passed": true, - "duration": 0.003291999999987638 + "duration": 0.002999999999985903 }, { "input": [ @@ -92,7 +92,7 @@ "name": "Example 7", "actual": 4, "passed": true, - "duration": 0.0017910000000256332 + "duration": 0.0018330000000332802 }, { "input": [ @@ -103,7 +103,7 @@ "name": "Example 8", "actual": 4, "passed": true, - "duration": 0.0017500000000154614 + "duration": 0.0017080000000078144 } ], "totalTests": 8, diff --git a/apps/site/src/data/problems/guess-number-higher-or-lower.json b/apps/site/src/data/problems/guess-number-higher-or-lower.json index 6dae89f..608c0c1 100644 --- a/apps/site/src/data/problems/guess-number-higher-or-lower.json +++ b/apps/site/src/data/problems/guess-number-higher-or-lower.json @@ -26,7 +26,7 @@ "name": "Example 1", "actual": 6, "passed": true, - "duration": 0.06904099999997015 + "duration": 0.08329099999997425 }, { "input": [ @@ -37,7 +37,7 @@ "name": "Example 2", "actual": 1, "passed": true, - "duration": 0.004457999999999629 + "duration": 0.0044999999999504325 }, { "input": [ @@ -48,7 +48,7 @@ "name": "Example 3", "actual": 1, "passed": true, - "duration": 0.0025000000000545697 + "duration": 0.0024579999999900792 } ], "totalTests": 3, diff --git a/apps/site/src/data/problems/longest-substring-without-repeating-characters.json b/apps/site/src/data/problems/longest-substring-without-repeating-characters.json index b2199c4..2c60c2e 100644 --- a/apps/site/src/data/problems/longest-substring-without-repeating-characters.json +++ b/apps/site/src/data/problems/longest-substring-without-repeating-characters.json @@ -26,7 +26,7 @@ "name": "Example 1", "actual": 3, "passed": true, - "duration": 0.049250000000029104 + "duration": 0.050957999999980075 }, { "input": [ @@ -36,7 +36,7 @@ "name": "Example 2", "actual": 1, "passed": true, - "duration": 0.0030420000000503933 + "duration": 0.0030409999999960746 }, { "input": [ @@ -46,7 +46,7 @@ "name": "Example 3", "actual": 3, "passed": true, - "duration": 0.001958000000058746 + "duration": 0.0020000000000095497 }, { "input": [ @@ -56,7 +56,7 @@ "name": "Example 4", "actual": 1, "passed": true, - "duration": 0.00100000000009004 + "duration": 0.001167000000009466 }, { "input": [ @@ -66,7 +66,7 @@ "name": "Example 5", "actual": 2, "passed": true, - "duration": 0.00100000000009004 + "duration": 0.0009590000000230248 }, { "input": [ @@ -76,7 +76,7 @@ "name": "Example 6", "actual": 2, "passed": true, - "duration": 0.005667000000016742 + "duration": 0.001167000000009466 }, { "input": [ @@ -86,7 +86,7 @@ "name": "Example 7", "actual": 3, "passed": true, - "duration": 0.0012910000000374566 + "duration": 0.0012920000000349319 }, { "input": [ @@ -96,7 +96,7 @@ "name": "Example 8", "actual": 3, "passed": true, - "duration": 0.001209000000017113 + "duration": 0.0012500000000272848 }, { "input": [ @@ -116,7 +116,7 @@ "name": "Example 10", "actual": 5, "passed": true, - "duration": 0.0020419999999603533 + "duration": 0.0020420000000171967 } ], "totalTests": 10, diff --git a/apps/site/src/data/problems/maximum-depth-of-binary-tree.json b/apps/site/src/data/problems/maximum-depth-of-binary-tree.json new file mode 100644 index 0000000..ee0dfeb --- /dev/null +++ b/apps/site/src/data/problems/maximum-depth-of-binary-tree.json @@ -0,0 +1,152 @@ +{ + "id": 104, + "slug": "maximum-depth-of-binary-tree", + "title": "Maximum Depth of Binary Tree", + "tags": [ + "tree", + "depth-first-search", + "breadth-first-search", + "binary-tree" + ], + "difficulty": "easy", + "solutions": [ + { + "name": "iterativeSolution", + "description": "", + "approach": "- Use a stack to traverse the tree iteratively\n - Track the current level and the maximum level\n - Return the maximum level", + "timeComplexity": "O(n)", + "spaceComplexity": "O(h) where h is the height of the tree", + "code": "
const iterativeSolution = (root) => {\n  if (!root) {\n    return 0;\n  }\n  const stack: [TreeNode, number][] = [[root, 1]];\n  let node: TreeNode | null = root;\n  let maxLevel = 0;\n  let currentLevel = 1;\n  while (stack.length > 0 || node !== null) {\n    while (node !== null) {\n      stack.push([node, currentLevel]);\n      node = node.left;\n      currentLevel += 1;\n    }\n\n    const [currentNode, level] = stack.pop() as [TreeNode, number];\n    maxLevel = Math.max(maxLevel, level);\n\n    node = currentNode.right;\n    currentLevel = level + 1;\n  }\n\n  return maxLevel;\n};\n
", + "utilities": [ + { + "name": "TreeNode", + "code": "
class TreeNode {\n  val: number;\n  left: TreeNode | null;\n  right: TreeNode | null;\n  constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n    this.val = val ?? 0;\n    this.left = left ?? null;\n    this.right = right ?? null;\n  }\n
" + } + ], + "testResults": [ + { + "input": [ + { + "val": 3, + "left": { + "val": 9, + "left": null, + "right": null + }, + "right": { + "val": 20, + "left": { + "val": 15, + "left": null, + "right": null + }, + "right": { + "val": 7, + "left": null, + "right": null + } + } + } + ], + "expected": 3, + "name": "Example 1", + "actual": 3, + "passed": true, + "duration": 0.26770800000002737 + }, + { + "input": [ + { + "val": 1, + "left": null, + "right": { + "val": 2, + "left": null, + "right": null + } + } + ], + "expected": 2, + "name": "Example 2", + "actual": 2, + "passed": true, + "duration": 0.014667000000031294 + } + ], + "totalTests": 2, + "passedTests": 2, + "failedTests": 0 + }, + { + "name": "recursiveSolution", + "description": "", + "approach": "- Use a recursive function to traverse the tree\n - Return the maximum depth of the left and right subtrees\n - Return the maximum depth of the tree", + "timeComplexity": "O(n)", + "spaceComplexity": "O(h) where h is the height of the tree", + "code": "
const recursiveSolution = (root) => {\n  if (!root) {\n    return 0;\n  }\n  return (\n    1 + Math.max(recursiveSolution(root.left), recursiveSolution(root.right))\n  );\n};\n
", + "utilities": [ + { + "name": "TreeNode", + "code": "
class TreeNode {\n  val: number;\n  left: TreeNode | null;\n  right: TreeNode | null;\n  constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n    this.val = val ?? 0;\n    this.left = left ?? null;\n    this.right = right ?? null;\n  }\n
" + } + ], + "testResults": [ + { + "input": [ + { + "val": 3, + "left": { + "val": 9, + "left": null, + "right": null + }, + "right": { + "val": 20, + "left": { + "val": 15, + "left": null, + "right": null + }, + "right": { + "val": 7, + "left": null, + "right": null + } + } + } + ], + "expected": 3, + "name": "Example 1", + "actual": 3, + "passed": true, + "duration": 0.09166699999997263 + }, + { + "input": [ + { + "val": 1, + "left": null, + "right": { + "val": 2, + "left": null, + "right": null + } + } + ], + "expected": 2, + "name": "Example 2", + "actual": 2, + "passed": true, + "duration": 0.0072499999999990905 + } + ], + "totalTests": 2, + "passedTests": 2, + "failedTests": 0 + } + ], + "notes": "\n", + "totalTests": 4, + "passedTests": 4, + "failedTests": 0 +} \ No newline at end of file diff --git a/apps/site/src/data/problems/median-of-two-sorted-arrays.json b/apps/site/src/data/problems/median-of-two-sorted-arrays.json index 274a4fe..429eee7 100644 --- a/apps/site/src/data/problems/median-of-two-sorted-arrays.json +++ b/apps/site/src/data/problems/median-of-two-sorted-arrays.json @@ -32,7 +32,7 @@ "name": "Example 1", "actual": 2, "passed": true, - "duration": 0.06529199999999946 + "duration": 0.061790999999971064 }, { "input": [ @@ -49,7 +49,7 @@ "name": "Example 2", "actual": 2.5, "passed": true, - "duration": 0.006499999999959982 + "duration": 0.006832999999971889 }, { "input": [ @@ -66,7 +66,7 @@ "name": "Extended Example 1", "actual": 0, "passed": true, - "duration": 0.005583000000001448 + "duration": 0.0029170000000249274 }, { "input": [ @@ -79,7 +79,7 @@ "name": "Extended Example 2", "actual": 1, "passed": true, - "duration": 0.002375000000029104 + "duration": 0.002292000000011285 }, { "input": [ @@ -92,7 +92,7 @@ "name": "Extended Example 3", "actual": 2, "passed": true, - "duration": 0.002124999999978172 + "duration": 0.003459000000020751 }, { "input": [ @@ -115,7 +115,7 @@ "name": "Extended Example 4", "actual": 5.5, "passed": true, - "duration": 0.0041669999999385254 + "duration": 0.0042910000000233595 }, { "input": [ @@ -132,7 +132,7 @@ "name": "Extended Example 5", "actual": 3, "passed": true, - "duration": 0.04891699999996035 + "duration": 0.05816700000002584 } ], "totalTests": 7, @@ -162,7 +162,7 @@ "name": "Example 1", "actual": 2, "passed": true, - "duration": 0.02895899999998619 + "duration": 0.028708999999992102 }, { "input": [ @@ -179,7 +179,7 @@ "name": "Example 2", "actual": 2.5, "passed": true, - "duration": 0.003917000000001281 + "duration": 0.005291999999997188 }, { "input": [ @@ -196,7 +196,7 @@ "name": "Extended Example 1", "actual": 0, "passed": true, - "duration": 0.0038329999999859865 + "duration": 0.0038339999999834617 }, { "input": [ @@ -209,7 +209,7 @@ "name": "Extended Example 2", "actual": 1, "passed": true, - "duration": 0.002040999999962878 + "duration": 0.0020419999999603533 }, { "input": [ @@ -222,7 +222,7 @@ "name": "Extended Example 3", "actual": 2, "passed": true, - "duration": 0.0052090000000362124 + "duration": 0.002082999999970525 }, { "input": [ @@ -245,7 +245,7 @@ "name": "Extended Example 4", "actual": 5.5, "passed": true, - "duration": 0.009958999999980733 + "duration": 0.009165999999993346 }, { "input": [ @@ -262,7 +262,7 @@ "name": "Extended Example 5", "actual": 3, "passed": true, - "duration": 0.0027919999999994616 + "duration": 0.0027499999999918145 } ], "totalTests": 7, diff --git a/apps/site/src/data/problems/sqrt-x.json b/apps/site/src/data/problems/sqrt-x.json index 289afa5..0d57b28 100644 --- a/apps/site/src/data/problems/sqrt-x.json +++ b/apps/site/src/data/problems/sqrt-x.json @@ -25,7 +25,7 @@ "name": "Example 1", "actual": 2, "passed": true, - "duration": 0.04399999999998272 + "duration": 0.039166000000022905 }, { "input": [ @@ -35,7 +35,7 @@ "name": "Example 2", "actual": 2, "passed": true, - "duration": 0.003207999999972344 + "duration": 0.002957999999978256 }, { "input": [ @@ -45,7 +45,7 @@ "name": "Extended Example 1", "actual": 4, "passed": true, - "duration": 0.0014169999999467109 + "duration": 0.0014999999999645297 }, { "input": [ @@ -55,7 +55,7 @@ "name": "Extended Example 2", "actual": 5, "passed": true, - "duration": 0.001125000000001819 + "duration": 0.0010839999999916472 }, { "input": [ @@ -65,7 +65,7 @@ "name": "Extended Example 3", "actual": 0, "passed": true, - "duration": 0.07929100000001199 + "duration": 0.05720800000000281 }, { "input": [ @@ -75,7 +75,7 @@ "name": "Extended Example 4", "actual": 3, "passed": true, - "duration": 0.003249999999979991 + "duration": 0.00304199999999355 }, { "input": [ @@ -85,7 +85,7 @@ "name": "Extended Example 5", "actual": 1, "passed": true, - "duration": 0.001040999999986525 + "duration": 0.0010419999999840002 }, { "input": [ @@ -95,7 +95,7 @@ "name": "Extended Example 6", "actual": 1, "passed": true, - "duration": 0.0008749999999508873 + "duration": 0.0008330000000000837 } ], "totalTests": 8, @@ -119,7 +119,7 @@ "name": "Example 1", "actual": 2, "passed": true, - "duration": 0.017416000000025633 + "duration": 0.0362920000000031 }, { "input": [ @@ -129,7 +129,7 @@ "name": "Example 2", "actual": 2, "passed": true, - "duration": 0.0014170000000603977 + "duration": 0.0019159999999942556 }, { "input": [ @@ -139,7 +139,7 @@ "name": "Extended Example 1", "actual": 4, "passed": true, - "duration": 0.003040999999939231 + "duration": 0.0032910000000470063 }, { "input": [ @@ -149,7 +149,7 @@ "name": "Extended Example 2", "actual": 5, "passed": true, - "duration": 0.0013749999999390639 + "duration": 0.0014170000000035543 }, { "input": [ @@ -159,7 +159,7 @@ "name": "Extended Example 3", "actual": 0, "passed": true, - "duration": 0.0008750000000645741 + "duration": 0.0009999999999763531 }, { "input": [ @@ -169,7 +169,7 @@ "name": "Extended Example 4", "actual": 3, "passed": true, - "duration": 0.001125000000001819 + "duration": 0.001082999999994172 }, { "input": [ @@ -179,7 +179,7 @@ "name": "Extended Example 5", "actual": 1, "passed": true, - "duration": 0.0009579999999687061 + "duration": 0.0010000000000331966 }, { "input": [ @@ -189,7 +189,7 @@ "name": "Extended Example 6", "actual": 1, "passed": true, - "duration": 0.0008750000000645741 + "duration": 0.0007919999999899119 } ], "totalTests": 8, diff --git a/apps/site/src/data/problems/two-sum.json b/apps/site/src/data/problems/two-sum.json index 54a5322..76d246b 100644 --- a/apps/site/src/data/problems/two-sum.json +++ b/apps/site/src/data/problems/two-sum.json @@ -37,7 +37,7 @@ 1 ], "passed": true, - "duration": 0.15195799999992232 + "duration": 0.16341699999998127 }, { "input": [ @@ -58,7 +58,7 @@ 2 ], "passed": true, - "duration": 0.09183299999995143 + "duration": 0.013166999999953077 }, { "input": [ @@ -78,7 +78,7 @@ 1 ], "passed": true, - "duration": 0.011249999999904503 + "duration": 0.057249999999953616 }, { "input": [ @@ -106,7 +106,7 @@ 9 ], "passed": true, - "duration": 0.013832999999976892 + "duration": 0.016665999999986525 }, { "input": [ @@ -134,7 +134,7 @@ 9 ], "passed": true, - "duration": 0.010708000000022366 + "duration": 0.012291000000004715 }, { "input": [ @@ -162,7 +162,7 @@ 1 ], "passed": true, - "duration": 0.06795899999997346 + "duration": 0.09458300000000008 } ], "totalTests": 6, @@ -198,7 +198,7 @@ 1 ], "passed": true, - "duration": 0.05320799999992687 + "duration": 0.05099999999998772 }, { "input": [ @@ -219,7 +219,7 @@ 2 ], "passed": true, - "duration": 0.004125000000044565 + "duration": 0.004625000000032742 }, { "input": [ @@ -239,7 +239,7 @@ 1 ], "passed": true, - "duration": 0.003458000000023276 + "duration": 0.0034999999999740794 }, { "input": [ @@ -267,7 +267,7 @@ 9 ], "passed": true, - "duration": 0.008832999999981439 + "duration": 0.009542000000010376 }, { "input": [ @@ -295,7 +295,7 @@ 9 ], "passed": true, - "duration": 0.005457999999975982 + "duration": 0.007958999999971184 }, { "input": [ @@ -323,7 +323,7 @@ 1 ], "passed": true, - "duration": 0.006499999999959982 + "duration": 0.007000000000005002 } ], "totalTests": 6, diff --git a/apps/site/src/data/problems/valid-parentheses.json b/apps/site/src/data/problems/valid-parentheses.json index 4be0df2..731a5cb 100644 --- a/apps/site/src/data/problems/valid-parentheses.json +++ b/apps/site/src/data/problems/valid-parentheses.json @@ -25,7 +25,7 @@ "name": "Example 1", "actual": true, "passed": true, - "duration": 0.05670799999995779 + "duration": 0.07254100000000108 }, { "input": [ @@ -35,7 +35,7 @@ "name": "Example 2", "actual": true, "passed": true, - "duration": 0.00483299999996234 + "duration": 0.005916999999953987 }, { "input": [ @@ -45,7 +45,7 @@ "name": "Example 3", "actual": false, "passed": true, - "duration": 0.00541699999996581 + "duration": 0.006957999999997355 }, { "input": [ @@ -55,7 +55,7 @@ "name": "Example 4", "actual": true, "passed": true, - "duration": 0.0037499999999681677 + "duration": 0.004415999999991982 }, { "input": [ @@ -65,7 +65,7 @@ "name": "Example 5", "actual": false, "passed": true, - "duration": 0.0032089999999698193 + "duration": 0.0014579999999568827 } ], "totalTests": 5, diff --git a/docs/notes/2025-11-05-binary-tree.md b/docs/notes/2025-11-05-binary-tree.md new file mode 100644 index 0000000..153b825 --- /dev/null +++ b/docs/notes/2025-11-05-binary-tree.md @@ -0,0 +1,129 @@ +# Binary Tree + +## Definition + +Binary Tree is a non-linear and hierarchical data structure where each node has at most two children referred to as the left child and the right child. + +## Structure + +### Example Binary Tree + +```mermaid +graph TD + id15((15)) + id35((35)) + id40((40)) + id3((3)) + id6((6)) + id5((5)) + id7((7)) + id1((1)) + id10((10)) + id8((8)) + id41((41)) + + id15 --- id35 + id15 --- id40 + id35 --- id3 + id35 --- id6 + id40 --- id5 + id40 --- id7 + id3 --- id1 + id3 --- id10 + id5 --- id8 + id5 --- id41 + + classDef internalNode fill:#D3D3D3,stroke:#333,stroke-width:1px,color:#000 + classDef leafNode fill:#90EE90,stroke:#333,stroke-width:1px,color:#000 + + class id15 internalNode + class id35 internalNode + class id40 internalNode + class id3 internalNode + class id6 internalNode + class id5 leafNode + class id7 leafNode + class id1 leafNode + class id10 leafNode + class id8 leafNode + class id41 leafNode +``` + +**Legend:** + +- Gray nodes = Internal nodes (nodes with at least one child) +- Green nodes = Leaf nodes (nodes with no children) + +**Tree Structure:** + +- Root: `15` +- Level 1: `35` (left child), `40` (right child) +- Level 2: `3` (left of 35), `6` (right of 35), `5` (left of 40), `7` (right of 40) +- Level 3: `1` (left of 3), `10` (right of 3), `8` (left of 5), `41` (right of 5) + +### Representation of a Binary Tree Node + +- Each node has 3 parts: + - Data + - Left Child + - Right Child + +```typescript +class TreeNode { + data: number; + left: TreeNode | null; + right: TreeNode | null; +} +``` + +### Terminology + +- **Root**: The topmost node in the tree +- **Parent Node**: A node that is the direct ancestor of a node(its child node) +- **Child Node**: A node that is the direct descendant of another node (its parent) +- **Ancestors of a node**: All nodes on the path from the root to that node (including the node itself) +- **Descendants of a node**: All nodes that lie in the subtree rooted at that node (including the node itself) +- **Subtree of a node**: A tree consisting of that node as root and all its descendants +- **Edge**: The link/connection between a parent node and its child node +- **Path in a binary tree**: A sequence of nodes connected by edges from one node to another +- **Leaf Node**: A node that does not have any children or both children are null +- **Internal Node**: A node that has at least one child +- **Depth/Level of a Node**: The number of edges in the path from root to that node (the depth/level of the root node is zero) +- **Height of a Binary Tree**: The number of edges on the longest path from root to a leaf + +### Properties of Binary Tree + +- The maximum number of nodes at level $i$ of a binary tree is $2^i$. +- The maximum number of nodes in a binary tree of height $h$ is $2^{h + 1} - 1$. +- Total number of leaf nodes = total number of nodes with 2 children + 1. +- The minimum height of a binary tree with $n$ nodes is $\log_2^n$. +- A binary tree with $L$ leaves has at least $\log_2^L + 1$ levels. + +## Operations on Binary Tree + +- **Insertion**: Insert a new node into the binary tree. +- **Deletion**: Delete a node from the binary tree. +- **Search**: Search for a node in the binary tree. +- **Traversal**: Traverse the binary tree. + +### Traversal of Binary Tree + +- **Breadth-first Traversal**: Visit the nodes level by level, from left to right. +- **Depth-first Traversal**: Visit the nodes depth by depth, from left to right. +- **Pre-order Traversal**: Visit the root node first, then the left subtree, then the right subtree. +- **In-order Traversal**: Visit the left subtree first, then the root node, then the right subtree. +- **Post-order Traversal**: Visit the left subtree first, then the right subtree, then the root node. + +## Advantages and Disadvantages of Binary Tree + +### Disadvantages + +- Limited structure +- Space inefficiency + +### Advantages + +- Represent hierarchical data +- Huffman Coding trees are used to compress data +- Useful for indexing segmented data and storing cache +- Useful for implementing decision trees, a type of machine learning algorithm for classification and regression analysis diff --git a/packages/src/build-data.ts b/packages/src/build-data.ts index a105f7c..03ed007 100644 --- a/packages/src/build-data.ts +++ b/packages/src/build-data.ts @@ -265,6 +265,16 @@ function extractUtilityDefinition( result = `const ${utilityName}: z.ZodType = ${zodMatch[1].trim()};`; } + // Try to find class definitions + const classRegex = new RegExp( + `export\\s+class\\s+${utilityName}\\s*\\{([\\s\\S]*?)\\}\\s*`, + 'g' + ); + const classMatch = classRegex.exec(sourceContent); + if (classMatch) { + result = `class ${utilityName} {${classMatch[1]}}`; + } + return result; } @@ -583,11 +593,26 @@ async function extractCodeAndNotes( const rawNotes = tsdocMatch ? tsdocMatch[1].trim() : ''; // Clean up excessive newlines before processing - const cleanedNotes = rawNotes + let cleanedNotes = rawNotes .replace(/^\s*\n/g, '') // Remove leading empty lines .replace(/\n\s*$/g, '') // Remove trailing empty lines .trim(); + // Convert "Image: URL" format to markdown image syntax + // Pattern: * Image: https://example.com/image.png + // Replace with: ![Example image](https://example.com/image.png) + cleanedNotes = cleanedNotes.replace(/\*\s+Image:\s+(\S+)/g, (match, url) => { + // Extract example number if available (from previous lines) + const exampleMatch = cleanedNotes + .substring(0, cleanedNotes.indexOf(match)) + .match(/\*\s+(\d+)\.\s+Input:/); + const exampleNum = exampleMatch ? exampleMatch[1] : ''; + const altText = exampleNum + ? `Example ${exampleNum} image` + : 'Example image'; + return `*\n * ![${altText}](${url})`; + }); + // Process markdown to HTML const notes = cleanedNotes ? await marked.parse(cleanedNotes, { diff --git a/packages/src/leetcode-scraper.ts b/packages/src/leetcode-scraper.ts index 700d6db..30fd17e 100644 --- a/packages/src/leetcode-scraper.ts +++ b/packages/src/leetcode-scraper.ts @@ -163,7 +163,12 @@ export interface ProblemContent { description: string; constraints?: string[]; followUp?: string[]; - examples?: Array<{ input: string; output: string; explanation?: string }>; + examples?: Array<{ + input: string; + output: string; + explanation?: string; + imageUrl?: string; + }>; } export class LeetCodeScraper { @@ -336,23 +341,122 @@ export class LeetCodeScraper { .filter((line) => line.length > 0) // Filter out empty lines after cleaning : []; + // First, extract image URLs from HTML content before markdown conversion + // Images are typically in tags within example sections + const imageUrlRegex = /]+src=["']([^"']+)["'][^>]*>/gi; + const imageUrls: string[] = []; + let imageMatch; + while ((imageMatch = imageUrlRegex.exec(content)) !== null) { + const imageUrl = imageMatch[1]; + // Only include images that look like LeetCode example images + // Usually they're hosted on LeetCode CDN or are relative paths + if (imageUrl && !imageUrl.startsWith('data:')) { + imageUrls.push(imageUrl); + } + } + // Extract examples const examples: Array<{ input: string; output: string; explanation?: string; + imageUrl?: string; }> = []; - const exampleRegex = - /\*{0,2}Example \d+:\*{0,2}\s*\n?\*{0,2}Input:\*{0,2}\s*([^\n]+?)\s*\n?\*{0,2}Output:\*{0,2}\s*([^\n]+?)(?:\s*\n?\*{0,2}Explanation:\*{0,2}\s*([^\n]+?))?(?=\*{0,2}Example|\n\n|$)/gis; - let match; - while ((match = exampleRegex.exec(cleanContent)) !== null) { - examples.push({ - input: this.normalizeWhitespace(match[1].trim()), - output: this.normalizeWhitespace(match[2].trim()), - explanation: match[3] - ? this.normalizeWhitespace(match[3].trim()) - : undefined, - }); + + // Try multiple regex patterns to handle different markdown formats from turndown + // Pattern 1: Standard format with bold markers + // Stop at next Example, Constraints, Follow-up, or end of content + const exampleRegex1 = + /\*{0,2}Example\s+(\d+):\*{0,2}[\s\S]*?\*{0,2}Input:\*{0,2}\s*\n?\s*(.+?)\s*\n\s*\*{0,2}Output:\*{0,2}\s*\n?\s*(.+?)(?:\s*\n\s*\*{0,2}Explanation:\*{0,2}\s*\n?\s*(.+?))?(?=\s*\n\s*\*{0,2}(?:Example\s+\d+|Constraints?|Follow-up):|$)/gis; + + // Pattern 2: Without bold markers (plain text) + // Stop at next Example, Constraints, Follow-up, or end of content + const exampleRegex2 = + /Example\s+(\d+):[\s\S]*?Input:\s*\n?\s*(.+?)\s*\n\s*Output:\s*\n?\s*(.+?)(?:\s*\n\s*Explanation:\s*\n?\s*(.+?))?(?=\s*\n\s*(?:Example\s+\d+|Constraints?|Follow-up):|$)/gis; + + // Pattern 3: More flexible spacing + // Stop at next Example, Constraints, Follow-up, or end of content + const exampleRegex3 = + /\*{0,2}Example\s+(\d+)\*{0,2}[\s\S]*?\*{0,2}Input\*{0,2}[\s\S]*?([^\n]+(?:[\s\S]+?)?)\s*\n\s*\*{0,2}Output\*{0,2}[\s\S]*?([^\n]+(?:[\s\S]+?)?)(?:\s*\n\s*\*{0,2}Explanation\*{0,2}[\s\S]*?([^\n]+(?:[\s\S]+?)?))?(?=\s*\n\s*\*{0,2}(?:Example\s+\d+|Constraints?|Follow-up):|$)/gis; + + const patterns = [exampleRegex1, exampleRegex2, exampleRegex3]; + + for (const pattern of patterns) { + let match; + pattern.lastIndex = 0; // Reset regex + while ((match = pattern.exec(cleanContent)) !== null) { + let input = + match[2]?.trim().replace(/\n\s*/g, ' ').replace(/\s+/g, ' ') || ''; + let output = + match[3]?.trim().replace(/\n\s*/g, ' ').replace(/\s+/g, ' ') || ''; + let explanation = match[4] + ?.trim() + .replace(/\n\s*/g, ' ') + .replace(/\s+/g, ' '); + + // Remove any section headers that might have been captured (Constraints, Follow-up, etc.) + const sectionHeaderRegex = + /\s*\*{0,2}(?:Constraints?|Follow-up):\s*.*$/i; + input = input.replace(sectionHeaderRegex, '').trim(); + output = output.replace(sectionHeaderRegex, '').trim(); + explanation = explanation?.replace(sectionHeaderRegex, '').trim(); + + // Only add if we have valid input and output + if (input && output) { + // Try to match image URL to this example (by index) + const imageUrl = imageUrls[examples.length] || undefined; + examples.push({ + input: this.normalizeWhitespace(input), + output: this.normalizeWhitespace(output), + explanation: explanation + ? this.normalizeWhitespace(explanation) + : undefined, + imageUrl: imageUrl, + }); + } + } + + // If we found examples, break (don't try other patterns) + if (examples.length > 0) { + break; + } + } + + // Fallback: parse examples directly from HTML if markdown parsing failed + if (examples.length === 0) { + // LeetCode often uses
 tags with Input: and Output:
+      const htmlExampleRegex =
+        /]*>[\s\S]*?]*>Input:\s*<\/strong>\s*([^<]+)[\s\S]*?]*>Output:\s*<\/strong>\s*([^<]+)(?:[\s\S]*?]*>Explanation:\s*<\/strong>\s*([^<]+))?[\s\S]*?<\/pre>/gi;
+      let htmlMatch;
+      while ((htmlMatch = htmlExampleRegex.exec(content)) !== null) {
+        const input =
+          htmlMatch[1]
+            ?.trim()
+            .replace(/ /g, ' ')
+            .replace(/\s+/g, ' ') || '';
+        const output =
+          htmlMatch[2]
+            ?.trim()
+            .replace(/ /g, ' ')
+            .replace(/\s+/g, ' ') || '';
+        const explanation = htmlMatch[3]
+          ?.trim()
+          .replace(/ /g, ' ')
+          .replace(/\s+/g, ' ');
+
+        if (input && output) {
+          // Try to match image URL to this example (by index)
+          const imageUrl = imageUrls[examples.length] || undefined;
+          examples.push({
+            input: this.normalizeWhitespace(input),
+            output: this.normalizeWhitespace(output),
+            explanation: explanation
+              ? this.normalizeWhitespace(explanation)
+              : undefined,
+            imageUrl: imageUrl,
+          });
+        }
+      }
     }
 
     // Extract main description (everything before constraints, examples, or follow-up)
@@ -465,7 +569,7 @@ ${content
   .examples!.map(
     (example, index) =>
       ` * ${index + 1}. Input: ${example.input}
- *    Output: ${example.output}${example.explanation ? `\n *    Explanation: ${example.explanation}` : ''}`
+ *    Output: ${example.output}${example.explanation ? `\n *    Explanation: ${example.explanation}` : ''}${example.imageUrl ? `\n *    Image: ${example.imageUrl}` : ''}`
   )
   .join('\n')}`
         : ' *';
@@ -533,32 +637,6 @@ export const solutions = [solution];
 `;
   }
 
-  private inferOutputType(
-    examples: Array<{ output: string }> | undefined
-  ): string {
-    if (examples?.length === 0) return 'any';
-
-    try {
-      const firstOutput = JSON.parse(examples![0].output);
-
-      if (Array.isArray(firstOutput)) {
-        if (firstOutput.length === 0) return 'any[]';
-        if (typeof firstOutput[0] === 'number') return 'number[]';
-        if (typeof firstOutput[0] === 'string') return 'string[]';
-        if (typeof firstOutput[0] === 'boolean') return 'boolean[]';
-        return 'any[]';
-      }
-
-      if (typeof firstOutput === 'number') return 'number';
-      if (typeof firstOutput === 'string') return 'string';
-      if (typeof firstOutput === 'boolean') return 'boolean';
-
-      return 'any';
-    } catch {
-      return 'any';
-    }
-  }
-
   private generateZodInputTypes(metaData: string): string {
     try {
       const parsed = JSON.parse(metaData);
diff --git a/problems/0104-maximum-depth-of-binary-tree.ts b/problems/0104-maximum-depth-of-binary-tree.ts
new file mode 100644
index 0000000..9297c8e
--- /dev/null
+++ b/problems/0104-maximum-depth-of-binary-tree.ts
@@ -0,0 +1,141 @@
+/**
+ * 0104. Maximum Depth of Binary Tree
+ *
+ * Difficulty: easy
+ * Tags: tree, depth-first-search, breadth-first-search, binary-tree
+ *
+ * Description:
+ * 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:
+ * 1. Input: root = \[3,9,20,null,null,15,7\]
+ *    Output: 3
+ *    Image: https://assets.leetcode.com/uploads/2020/11/26/tmp-tree.jpg
+ * 2. 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`
+ *
+ */
+import { z } from 'zod';
+import type { TestCase } from '../packages/src/types.js';
+
+export class TreeNode {
+  val: number;
+  left: TreeNode | null;
+  right: TreeNode | null;
+  constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
+    this.val = val ?? 0;
+    this.left = left ?? null;
+    this.right = right ?? null;
+  }
+}
+
+export const TreeNodeSchema = z
+  .instanceof(TreeNode)
+  .check(
+    z.property('val', z.number()),
+    z.property('left', z.lazy(() => TreeNodeSchema).nullable()),
+    z.property('right', z.lazy(() => TreeNodeSchema).nullable())
+  );
+
+export const SolutionSchema = z.function({
+  input: [TreeNodeSchema.nullable()],
+  output: z.number(),
+});
+
+export type Solution = z.infer;
+
+const arrayToTreeNode = (array: (number | null)[]): TreeNode => {
+  if (array.length === 0) {
+    throw new Error('Array is empty');
+  }
+  const root = new TreeNode(array[0]!);
+  const queue: (TreeNode | null)[] = [root];
+
+  for (let index = 1; index < array.length + 1; index += 2) {
+    const node = queue.shift();
+    if (node) {
+      node.left = array[index] ? new TreeNode(array[index]!) : null;
+      queue.push(node.left);
+      if (index + 1 < array.length) {
+        node.right = array[index + 1] ? new TreeNode(array[index + 1]!) : null;
+        queue.push(node.right);
+      }
+    }
+  }
+  return root;
+};
+
+export const cases: TestCase[] = [
+  {
+    input: [arrayToTreeNode([3, 9, 20, null, null, 15, 7])],
+    expected: 3,
+    name: 'Example 1',
+  },
+  {
+    input: [arrayToTreeNode([1, null, 2])],
+    expected: 2,
+    name: 'Example 2',
+  },
+];
+
+/**
+ * Iterative Solution
+ * @utilities: TreeNode
+ * Approach:
+ *   - Use a stack to traverse the tree iteratively
+ *   - Track the current level and the maximum level
+ *   - Return the maximum level
+ * Time Complexity: O(n)
+ * Space Complexity: O(h) where h is the height of the tree
+ */
+export const iterativeSolution = SolutionSchema.implement((root) => {
+  if (!root) {
+    return 0;
+  }
+  const stack: [TreeNode, number][] = [[root, 1]];
+  let node: TreeNode | null = root;
+  let maxLevel = 0;
+  let currentLevel = 1;
+  while (stack.length > 0 || node !== null) {
+    while (node !== null) {
+      stack.push([node, currentLevel]);
+      node = node.left;
+      currentLevel += 1;
+    }
+
+    const [currentNode, level] = stack.pop() as [TreeNode, number];
+    maxLevel = Math.max(maxLevel, level);
+
+    node = currentNode.right;
+    currentLevel = level + 1;
+  }
+
+  return maxLevel;
+});
+
+/**
+ * Recursive Solution
+ * @utilities: TreeNode
+ * Approach:
+ *   - Use a recursive function to traverse the tree
+ *   - Return the maximum depth of the left and right subtrees
+ *   - Return the maximum depth of the tree
+ * Time Complexity: O(n)
+ * Space Complexity: O(h) where h is the height of the tree
+ */
+export const recursiveSolution = SolutionSchema.implement((root) => {
+  if (!root) {
+    return 0;
+  }
+  return (
+    1 + Math.max(recursiveSolution(root.left), recursiveSolution(root.right))
+  );
+});
+
+export const solutions = [iterativeSolution, recursiveSolution];