From b14568151c1e122dd9778c2abf594bbfb9459abf Mon Sep 17 00:00:00 2001 From: Justin Tse Date: Tue, 7 Feb 2023 01:21:58 +0800 Subject: [PATCH] Add TypeScript code and docs to AVL tree and the coding style for Typescript and JavaScript (#342) * Add TypeScript code and docs to AVL tree and update JavaScript style * Update the coding style for Typescript and JavaScript --- .../worst_best_time_complexity.js | 2 +- codes/javascript/chapter_tree/avl_tree.js | 27 ++- .../worst_best_time_complexity.ts | 12 +- .../chapter_searching/linear_search.ts | 2 +- codes/typescript/chapter_tree/avl_tree.ts | 228 ++++++++++++++++++ codes/typescript/module/ListNode.ts | 19 +- codes/typescript/module/TreeNode.ts | 19 +- .../time_complexity.md | 8 +- docs/chapter_tree/avl_tree.md | 185 ++++++++++++-- 9 files changed, 453 insertions(+), 49 deletions(-) create mode 100644 codes/typescript/chapter_tree/avl_tree.ts diff --git a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js index 488ff6534..f2835900b 100644 --- a/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js +++ b/codes/javascript/chapter_computational_complexity/worst_best_time_complexity.js @@ -6,7 +6,7 @@ /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n) { - let nums = Array(n); + const nums = Array(n); // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; diff --git a/codes/javascript/chapter_tree/avl_tree.js b/codes/javascript/chapter_tree/avl_tree.js index 8cbacf61c..580989c2b 100644 --- a/codes/javascript/chapter_tree/avl_tree.js +++ b/codes/javascript/chapter_tree/avl_tree.js @@ -1,11 +1,11 @@ /** - * File: avl_tree.cpp + * File: avl_tree.js * Created Time: 2023-02-05 * Author: what-is-me (whatisme@outlook.jp) */ -let { TreeNode } = require("../include/TreeNode"); -let { printTree } = require("../include/PrintUtil"); +const { TreeNode } = require("../include/TreeNode"); +const { printTree } = require("../include/PrintUtil"); /* AVL 树*/ class AVLTree { @@ -36,8 +36,8 @@ class AVLTree { /* 右旋操作 */ rightRotate(node) { - let child = node.left; - let grandChild = child.right; + const child = node.left; + const grandChild = child.right; // 以 child 为原点,将 node 向右旋转 child.right = node; node.left = grandChild; @@ -50,8 +50,8 @@ class AVLTree { /* 左旋操作 */ leftRotate(node) { - let child = node.right; - let grandChild = child.left; + const child = node.right; + const grandChild = child.left; // 以 child 为原点,将 node 向左旋转 child.left = node; node.right = grandChild; @@ -65,7 +65,7 @@ class AVLTree { /* 执行旋转操作,使该子树重新恢复平衡 */ rotate(node) { // 获取结点 node 的平衡因子 - let balanceFactor = this.balanceFactor(node); + const balanceFactor = this.balanceFactor(node); // 左偏树 if (balanceFactor > 1) { if (this.balanceFactor(node.left) >= 0) { @@ -126,14 +126,14 @@ class AVLTree { else if (val > node.val) node.right = this.removeHelper(node.right, val); else { if (node.left === null || node.right === null) { - let child = node.left !== null ? node.left : node.right; + const child = node.left !== null ? node.left : node.right; // 子结点数量 = 0 ,直接删除 node 并返回 if (child === null) return null; // 子结点数量 = 1 ,直接删除 node else node = child; } else { // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - let temp = this.getInOrderNext(node.right); + const temp = this.getInOrderNext(node.right); node.right = this.removeHelper(node.right, temp.val); node.val = temp.val; } @@ -184,8 +184,9 @@ function testRemove(tree, val) { printTree(tree.root); } +/* Driver Code */ /* 初始化空 AVL 树 */ -let avlTree = new AVLTree(); +const avlTree = new AVLTree(); /* 插入结点 */ // 请关注插入结点后,AVL 树是如何保持平衡的 testInsert(avlTree, 1); @@ -209,5 +210,5 @@ testRemove(avlTree, 5); // 删除度为 1 的结点 testRemove(avlTree, 4); // 删除度为 2 的结点 /* 查询结点 */ -let node = avlTree.search(7); -console.log("\n查找到的结点对象为 " + node + ",结点值 = " + node.val); +const node = avlTree.search(7); +console.log("\n查找到的结点对象为", node, ",结点值 = " + node.val); diff --git a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts index d273ee2bc..45e3d6c5e 100644 --- a/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts +++ b/codes/typescript/chapter_computational_complexity/worst_best_time_complexity.ts @@ -6,15 +6,15 @@ /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n: number): number[] { - let nums = Array(n); + const nums = Array(n); // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } // 随机打乱数组元素 for (let i = 0; i < n; i++) { - let r = Math.floor(Math.random() * (i + 1)); - let temp = nums[i]; + const r = Math.floor(Math.random() * (i + 1)); + const temp = nums[i]; nums[i] = nums[r]; nums[r] = temp; } @@ -35,9 +35,9 @@ function findOne(nums: number[]): number { /* Driver Code */ for (let i = 0; i < 10; i++) { - let n = 100; - let nums = randomNumbers(n); - let index = findOne(nums); + const n = 100; + const nums = randomNumbers(n); + const index = findOne(nums); console.log( "\n数组 [ 1, 2, ..., n ] 被打乱后 = [" + nums.join(", ") + "]" ); diff --git a/codes/typescript/chapter_searching/linear_search.ts b/codes/typescript/chapter_searching/linear_search.ts index 530845c1b..e02dd9168 100644 --- a/codes/typescript/chapter_searching/linear_search.ts +++ b/codes/typescript/chapter_searching/linear_search.ts @@ -37,7 +37,7 @@ function linearSearchLinkedList(head: ListNode | null, target: number): ListNode const target = 3; /* 在数组中执行线性查找 */ -const nums = [ 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 ]; +const nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]; const index = linearSearchArray(nums, target); console.log('目标元素 3 的索引 =', index); diff --git a/codes/typescript/chapter_tree/avl_tree.ts b/codes/typescript/chapter_tree/avl_tree.ts new file mode 100644 index 000000000..bd3f9c659 --- /dev/null +++ b/codes/typescript/chapter_tree/avl_tree.ts @@ -0,0 +1,228 @@ +/** + * File: avl_tree.ts + * Created Time: 2023-02-06 + * Author: Justin (xiefahit@gmail.com) + */ + +import { TreeNode } from "../module/TreeNode"; +import { printTree } from "../module/PrintUtil"; + +/* AVL 树*/ +class AVLTree { + root: TreeNode; + /*构造函数*/ + constructor() { + this.root = null; //根节点 + } + + /* 获取结点高度 */ + height(node: TreeNode): number { + // 空结点高度为 -1 ,叶结点高度为 0 + return node === null ? -1 : node.height; + } + + /* 更新结点高度 */ + updateHeight(node: TreeNode): void { + // 结点高度等于最高子树高度 + 1 + node.height = Math.max(this.height(node.left), this.height(node.right)) + 1; + } + + /* 获取平衡因子 */ + balanceFactor(node: TreeNode): number { + // 空结点平衡因子为 0 + if (node === null) return 0; + // 结点平衡因子 = 左子树高度 - 右子树高度 + return this.height(node.left) - this.height(node.right); + } + + /* 右旋操作 */ + rightRotate(node: TreeNode): TreeNode { + const child = node.left; + const grandChild = child.right; + // 以 child 为原点,将 node 向右旋转 + child.right = node; + node.left = grandChild; + // 更新结点高度 + this.updateHeight(node); + this.updateHeight(child); + // 返回旋转后子树的根节点 + return child; + } + + /* 左旋操作 */ + leftRotate(node: TreeNode): TreeNode { + const child = node.right; + const grandChild = child.left; + // 以 child 为原点,将 node 向左旋转 + child.left = node; + node.right = grandChild; + // 更新结点高度 + this.updateHeight(node); + this.updateHeight(child); + // 返回旋转后子树的根节点 + return child; + } + + /* 执行旋转操作,使该子树重新恢复平衡 */ + rotate(node: TreeNode): TreeNode { + // 获取结点 node 的平衡因子 + const balanceFactor = this.balanceFactor(node); + // 左偏树 + if (balanceFactor > 1) { + if (this.balanceFactor(node.left) >= 0) { + // 右旋 + return this.rightRotate(node); + } else { + // 先左旋后右旋 + node.left = this.leftRotate(node.left); + return this.rightRotate(node); + } + } + // 右偏树 + if (balanceFactor < -1) { + if (this.balanceFactor(node.right) <= 0) { + // 左旋 + return this.leftRotate(node); + } else { + // 先右旋后左旋 + node.right = this.rightRotate(node.right); + return this.leftRotate(node); + } + } + // 平衡树,无需旋转,直接返回 + return node; + } + + /* 插入结点 */ + insert(val: number): TreeNode { + this.root = this.insertHelper(this.root, val); + return this.root; + } + + /* 递归插入结点(辅助函数) */ + insertHelper(node: TreeNode, val: number): TreeNode { + if (node === null) return new TreeNode(val); + /* 1. 查找插入位置,并插入结点 */ + if (val < node.val) { + node.left = this.insertHelper(node.left, val); + } else if (val > node.val) { + node.right = this.insertHelper(node.right, val); + } else { + return node; // 重复结点不插入,直接返回 + } + this.updateHeight(node); // 更新结点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = this.rotate(node); + // 返回子树的根节点 + return node; + } + + /* 删除结点 */ + remove(val: number): TreeNode { + this.root = this.removeHelper(this.root, val); + return this.root; + } + + /* 递归删除结点(辅助函数) */ + removeHelper(node: TreeNode, val: number): TreeNode { + if (node === null) return null; + /* 1. 查找结点,并删除之 */ + if (val < node.val) { + node.left = this.removeHelper(node.left, val); + } else if (val > node.val) { + node.right = this.removeHelper(node.right, val); + } else { + if (node.left === null || node.right === null) { + const child = node.left !== null ? node.left : node.right; + // 子结点数量 = 0 ,直接删除 node 并返回 + if (child === null) { + return null; + } else { + // 子结点数量 = 1 ,直接删除 node + node = child; + } + } else { + // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + const temp = this.getInOrderNext(node.right); + node.right = this.removeHelper(node.right, temp.val); + node.val = temp.val; + } + } + this.updateHeight(node); // 更新结点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = this.rotate(node); + // 返回子树的根节点 + return node; + } + + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + getInOrderNext(node: TreeNode): TreeNode { + if (node === null) return node; + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (node.left !== null) { + node = node.left; + } + return node; + } + + /* 查找结点 */ + search(val: number): TreeNode { + let cur = this.root; + // 循环查找,越过叶结点后跳出 + while (cur !== null) { + if (cur.val < val) { + // 目标结点在 cur 的右子树中 + cur = cur.right; + } else if (cur.val > val) { + // 目标结点在 cur 的左子树中 + cur = cur.left; + } else { + // 找到目标结点,跳出循环 + break; + } + } + // 返回目标结点 + return cur; + } +} + +function testInsert(tree: AVLTree, val: number): void { + tree.insert(val); + console.log("\n插入结点 " + val + " 后,AVL 树为"); + printTree(tree.root); +} + +function testRemove(tree: AVLTree, val: number): void { + tree.remove(val); + console.log("\n删除结点 " + val + " 后,AVL 树为"); + printTree(tree.root); +} + +/* Driver Code */ +/* 初始化空 AVL 树 */ +const avlTree = new AVLTree(); +/* 插入结点 */ +// 请关注插入结点后,AVL 树是如何保持平衡的 +testInsert(avlTree, 1); +testInsert(avlTree, 2); +testInsert(avlTree, 3); +testInsert(avlTree, 4); +testInsert(avlTree, 5); +testInsert(avlTree, 8); +testInsert(avlTree, 7); +testInsert(avlTree, 9); +testInsert(avlTree, 10); +testInsert(avlTree, 6); + +/* 插入重复结点 */ +testInsert(avlTree, 7); + +/* 删除结点 */ +// 请关注删除结点后,AVL 树是如何保持平衡的 +testRemove(avlTree, 8); // 删除度为 0 的结点 +testRemove(avlTree, 5); // 删除度为 1 的结点 +testRemove(avlTree, 4); // 删除度为 2 的结点 + +/* 查询结点 */ +const node = avlTree.search(7); +console.log("\n查找到的结点对象为", node, ",结点值 = " + node.val); diff --git a/codes/typescript/module/ListNode.ts b/codes/typescript/module/ListNode.ts index a1e6cf6ed..3a2ba2d7b 100644 --- a/codes/typescript/module/ListNode.ts +++ b/codes/typescript/module/ListNode.ts @@ -31,4 +31,21 @@ function arrToLinkedList(arr: number[]): ListNode | null { return dum.next; } -export { ListNode, arrToLinkedList }; +/** + * Get a list node with specific value from a linked list + * @param head + * @param val + * @return + */ +function getListNode(head: ListNode | null, val: number): ListNode | null { + while (head !== null && head.val !== val) { + head = head.next; + } + return head; +} + +export { + ListNode, + arrToLinkedList, + getListNode +}; diff --git a/codes/typescript/module/TreeNode.ts b/codes/typescript/module/TreeNode.ts index 0e3425006..b7b746c03 100644 --- a/codes/typescript/module/TreeNode.ts +++ b/codes/typescript/module/TreeNode.ts @@ -8,14 +8,15 @@ * Definition for a binary tree node. */ class TreeNode { - val: number; - left: TreeNode | null; - right: TreeNode | null; - - constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { - this.val = val === undefined ? 0 : val; // 结点值 - this.left = left === undefined ? null : left; // 左子结点指针 - this.right = right === undefined ? null : right; // 右子结点指针 + val: number; // 结点值 + height: number; // 结点高度 + left: TreeNode | null; // 左子结点指针 + right: TreeNode | null; // 右子结点指针 + constructor(val?: number, height?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = val === undefined ? 0 : val; + this.height = height === undefined ? 0 : height; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; } } @@ -33,7 +34,7 @@ function arrToTree(arr: (number | null)[]): TreeNode | null { const queue = [root]; let i = 0; while (queue.length) { - let node = queue.shift() as TreeNode; + const node = queue.shift() as TreeNode; if (++i >= arr.length) break; if (arr[i] !== null) { node.left = new TreeNode(arr[i] as number); diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 4a64327d1..e9d9b7528 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -2555,7 +2555,7 @@ $$ ```js title="worst_best_time_complexity.js" /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n) { - let nums = Array(n); + const nums = Array(n); // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; @@ -2588,15 +2588,15 @@ $$ ```typescript title="worst_best_time_complexity.ts" /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ function randomNumbers(n: number): number[] { - let nums = Array(n); + const nums = Array(n); // 生成数组 nums = { 1, 2, 3, ..., n } for (let i = 0; i < n; i++) { nums[i] = i + 1; } // 随机打乱数组元素 for (let i = 0; i < n; i++) { - let r = Math.floor(Math.random() * (i + 1)); - let temp = nums[i]; + const r = Math.floor(Math.random() * (i + 1)); + const temp = nums[i]; nums[i] = nums[r]; nums[r] = temp; } diff --git a/docs/chapter_tree/avl_tree.md b/docs/chapter_tree/avl_tree.md index 39cc374a6..80ae659b6 100755 --- a/docs/chapter_tree/avl_tree.md +++ b/docs/chapter_tree/avl_tree.md @@ -82,14 +82,14 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit ```js title="avl_tree.js" class TreeNode { val; // 结点值 + height; //结点高度 left; // 左子结点指针 right; // 右子结点指针 - height; //结点高度 constructor(val, left, right, height) { this.val = val === undefined ? 0 : val; + this.height = height === undefined ? 0 : height; this.left = left === undefined ? null : left; this.right = right === undefined ? null : right; - this.height = height === undefined ? 0 : height; } } ``` @@ -97,7 +97,18 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "TypeScript" ```typescript title="avl_tree.ts" - + class TreeNode { + val: number; // 结点值 + height: number; // 结点高度 + left: TreeNode | null; // 左子结点指针 + right: TreeNode | null; // 右子结点指针 + constructor(val?: number, height?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = val === undefined ? 0 : val; + this.height = height === undefined ? 0 : height; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; + } + } ``` === "C" @@ -228,7 +239,17 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "TypeScript" ```typescript title="avl_tree.ts" + /* 获取结点高度 */ + height(node: TreeNode): number { + // 空结点高度为 -1 ,叶结点高度为 0 + return node === null ? -1 : node.height; + } + /* 更新结点高度 */ + updateHeight(node: TreeNode): void { + // 结点高度等于最高子树高度 + 1 + node.height = Math.max(this.height(node.left), this.height(node.right)) + 1; + } ``` === "C" @@ -340,7 +361,13 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit === "TypeScript" ```typescript title="avl_tree.ts" - + /* 获取平衡因子 */ + balanceFactor(node: TreeNode): number { + // 空结点平衡因子为 0 + if (node === null) return 0; + // 结点平衡因子 = 左子树高度 - 右子树高度 + return this.height(node.left) - this.height(node.right); + } ``` === "C" @@ -479,8 +506,8 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 ```js title="avl_tree.js" /* 右旋操作 */ rightRotate(node) { - let child = node.left; - let grandChild = child.right; + const child = node.left; + const grandChild = child.right; // 以 child 为原点,将 node 向右旋转 child.right = node; node.left = grandChild; @@ -495,7 +522,19 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "TypeScript" ```typescript title="avl_tree.ts" - + /* 右旋操作 */ + rightRotate(node: TreeNode): TreeNode { + const child = node.left; + const grandChild = child.right; + // 以 child 为原点,将 node 向右旋转 + child.right = node; + node.left = grandChild; + // 更新结点高度 + this.updateHeight(node); + this.updateHeight(child); + // 返回旋转后子树的根节点 + return child; + } ``` === "C" @@ -624,8 +663,8 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 ```js title="avl_tree.js" /* 左旋操作 */ leftRotate(node) { - let child = node.right; - let grandChild = child.left; + const child = node.right; + const grandChild = child.left; // 以 child 为原点,将 node 向左旋转 child.left = node; node.right = grandChild; @@ -640,7 +679,19 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "TypeScript" ```typescript title="avl_tree.ts" - + /* 左旋操作 */ + leftRotate(node: TreeNode): TreeNode { + const child = node.right; + const grandChild = child.left; + // 以 child 为原点,将 node 向左旋转 + child.left = node; + node.right = grandChild; + // 更新结点高度 + this.updateHeight(node); + this.updateHeight(child); + // 返回旋转后子树的根节点 + return child; + } ``` === "C" @@ -843,7 +894,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 /* 执行旋转操作,使该子树重新恢复平衡 */ rotate(node) { // 获取结点 node 的平衡因子 - let balanceFactor = this.balanceFactor(node); + const balanceFactor = this.balanceFactor(node); // 左偏树 if (balanceFactor > 1) { if (this.balanceFactor(node.left) >= 0) { @@ -874,7 +925,35 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "TypeScript" ```typescript title="avl_tree.ts" - + /* 执行旋转操作,使该子树重新恢复平衡 */ + rotate(node: TreeNode): TreeNode { + // 获取结点 node 的平衡因子 + const balanceFactor = this.balanceFactor(node); + // 左偏树 + if (balanceFactor > 1) { + if (this.balanceFactor(node.left) >= 0) { + // 右旋 + return this.rightRotate(node); + } else { + // 先左旋后右旋 + node.left = this.leftRotate(node.left); + return this.rightRotate(node); + } + } + // 右偏树 + if (balanceFactor < -1) { + if (this.balanceFactor(node.right) <= 0) { + // 左旋 + return this.leftRotate(node); + } else { + // 先右旋后左旋 + node.right = this.rightRotate(node.right); + return this.leftRotate(node); + } + } + // 平衡树,无需旋转,直接返回 + return node; + } ``` === "C" @@ -1092,7 +1171,29 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 === "TypeScript" ```typescript title="avl_tree.ts" + /* 插入结点 */ + insert(val: number): TreeNode { + this.root = this.insertHelper(this.root, val); + return this.root; + } + /* 递归插入结点(辅助函数) */ + insertHelper(node: TreeNode, val: number): TreeNode { + if (node === null) return new TreeNode(val); + /* 1. 查找插入位置,并插入结点 */ + if (val < node.val) { + node.left = this.insertHelper(node.left, val); + } else if (val > node.val) { + node.right = this.insertHelper(node.right, val); + } else { + return node; // 重复结点不插入,直接返回 + } + this.updateHeight(node); // 更新结点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = this.rotate(node); + // 返回子树的根节点 + return node; + } ``` === "C" @@ -1333,14 +1434,14 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 else if (val > node.val) node.right = this.removeHelper(node.right, val); else { if (node.left === null || node.right === null) { - let child = node.left !== null ? node.left : node.right; + const child = node.left !== null ? node.left : node.right; // 子结点数量 = 0 ,直接删除 node 并返回 if (child === null) return null; // 子结点数量 = 1 ,直接删除 node else node = child; } else { // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 - let temp = this.getInOrderNext(node.right); + const temp = this.getInOrderNext(node.right); node.right = this.removeHelper(node.right, temp.val); node.val = temp.val; } @@ -1351,12 +1452,68 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影 // 返回子树的根节点 return node; } + + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + getInOrderNext(node) { + if (node === null) return node; + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (node.left !== null) { + node = node.left; + } + return node; + } ``` === "TypeScript" ```typescript title="avl_tree.ts" + /* 删除结点 */ + remove(val: number): TreeNode { + this.root = this.removeHelper(this.root, val); + return this.root; + } + /* 递归删除结点(辅助函数) */ + removeHelper(node: TreeNode, val: number): TreeNode { + if (node === null) return null; + /* 1. 查找结点,并删除之 */ + if (val < node.val) { + node.left = this.removeHelper(node.left, val); + } else if (val > node.val) { + node.right = this.removeHelper(node.right, val); + } else { + if (node.left === null || node.right === null) { + const child = node.left !== null ? node.left : node.right; + // 子结点数量 = 0 ,直接删除 node 并返回 + if (child === null) { + return null; + } else { + // 子结点数量 = 1 ,直接删除 node + node = child; + } + } else { + // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + const temp = this.getInOrderNext(node.right); + node.right = this.removeHelper(node.right, temp.val); + node.val = temp.val; + } + } + this.updateHeight(node); // 更新结点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = this.rotate(node); + // 返回子树的根节点 + return node; + } + + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + getInOrderNext(node: TreeNode): TreeNode { + if (node === null) return node; + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (node.left !== null) { + node = node.left; + } + return node; + } ``` === "C"