[ { "name": "① 简短问答", "direct": { "totalMs": 3746, "headerMs": 911, "ttfbMs": 2711, "streamMs": 1035, "textLength": 194, "chunkCount": 29, "text": "Quicksort has an average-case time complexity of O(n log n) and a worst-case time complexity of O(n²), which occurs when the pivot selection consistently results in highly unbalanced partitions." }, "proxy": { "totalMs": 2784, "headerMs": 19, "ttfbMs": 2784, "firstContentMs": 2784, "streamMs": 0, "textLength": 242, "chunkCount": 1, "text": "Quicksort has an average-case time complexity of O(n log n) and a worst-case time complexity of O(n²), which occurs when the pivot selection consistently results in unbalanced partitions (e.g., already sorted input with a naive pivot choice)." } }, { "name": "② 中等代码", "direct": { "totalMs": 18302, "headerMs": 1058, "ttfbMs": 2076, "streamMs": 16226, "textLength": 4471, "chunkCount": 515, "text": "Here is a clean, well-documented Python function to validate an IPv4 address:\n\n```python\ndef is_valid_ipv4(address: str) -> bool:\n \"\"\"\n Check if a given string is a valid IPv4 address.\n\n An IPv4 address is considered valid if it meets all of the following criteria:\n - Consists of exactly 4 octets separated by periods ('.')\n - Each octet is a decimal integer in the range 0–255 (inclusive)\n - No octet contains leading zeros (e.g., '01', '00' are invalid)\n - No extra whitespace or empty octets are present\n\n Args:\n address (str): The string to validate.\n\n Returns:\n bool: True if the string is a valid IPv4 address, False otherwise.\n\n Examples:\n >>> is_valid_ipv4(\"192.168.1.1\")\n True\n >>> is_valid_ipv4(\"255.255.255.255\")\n True\n >>> is_valid_ipv4(\"0.0.0.0\")\n True\n >>> is_valid_ipv4(\"256.100.50.25\")\n False\n >>> is_valid_ipv4(\"192.168.1\")\n False\n >>> is_valid_ipv4(\"192.168.01.1\")\n False\n >>> is_valid_ipv4(\"192.168.1.1.1\")\n False\n >>> is_valid_ipv4(\"\")\n False\n >>> is_valid_ipv4(\"abc.def.ghi.jkl\")\n False\n \"\"\"\n if not isinstance(address, str) or not address:\n return False\n\n octets = address.split(\".\")\n\n if len(octets) != 4:\n return False\n\n for octet in octets:\n # Reject empty octets or those with leading zeros (except \"0\" itself)\n if not octet or (octet.startswith(\"0\") and len(octet) > 1):\n return False\n\n # Reject non-numeric octets\n if not octet.isdigit():\n return False\n\n # Reject octets outside the valid range\n if not (0 <= int(octet) <= 255):\n return False\n\n return True\n\n\n# --- Quick self-test ---\nif __name__ == \"__main__\":\n test_cases = [\n (\"192.168.1.1\", True),\n (\"255.255.255.255\", True),\n (\"0.0.0.0\", True),\n (\"10.0.0.1\", True),\n (\"256.100.50.25\", False), # octet > 255\n (\"192.168.1\", False), # only 3 octets\n (\"192.168.01.1\", False), # leading zero\n (\"192.168.1.1.1\", False), # 5 octets\n (\"\", False), # empty string\n (\"abc.def.ghi.jkl\", False), # non-numeric\n (\"192.168.1.-1\", False), # negative number\n (\"192.168. 1.1\", False), # whitespace\n (\" 192.168.1.1\", False), # leading whitespace\n (\"192.168.1.1 \", False), # trailing whitespace\n ]\n\n all_passed = True\n for ip, expected in test_cases:\n result = is_valid_ipv4(ip)\n status = \"PASS\" if result == expected else \"FAIL\"\n if status == \"FAIL\":\n all_passed = False\n print(f\"[{status}] is_valid_ipv4({ip!r:25}) => {result} (expected {expected})\")\n\n print(\"\\nAll tests passed!\" if all_passed else \"\\nSome tests FAILED.\")\n```\n\n**Sample output:**\n\n```\n[PASS] is_valid_ipv4('192.168.1.1' ) => True (expected True)\n[PASS] is_valid_ipv4('255.255.255.255' ) => True (expected True)\n[PASS] is_valid_ipv4('0.0.0.0' ) => True (expected True)\n[PASS] is_valid_ipv4('10.0.0.1' ) => True (expected True)\n[PASS] is_valid_ipv4('256.100.50.25' ) => False (expected False)\n[PASS] is_valid_ipv4('192.168.1' ) => False (expected False)\n[PASS] is_valid_ipv4('192.168.01.1' ) => False (expected False)\n[PASS] is_valid_ipv4('192.168.1.1.1' ) => False (expected False)\n[PASS] is_valid_ipv4('' ) => False (expected False)\n[PASS] is_valid_ipv4('abc.def.ghi.jkl' ) => False (expected False)\n[PASS] is_valid_ipv4('192.168.1.-1' ) => False (expected False)\n[PASS] is_valid_ipv4('192.168. 1.1' ) => False (expected False)\n[PASS] is_valid_ipv4(' 192.168.1.1' ) => False (expected False)\n[PASS] is_valid_ipv4('192.168.1.1 ' ) => False (expected False)\n\nAll tests passed!\n```\n\n---\n\n**Key design decisions explained:**\n\n| Check | Reason |\n|---|---|\n| `isinstance(address, str)` | Guards against non-string inputs like `None` or integers |\n| `len(octets) != 4` | Ensures exactly 4 dot-separated parts |\n| Leading zero check | `\"01\"` is ambiguous (octal notation) and not valid in IPv4 |\n| `isdigit()` before `int()` | Rejects negatives (`\"-1\"`), spaces, and letters without risking a `ValueError` |\n| `0 <= int(octet) <= 255` | Enforces the valid octet range per the IPv4 spec |" }, "proxy": { "totalMs": 16276, "headerMs": 5, "ttfbMs": 2737, "firstContentMs": 2737, "streamMs": 13539, "textLength": 3777, "chunkCount": 431, "text": "Here is a clean, well-documented Python function to validate an IPv4 address:\n\n```python\ndef is_valid_ipv4(address: str) -> bool:\n \"\"\"\n Check if a given string is a valid IPv4 address.\n\n An IPv4 address is considered valid if it meets ALL of the following criteria:\n - Consists of exactly 4 parts separated by dots ('.')\n - Each part is a valid integer (no leading zeros, no letters or symbols)\n - Each integer is in the range 0 to 255 (inclusive)\n - The string is not empty and contains no extra whitespace or characters\n\n Args:\n address (str): The string to validate as an IPv4 address.\n\n Returns:\n bool: True if the string is a valid IPv4 address, False otherwise.\n\n Examples:\n >>> is_valid_ipv4(\"192.168.1.1\")\n True\n >>> is_valid_ipv4(\"255.255.255.255\")\n True\n >>> is_valid_ipv4(\"0.0.0.0\")\n True\n >>> is_valid_ipv4(\"256.100.50.25\")\n False\n >>> is_valid_ipv4(\"192.168.1\")\n False\n >>> is_valid_ipv4(\"192.168.01.1\") # Leading zero\n False\n >>> is_valid_ipv4(\"192.168.1.1.1\") # Too many octets\n False\n >>> is_valid_ipv4(\"abc.def.ghi.jkl\")\n False\n >>> is_valid_ipv4(\"\")\n False\n \"\"\"\n if not isinstance(address, str) or not address:\n return False\n\n parts = address.split(\".\")\n\n if len(parts) != 4:\n return False\n\n for part in parts:\n # Reject empty parts, parts with leading zeros, and non-numeric parts\n if not part or not part.isdigit():\n return False\n\n # Reject leading zeros (e.g. \"01\", \"001\"), but allow \"0\" itself\n if len(part) > 1 and part[0] == \"0\":\n return False\n\n # Reject values outside the valid octet range\n if not (0 <= int(part) <= 255):\n return False\n\n return True\n```\n\n---\n\n**Key design decisions explained:**\n\n| Check | Reason |\n|---|---|\n| `isinstance(address, str)` | Guards against non-string inputs like `None` or integers |\n| `split(\".\")` produces exactly 4 parts | Enforces the 4-octet structure |\n| `part.isdigit()` | Rejects letters, symbols, spaces, and negative signs (e.g. `\"-1\"`) |\n| Leading zero check | `\"01\"` or `\"001\"` are ambiguous/invalid in standard IPv4 notation |\n| `0 <= int(part) <= 255` | Each octet must be within the valid byte range |\n\n---\n\n**Quick test run:**\n\n```python\ntest_cases = [\n (\"192.168.1.1\", True),\n (\"0.0.0.0\", True),\n (\"255.255.255.255\", True),\n (\"256.0.0.1\", False), # Out of range\n (\"192.168.01.1\", False), # Leading zero\n (\"192.168.1\", False), # Too few octets\n (\"192.168.1.1.1\", False), # Too many octets\n (\"192.168.1.-1\", False), # Negative number\n (\"abc.def.ghi.jkl\", False), # Non-numeric\n (\"\", False), # Empty string\n (\"1.2.3. 4\", False), # Whitespace\n]\n\nfor ip, expected in test_cases:\n result = is_valid_ipv4(ip)\n status = \"PASS\" if result == expected else \"FAIL\"\n print(f\"[{status}] is_valid_ipv4({ip!r:20}) => {result}\")\n```\n\n**Output:**\n```\n[PASS] is_valid_ipv4('192.168.1.1' ) => True\n[PASS] is_valid_ipv4('0.0.0.0' ) => True\n[PASS] is_valid_ipv4('255.255.255.255' ) => True\n[PASS] is_valid_ipv4('256.0.0.1' ) => False\n[PASS] is_valid_ipv4('192.168.01.1' ) => False\n[PASS] is_valid_ipv4('192.168.1' ) => False\n[PASS] is_valid_ipv4('192.168.1.1.1' ) => False\n[PASS] is_valid_ipv4('192.168.1.-1' ) => False\n[PASS] is_valid_ipv4('abc.def.ghi.jkl' ) => False\n[PASS] is_valid_ipv4('' ) => False\n[PASS] is_valid_ipv4('1.2.3. 4' ) => False\n```\n\nAll 11 cases pass. The function handles all common edge cases without relying on any external libraries or regex." } }, { "name": "③ 长代码生成", "direct": { "totalMs": 38394, "headerMs": 861, "ttfbMs": 2590, "streamMs": 35804, "textLength": 11113, "chunkCount": 1371, "text": "Here is a complete, well-typed Binary Search Tree implementation in TypeScript:\n\n```typescript\n// ============================================================\n// Type Definitions\n// ============================================================\n\ninterface BSTNode {\n value: T;\n left: BSTNode | null;\n right: BSTNode | null;\n}\n\ntype Comparator = (a: T, b: T) => number;\n\n// ============================================================\n// Node Factory\n// ============================================================\n\nfunction createNode(value: T): BSTNode {\n return { value, left: null, right: null };\n}\n\n// ============================================================\n// Binary Search Tree Class\n// ============================================================\n\nclass BinarySearchTree {\n private root: BSTNode | null = null;\n private comparator: Comparator;\n\n /**\n * @param comparator - Optional custom comparator function.\n * Defaults to natural (ascending) ordering for numbers and strings.\n * Return negative if a < b, 0 if a === b, positive if a > b.\n */\n constructor(comparator?: Comparator) {\n this.comparator =\n comparator ??\n ((a, b) => {\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n });\n }\n\n // ──────────────────────────────────────────────────────────\n // INSERT\n // ──────────────────────────────────────────────────────────\n\n /**\n * Inserts a value into the BST. Duplicate values are ignored.\n * Time complexity: O(h), where h is the height of the tree.\n */\n insert(value: T): this {\n this.root = this._insertNode(this.root, value);\n return this; // enables chaining: bst.insert(1).insert(2)\n }\n\n private _insertNode(node: BSTNode | null, value: T): BSTNode {\n if (node === null) return createNode(value);\n\n const cmp = this.comparator(value, node.value);\n\n if (cmp < 0) {\n node.left = this._insertNode(node.left, value);\n } else if (cmp > 0) {\n node.right = this._insertNode(node.right, value);\n }\n // cmp === 0: duplicate — do nothing\n\n return node;\n }\n\n // ──────────────────────────────────────────────────────────\n // SEARCH\n // ──────────────────────────────────────────────────────────\n\n /**\n * Searches for a value in the BST.\n * Returns the matching node, or null if not found.\n * Time complexity: O(h)\n */\n search(value: T): BSTNode | null {\n return this._searchNode(this.root, value);\n }\n\n /** Convenience method — returns true/false instead of the node. */\n contains(value: T): boolean {\n return this.search(value) !== null;\n }\n\n private _searchNode(\n node: BSTNode | null,\n value: T\n ): BSTNode | null {\n if (node === null) return null;\n\n const cmp = this.comparator(value, node.value);\n\n if (cmp < 0) return this._searchNode(node.left, value);\n if (cmp > 0) return this._searchNode(node.right, value);\n return node; // found\n }\n\n // ──────────────────────────────────────────────────────────\n // DELETE\n // ──────────────────────────────────────────────────────────\n\n /**\n * Removes a value from the BST (if it exists).\n * Uses the in-order successor strategy for nodes with two children.\n * Time complexity: O(h)\n */\n delete(value: T): this {\n this.root = this._deleteNode(this.root, value);\n return this;\n }\n\n private _deleteNode(\n node: BSTNode | null,\n value: T\n ): BSTNode | null {\n if (node === null) return null; // value not found — no-op\n\n const cmp = this.comparator(value, node.value);\n\n if (cmp < 0) {\n node.left = this._deleteNode(node.left, value);\n } else if (cmp > 0) {\n node.right = this._deleteNode(node.right, value);\n } else {\n // Node to delete found — three cases:\n\n // Case 1: Leaf node (no children)\n if (node.left === null && node.right === null) {\n return null;\n }\n\n // Case 2: One child — replace node with its child\n if (node.left === null) return node.right;\n if (node.right === null) return node.left;\n\n // Case 3: Two children — replace value with in-order successor\n // (smallest value in the right subtree), then delete the successor.\n const successor = this._findMin(node.right);\n node.value = successor.value;\n node.right = this._deleteNode(node.right, successor.value);\n }\n\n return node;\n }\n\n // ──────────────────────────────────────────────────────────\n // TRAVERSALS\n // ──────────────────────────────────────────────────────────\n\n /**\n * In-order traversal (Left → Root → Right).\n * Visits nodes in ascending sorted order.\n * Time complexity: O(n)\n */\n inorder(): T[] {\n const result: T[] = [];\n this._inorderTraverse(this.root, result);\n return result;\n }\n\n private _inorderTraverse(node: BSTNode | null, result: T[]): void {\n if (node === null) return;\n this._inorderTraverse(node.left, result);\n result.push(node.value);\n this._inorderTraverse(node.right, result);\n }\n\n /**\n * Pre-order traversal (Root → Left → Right).\n * Useful for serializing/copying the tree structure.\n * Time complexity: O(n)\n */\n preorder(): T[] {\n const result: T[] = [];\n this._preorderTraverse(this.root, result);\n return result;\n }\n\n private _preorderTraverse(node: BSTNode | null, result: T[]): void {\n if (node === null) return;\n result.push(node.value);\n this._preorderTraverse(node.left, result);\n this._preorderTraverse(node.right, result);\n }\n\n /**\n * Post-order traversal (Left → Right → Root).\n * Useful for deleting or evaluating the entire tree.\n * Time complexity: O(n)\n */\n postorder(): T[] {\n const result: T[] = [];\n this._postorderTraverse(this.root, result);\n return result;\n }\n\n private _postorderTraverse(node: BSTNode | null, result: T[]): void {\n if (node === null) return;\n this._postorderTraverse(node.left, result);\n this._postorderTraverse(node.right, result);\n result.push(node.value);\n }\n\n // ──────────────────────────────────────────────────────────\n // UTILITY METHODS\n // ──────────────────────────────────────────────────────────\n\n /** Returns the node with the minimum value in the tree (or subtree). */\n findMin(): BSTNode | null {\n if (this.root === null) return null;\n return this._findMin(this.root);\n }\n\n private _findMin(node: BSTNode): BSTNode {\n return node.left === null ? node : this._findMin(node.left);\n }\n\n /** Returns the node with the maximum value in the tree (or subtree). */\n findMax(): BSTNode | null {\n if (this.root === null) return null;\n return this._findMax(this.root);\n }\n\n private _findMax(node: BSTNode): BSTNode {\n return node.right === null ? node : this._findMax(node.right);\n }\n\n /** Returns the height of the tree. An empty tree has height -1. */\n height(): number {\n return this._calcHeight(this.root);\n }\n\n private _calcHeight(node: BSTNode | null): number {\n if (node === null) return -1;\n return 1 + Math.max(this._calcHeight(node.left), this._calcHeight(node.right));\n }\n\n /** Returns the total number of nodes. */\n size(): number {\n return this._countNodes(this.root);\n }\n\n private _countNodes(node: BSTNode | null): number {\n if (node === null) return 0;\n return 1 + this._countNodes(node.left) + this._countNodes(node.right);\n }\n\n /** Removes all nodes from the tree. */\n clear(): void {\n this.root = null;\n }\n\n /** Returns true if the tree is empty. */\n isEmpty(): boolean {\n return this.root === null;\n }\n}\n\n// ============================================================\n// Usage Examples\n// ============================================================\n\n// --- Numeric BST ---\nconst numTree = new BinarySearchTree();\n\nnumTree.insert(10).insert(5).insert(15).insert(3).insert(7).insert(12).insert(18);\n// 10\n// / \\\n// 5 15\n// / \\ / \\\n// 3 7 12 18\n\nconsole.log(\"Inorder: \", numTree.inorder()); // [3, 5, 7, 10, 12, 15, 18]\nconsole.log(\"Preorder: \", numTree.preorder()); // [10, 5, 3, 7, 15, 12, 18]\nconsole.log(\"Postorder:\", numTree.postorder()); // [3, 7, 5, 12, 18, 15, 10]\n\nconsole.log(\"Contains 7:\", numTree.contains(7)); // true\nconsole.log(\"Contains 99:\", numTree.contains(99)); // false\nconsole.log(\"Search 15:\", numTree.search(15)); // { value: 15, left: {...}, right: {...} }\n\nconsole.log(\"Min:\", numTree.findMin()?.value); // 3\nconsole.log(\"Max:\", numTree.findMax()?.value); // 18\nconsole.log(\"Height:\", numTree.height()); // 2\nconsole.log(\"Size:\", numTree.size()); // 7\n\n// Delete a leaf\nnumTree.delete(3);\nconsole.log(\"After deleting 3:\", numTree.inorder()); // [5, 7, 10, 12, 15, 18]\n\n// Delete a node with one child\nnumTree.delete(5);\nconsole.log(\"After deleting 5:\", numTree.inorder()); // [7, 10, 12, 15, 18]\n\n// Delete a node with two children\nnumTree.delete(15);\nconsole.log(\"After deleting 15:\", numTree.inorder()); // [7, 10, 12, 18]\n\n// --- String BST ---\nconst strTree = new BinarySearchTree();\nstrTree.insert(\"banana\").insert(\"apple\").insert(\"cherry\").insert(\"date\");\nconsole.log(\"String BST inorder:\", strTree.inorder()); // [\"apple\", \"banana\", \"cherry\", \"date\"]\n\n// --- Custom Object BST (with comparator) ---\ninterface Person {\n name: string;\n age: number;\n}\n\nconst personTree = new BinarySearchTree(\n (a, b) => a.age - b.age // sort by age\n);\n\npersonTree.insert({ name: \"Alice\", age: 30 });\npersonTree.insert({ name: \"Bob\", age: 25 });\npersonTree.insert({ name: \"Carol\", age: 35 });\n\nconsole.log(\n \"People by age:\",\n personTree.inorder().map((p) => `${p.name}(${p.age})`)\n); // [\"Bob(25)\", \"Alice(30)\", \"Carol(35)\"]\n```\n\n---\n\nHere is a breakdown of the key design decisions:\n\n**Generic with a custom comparator**\nThe class is `BinarySearchTree`, so it works with numbers, strings, or any custom object. A default comparator handles primitives automatically; pass a custom one for complex types.\n\n**Delete uses the in-order successor strategy**\nWhen deleting a node with two children, it finds the smallest node in the right subtree (in-order successor), copies its value up, then deletes the successor — this preserves BST ordering with minimal restructuring.\n\n**Method chaining**\n`insert` and `delete` return `this`, so you can chain calls like `bst.insert(1).insert(2).delete(1)`.\n\n**All three traversals included**\n- `inorder` (L → Root → R): produces sorted output, the most commonly needed\n- `preorder` (Root → L → R): useful for serialization/cloning\n- `postorder` (L → R → Root): useful for safe deletion or expression evaluation\n\n**Time complexities**\n\n| Method | Average | Worst (skewed tree) |\n|---|---|---|\n| `insert` | O(log n) | O(n) |\n| `search` / `contains` | O(log n) | O(n) |\n| `delete` | O(log n) | O(n) |\n| `inorder` / `preorder` / `postorder` | O(n) | O(n) |\n| `findMin` / `findMax` | O(log n) | O(n) |\n| `height` / `size` | O(n) | O(n) |\n\nIf you need guaranteed O(log n) performance regardless of insertion order, consider extending this into a self-balancing tree like an AVL or Red-Black tree." }, "proxy": { "totalMs": 27861, "headerMs": 4, "ttfbMs": 3324, "firstContentMs": 3324, "streamMs": 24537, "textLength": 7426, "chunkCount": 940, "text": "Here is a complete, well-typed Binary Search Tree implementation in TypeScript:\n\n```typescript\n// ─── Type Definitions ────────────────────────────────────────────────────────\n\ntype Nullable = T | null;\n\ninterface BSTNode {\n value: T;\n left: Nullable>;\n right: Nullable>;\n}\n\ninterface IBinarySearchTree {\n insert(value: T): void;\n delete(value: T): void;\n search(value: T): Nullable>;\n inorder(): T[];\n isEmpty(): boolean;\n getRoot(): Nullable>;\n}\n\n// ─── Node Factory ─────────────────────────────────────────────────────────────\n\nfunction createNode(value: T): BSTNode {\n return { value, left: null, right: null };\n}\n\n// ─── Comparator Type ─────────────────────────────────────────────────────────\n\n/**\n * Returns:\n * -1 (or any negative) if a < b\n * 0 if a === b\n * 1 (or any positive) if a > b\n */\ntype Comparator = (a: T, b: T) => number;\n\nconst defaultComparator = (a: T, b: T): number => {\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n};\n\n// ─── BinarySearchTree Class ───────────────────────────────────────────────────\n\nclass BinarySearchTree implements IBinarySearchTree {\n private root: Nullable> = null;\n private readonly compare: Comparator;\n\n constructor(comparator: Comparator = defaultComparator) {\n this.compare = comparator;\n }\n\n // ── Public API ─────────────────────────────────────────────────────────────\n\n isEmpty(): boolean {\n return this.root === null;\n }\n\n getRoot(): Nullable> {\n return this.root;\n }\n\n /**\n * Inserts a value into the BST.\n * Duplicate values are ignored.\n * Time complexity: O(h) where h is the height of the tree.\n */\n insert(value: T): void {\n this.root = this.insertNode(this.root, value);\n }\n\n /**\n * Removes a value from the BST if it exists.\n * Time complexity: O(h) where h is the height of the tree.\n */\n delete(value: T): void {\n this.root = this.deleteNode(this.root, value);\n }\n\n /**\n * Searches for a value and returns its node, or null if not found.\n * Time complexity: O(h) where h is the height of the tree.\n */\n search(value: T): Nullable> {\n return this.searchNode(this.root, value);\n }\n\n /**\n * Returns all values in sorted (ascending) order via in-order traversal.\n * Time complexity: O(n).\n */\n inorder(): T[] {\n const result: T[] = [];\n this.inorderTraversal(this.root, result);\n return result;\n }\n\n // ── Private Helpers ────────────────────────────────────────────────────────\n\n private insertNode(node: Nullable>, value: T): BSTNode {\n // Base case: found an empty slot — create and return a new node\n if (node === null) {\n return createNode(value);\n }\n\n const cmp = this.compare(value, node.value);\n\n if (cmp < 0) {\n // Value is smaller — go left\n node.left = this.insertNode(node.left, value);\n } else if (cmp > 0) {\n // Value is larger — go right\n node.right = this.insertNode(node.right, value);\n }\n // cmp === 0: duplicate, do nothing\n\n return node;\n }\n\n private deleteNode(node: Nullable>, value: T): Nullable> {\n // Base case: value not found\n if (node === null) return null;\n\n const cmp = this.compare(value, node.value);\n\n if (cmp < 0) {\n // Target is in the left subtree\n node.left = this.deleteNode(node.left, value);\n } else if (cmp > 0) {\n // Target is in the right subtree\n node.right = this.deleteNode(node.right, value);\n } else {\n // Found the node to delete — handle 3 cases:\n\n // Case 1: Leaf node (no children)\n if (node.left === null && node.right === null) {\n return null;\n }\n\n // Case 2a: Only right child\n if (node.left === null) {\n return node.right;\n }\n\n // Case 2b: Only left child\n if (node.right === null) {\n return node.left;\n }\n\n // Case 3: Two children\n // Replace with in-order successor (smallest value in right subtree),\n // then delete the successor from the right subtree.\n const successor = this.findMin(node.right);\n node.value = successor.value;\n node.right = this.deleteNode(node.right, successor.value);\n }\n\n return node;\n }\n\n private searchNode(node: Nullable>, value: T): Nullable> {\n if (node === null) return null;\n\n const cmp = this.compare(value, node.value);\n\n if (cmp < 0) return this.searchNode(node.left, value);\n if (cmp > 0) return this.searchNode(node.right, value);\n return node; // cmp === 0: found\n }\n\n private inorderTraversal(node: Nullable>, result: T[]): void {\n if (node === null) return;\n this.inorderTraversal(node.left, result);\n result.push(node.value);\n this.inorderTraversal(node.right, result);\n }\n\n /**\n * Returns the node with the minimum value in a given subtree.\n * Assumes the subtree is non-null.\n */\n private findMin(node: BSTNode): BSTNode {\n let current = node;\n while (current.left !== null) {\n current = current.left;\n }\n return current;\n }\n}\n\n// ─── Usage Examples ───────────────────────────────────────────────────────────\n\n// --- Numbers ---\nconst numTree = new BinarySearchTree();\n\nnumTree.insert(10);\nnumTree.insert(5);\nnumTree.insert(15);\nnumTree.insert(3);\nnumTree.insert(7);\nnumTree.insert(12);\nnumTree.insert(20);\n\nconsole.log(\"Inorder:\", numTree.inorder());\n// → [3, 5, 7, 10, 12, 15, 20]\n\nconsole.log(\"Search 7:\", numTree.search(7));\n// → { value: 7, left: null, right: null }\n\nconsole.log(\"Search 99:\", numTree.search(99));\n// → null\n\nnumTree.delete(5); // Node with two children\nconsole.log(\"After deleting 5:\", numTree.inorder());\n// → [3, 7, 10, 12, 15, 20]\n\nnumTree.delete(15); // Node with two children\nconsole.log(\"After deleting 15:\", numTree.inorder());\n// → [3, 7, 10, 12, 20]\n\nnumTree.delete(3); // Leaf node\nconsole.log(\"After deleting 3:\", numTree.inorder());\n// → [7, 10, 12, 20]\n\n// --- Strings (custom comparator) ---\nconst strTree = new BinarySearchTree((a, b) => a.localeCompare(b));\n\nstrTree.insert(\"banana\");\nstrTree.insert(\"apple\");\nstrTree.insert(\"cherry\");\nstrTree.insert(\"date\");\n\nconsole.log(\"String tree inorder:\", strTree.inorder());\n// → [\"apple\", \"banana\", \"cherry\", \"date\"]\n```\n\n---\n\n## Key Design Decisions\n\n| Decision | Rationale |\n|---|---|\n| **Generic ``** | Works with numbers, strings, or any custom type |\n| **Custom `Comparator`** | Makes the tree flexible for non-primitive types (e.g., objects) |\n| **Interface `IBinarySearchTree`** | Enforces the public API contract and is easy to extend or mock |\n| **`Nullable` alias** | Cleaner than repeating `T \\| null` everywhere |\n| **In-order successor for delete** | Standard, correct approach for two-child deletion |\n| **Duplicate values ignored** | Common BST convention; easily changed to allow duplicates |\n| **Recursive implementation** | Clear and readable; for very deep trees, an iterative version avoids stack overflow |\n\n## Complexity Summary\n\n| Operation | Average Case | Worst Case (skewed tree) |\n|---|---|---|\n| `insert` | O(log n) | O(n) |\n| `delete` | O(log n) | O(n) |\n| `search` | O(log n) | O(n) |\n| `inorder` | O(n) | O(n) |\n\nFor guaranteed O(log n) worst-case performance, consider extending this into a self-balancing tree such as an AVL tree or Red-Black tree." } } ]