diff --git a/codes/javascript/chapter_tree/binary_search_tree.js b/codes/javascript/chapter_tree/binary_search_tree.js new file mode 100644 index 000000000..74b725080 --- /dev/null +++ b/codes/javascript/chapter_tree/binary_search_tree.js @@ -0,0 +1,146 @@ +/** + * File: binary_tree.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const Tree = require("../include/TreeNode"); +const { printTree } = require("../include/PrintUtil"); + +/* 二叉搜索树 */ +var root; + +function BinarySearchTree(nums) { + nums.sort((a,b) => { return a-b }); // 排序数组 + root = buildTree(nums, 0, nums.length - 1); // 构建二叉搜索树 +} + +/* 获取二叉树根结点 */ +function getRoot() { + return root; +} + +/* 构建二叉搜索树 */ +function buildTree(nums, i, j) { + if (i > j) return null; + // 将数组中间结点作为根结点 + let mid = Math.floor((i + j) / 2); + let root = new Tree.TreeNode(nums[mid]); + // 递归建立左子树和右子树 + root.left = buildTree(nums, i, mid - 1); + root.right = buildTree(nums, mid + 1, j); + return root; +} + +/* 查找结点 */ +function search(num) { + let cur = root; + // 循环查找,越过叶结点后跳出 + while (cur !== null) { + // 目标结点在 root 的右子树中 + if (cur.val < num) cur = cur.right; + // 目标结点在 root 的左子树中 + else if (cur.val > num) cur = cur.left; + // 找到目标结点,跳出循环 + else break; + } + // 返回目标结点 + return cur; +} + +/* 插入结点 */ +function insert(num) { + // 若树为空,直接提前返回 + if (root === null) return null; + let cur = root, pre = null; + // 循环查找,越过叶结点后跳出 + while (cur !== null) { + // 找到重复结点,直接返回 + if (cur.val === num) return null; + pre = cur; + // 插入位置在 root 的右子树中 + if (cur.val < num) cur = cur.right; + // 插入位置在 root 的左子树中 + else cur = cur.left; + } + // 插入结点 val + let node = new Tree.TreeNode(num); + if (pre.val < num) pre.right = node; + else pre.left = node; + return node; +} + +/* 删除结点 */ +function remove(num) { + // 若树为空,直接提前返回 + if (root === null) return null; + let cur = root, pre = null; + // 循环查找,越过叶结点后跳出 + while (cur !== null) { + // 找到待删除结点,跳出循环 + if (cur.val === num) break; + pre = cur; + // 待删除结点在 root 的右子树中 + if (cur.val < num) cur = cur.right; + // 待删除结点在 root 的左子树中 + else cur = cur.left; + } + // 若无待删除结点,则直接返回 + if (cur === null) return null; + // 子结点数量 = 0 or 1 + if (cur.left === null || cur.right === null) { + // 当子结点数量 = 0 / 1 时, child = null / 该子结点 + let child = cur.left !== null ? cur.left : cur.right; + // 删除结点 cur + if (pre.left === cur) pre.left = child; + else pre.right = child; + } + // 子结点数量 = 2 + else { + // 获取中序遍历中 cur 的下一个结点 + let nex = min(cur.right); + let tmp = nex.val; + // 递归删除结点 nex + remove(nex.val); + // 将 nex 的值复制给 cur + cur.val = tmp; + } + return cur; +} + +/* 获取最小结点 */ +function min(root) { + if (root === null) return root; + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (root.left !== null) { + root = root.left; + } + return root; +} + +/* Driver Code */ +/* 初始化二叉搜索树 */ +var nums = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]; +BinarySearchTree(nums) +console.log("\n初始化的二叉树为\n"); +printTree(getRoot()); + +/* 查找结点 */ +let node = search(5); +console.log("\n查找到的结点对象为 " + node + ",结点值 = " + node.val); + +/* 插入结点 */ +node = insert(16); +console.log("\n插入结点 16 后,二叉树为\n"); +printTree(getRoot()); + +/* 删除结点 */ +remove(1); +console.log("\n删除结点 1 后,二叉树为\n"); +printTree(getRoot()); +remove(2); +console.log("\n删除结点 2 后,二叉树为\n"); +printTree(getRoot()); +remove(4); +console.log("\n删除结点 4 后,二叉树为\n"); +printTree(getRoot()); diff --git a/codes/javascript/chapter_tree/binary_tree.js b/codes/javascript/chapter_tree/binary_tree.js new file mode 100644 index 000000000..1b91ef5a9 --- /dev/null +++ b/codes/javascript/chapter_tree/binary_tree.js @@ -0,0 +1,35 @@ +/** + * File: binary_tree.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const Tree = require("../include/TreeNode"); +const { printTree } = require("../include/PrintUtil"); + +/* 初始化二叉树 */ +// 初始化结点 +let n1 = new Tree.TreeNode(1), +n2 = new Tree.TreeNode(2), +n3 = new Tree.TreeNode(3), +n4 = new Tree.TreeNode(4), +n5 = new Tree.TreeNode(5); +// 构建引用指向(即指针) +n1.left = n2; +n1.right = n3; +n2.left = n4; +n2.right = n5; +console.log("\n初始化二叉树\n") +printTree(n1) + +/* 插入与删除结点 */ +let P = new Tree.TreeNode(0); +// 在 n1 -> n2 中间插入结点 P +n1.left = P; +P.left = n2; +console.log("\n插入结点 P 后\n"); +printTree(n1); +// 删除结点 P +n1.left = n2; +console.log("\n删除结点 P 后\n"); +printTree(n1); diff --git a/codes/javascript/chapter_tree/binary_tree_bfs.js b/codes/javascript/chapter_tree/binary_tree_bfs.js new file mode 100644 index 000000000..f7e6aa0e3 --- /dev/null +++ b/codes/javascript/chapter_tree/binary_tree_bfs.js @@ -0,0 +1,37 @@ +/** + * File: binary_tree.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const { arrToTree } = require("../include/TreeNode"); +const { printTree } = require("../include/PrintUtil"); + +/* 层序遍历 */ +function hierOrder(root) { + // 初始化队列,加入根结点 + let queue = [root]; + // 初始化一个列表,用于保存遍历序列 + let list = []; + while (queue.length) { + let node = queue.shift(); // 队列出队 + list.push(node.val); // 保存结点 + if (node.left) + queue.push(node.left); // 左子结点入队 + if (node.right) + queue.push(node.right); // 右子结点入队 + + } + return list; +} + +/* Driver Code */ +/* 初始化二叉树 */ +// 这里借助了一个从数组直接生成二叉树的函数 +var root = arrToTree([1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null ]); +console.log("\n初始化二叉树\n"); +printTree(root); + +/* 层序遍历 */ +let list = hierOrder(root); +console.log("\n层序遍历的结点打印序列 = " + list); \ No newline at end of file diff --git a/codes/javascript/chapter_tree/binary_tree_dfs.js b/codes/javascript/chapter_tree/binary_tree_dfs.js new file mode 100644 index 000000000..9dd3083ff --- /dev/null +++ b/codes/javascript/chapter_tree/binary_tree_dfs.js @@ -0,0 +1,61 @@ +/** + * File: binary_tree.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +const { arrToTree } = require("../include/TreeNode"); +const { printTree } = require("../include/PrintUtil"); + +// 初始化列表,用于存储遍历序列 +var list = [] + +/* 前序遍历 */ +function preOrder(root){ + if (root === null) return; + // 访问优先级:根结点 -> 左子树 -> 右子树 + list.push(root.val); + preOrder(root.left); + preOrder(root.right); + } + +/* 中序遍历 */ +function inOrder(root) { + if (root === null) return; + // 访问优先级:左子树 -> 根结点 -> 右子树 + inOrder(root.left); + list.push(root.val); + inOrder(root.right); +} + +/* 后序遍历 */ +function postOrder(root) { + if (root === null) return; + // 访问优先级:左子树 -> 右子树 -> 根结点 + postOrder(root.left); + postOrder(root.right); + list.push(root.val); +} + +/* Driver Code */ +/* 初始化二叉树 */ +// 这里借助了一个从数组直接生成二叉树的函数 +var root = arrToTree([1, 2, 3, 4, 5, 6, 7, null, null, null, null, null, null, null, null]); +console.log("\n初始化二叉树\n"); +printTree(root); + +/* 前序遍历 */ +list.length = 0; +preOrder(root); +console.log("\n前序遍历的结点打印序列 = " + list); + +/* 中序遍历 */ +list.length = 0; +inOrder(root); +console.log("\n中序遍历的结点打印序列 = " + list); + +/* 后序遍历 */ +list.length = 0; +postOrder(root); +console.log("\n后序遍历的结点打印序列 = " + list); + diff --git a/codes/javascript/include/PrintUtil.js b/codes/javascript/include/PrintUtil.js new file mode 100644 index 000000000..daeb24a7d --- /dev/null +++ b/codes/javascript/include/PrintUtil.js @@ -0,0 +1,88 @@ +/** + * File: PrintUtil.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +function Trunk(prev, str) { + this.prev = prev; + this.str = str; +} + +/** + * Print a linked list + * @param head + */ +function printLinkedList(head) { + let list = []; + while (head !== null) { + list.push(head.val.toString()); + head = head.next; + } + console.log(list.join(" -> ")); +} + +/** + * The interface of the tree printer + * This tree printer is borrowed from TECHIE DELIGHT + * https://www.techiedelight.com/c-program-print-binary-tree/ + * @param root + */ +function printTree(root) { + printTree(root, null, false); +} + +/** + * Print a binary tree + * @param root + * @param prev + * @param isLeft + */ +function printTree(root, prev, isLeft) { + if (root === null) { + return; + } + + let prev_str = " "; + let trunk = new Trunk(prev, prev_str); + + printTree(root.right, trunk, true); + + if (!prev) { + trunk.str = "———"; + } else if (isLeft) { + trunk.str = "/———"; + prev_str = " |"; + } else { + trunk.str = "\\———"; + prev.str = prev_str; + } + + showTrunks(trunk); + console.log(" " + root.val); + + if (prev) { + prev.str = prev_str; + } + trunk.str = " |"; + + printTree(root.left, trunk, false); +} + +/** + * Helper function to print branches of the binary tree + * @param p + */ +function showTrunks(p) { + if (!p) { + return; + } + + showTrunks(p.prev); + console.log(p.str); +} + +module.exports = { + printTree, + printLinkedList, +} diff --git a/codes/javascript/include/TreeNode.js b/codes/javascript/include/TreeNode.js new file mode 100644 index 000000000..20c709e96 --- /dev/null +++ b/codes/javascript/include/TreeNode.js @@ -0,0 +1,47 @@ +/** + * File: TreeNode.js + * Created Time: 2022-12-04 + * Author: IsChristina (christinaxia77@foxmail.com) + */ + +/** + * Definition for a binary tree node. + */ +function TreeNode(val, left, right) { + this.val = (val === undefined ? 0 : val) // 结点值 + this.left = (left === undefined ? null : left) // 左子结点指针 + this.right = (right === undefined ? null : right) // 右子结点指针 +} + +/** +* Generate a binary tree with an array +* @param arr +* @return +*/ +function arrToTree(arr) { + if (arr.length === 0) + return null; + + let root = new TreeNode(arr[0]); + let queue = [root] + let i = 1; + while(queue.length) { + let node = queue.shift(); + if(arr[i] !== null) { + node.left = new TreeNode(arr[i]); + queue.push(node.left); + } + i++; + if(arr[i] !== null) { + node.right = new TreeNode(arr[i]); + queue.push(node.right); + } + i++; + } + return root; +} + +module.exports = { + TreeNode, + arrToTree, +} \ No newline at end of file diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index 30d392999..8901b769d 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -112,7 +112,21 @@ comments: true === "JavaScript" ```js title="binary_search_tree.js" - + /* 查找结点 */ + function search(num) { + let cur = root; + // 循环查找,越过叶结点后跳出 + while (cur !== null) { + // 目标结点在 root 的右子树中 + if (cur.val < num) cur = cur.right; + // 目标结点在 root 的左子树中 + else if (cur.val > num) cur = cur.left; + // 找到目标结点,跳出循环 + else break; + } + // 返回目标结点 + return cur; + } ``` === "TypeScript" @@ -240,7 +254,27 @@ comments: true === "JavaScript" ```js title="binary_search_tree.js" - + /* 插入结点 */ + function insert(num) { + // 若树为空,直接提前返回 + if (root === null) return null; + let cur = root, pre = null; + // 循环查找,越过叶结点后跳出 + while (cur !== null) { + // 找到重复结点,直接返回 + if (cur.val === num) return null; + pre = cur; + // 插入位置在 root 的右子树中 + if (cur.val < num) cur = cur.right; + // 插入位置在 root 的左子树中 + else cur = cur.left; + } + // 插入结点 val + let node = new Tree.TreeNode(num); + if (pre.val < num) pre.right = node; + else pre.left = node; + return node; + } ``` === "TypeScript" @@ -471,7 +505,43 @@ comments: true === "JavaScript" ```js title="binary_search_tree.js" - + /* 删除结点 */ + function remove(num) { + // 若树为空,直接提前返回 + if (root === null) return null; + let cur = root, pre = null; + // 循环查找,越过叶结点后跳出 + while (cur !== null) { + // 找到待删除结点,跳出循环 + if (cur.val === num) break; + pre = cur; + // 待删除结点在 root 的右子树中 + if (cur.val < num) cur = cur.right; + // 待删除结点在 root 的左子树中 + else cur = cur.left; + } + // 若无待删除结点,则直接返回 + if (cur === null) return null; + // 子结点数量 = 0 or 1 + if (cur.left === null || cur.right === null) { + // 当子结点数量 = 0 / 1 时, child = null / 该子结点 + let child = cur.left !== null ? cur.left : cur.right; + // 删除结点 cur + if (pre.left === cur) pre.left = child; + else pre.right = child; + } + // 子结点数量 = 2 + else { + // 获取中序遍历中 cur 的下一个结点 + let nex = min(cur.right); + let tmp = nex.val; + // 递归删除结点 nex + remove(nex.val); + // 将 nex 的值复制给 cur + cur.val = tmp; + } + return cur; + } ``` === "TypeScript" diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index d7c35d928..5913a1a43 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -63,7 +63,12 @@ comments: true === "JavaScript" ```js title="" - + /* 链表结点类 */ + function TreeNode(val, left, right) { + this.val = (val === undefined ? 0 : val) // 结点值 + this.left = (left === undefined ? null : left) // 左子结点指针 + this.right = (right === undefined ? null : right) // 右子结点指针 + } ``` === "TypeScript" @@ -193,7 +198,18 @@ comments: true === "JavaScript" ```js title="binary_tree.js" - + /* 初始化二叉树 */ + // 初始化结点 + let n1 = new TreeNode(1), + n2 = new TreeNode(2), + n3 = new TreeNode(3), + n4 = new TreeNode(4), + n5 = new TreeNode(5); + // 构建引用指向(即指针) + n1.left = n2; + n1.right = n3; + n2.left = n4; + n2.right = n5; ``` === "TypeScript" @@ -265,7 +281,14 @@ comments: true === "JavaScript" ```js title="binary_tree.js" + /* 插入与删除结点 */ + let P = new TreeNode(0); + // 在 n1 -> n2 中间插入结点 P + n1.left = P; + P.left = n2; + // 删除结点 P + n1.left = n2; ``` === "TypeScript" @@ -387,7 +410,23 @@ comments: true === "JavaScript" ```js title="binary_tree_bfs.js" - + /* 层序遍历 */ + function hierOrder(root) { + // 初始化队列,加入根结点 + let queue = [root]; + // 初始化一个列表,用于保存遍历序列 + let list = []; + while (queue.length) { + let node = queue.shift(); // 队列出队 + list.push(node.val); // 保存结点 + if (node.left) + queue.push(node.left); // 左子结点入队 + if (node.right) + queue.push(node.right); // 右子结点入队 + + } + return list; + } ``` === "TypeScript" @@ -536,7 +575,32 @@ comments: true === "JavaScript" ```js title="binary_tree_dfs.js" + /* 前序遍历 */ + function preOrder(root){ + if (root === null) return; + // 访问优先级:根结点 -> 左子树 -> 右子树 + list.push(root.val); + preOrder(root.left); + preOrder(root.right); + } + /* 中序遍历 */ + function inOrder(root) { + if (root === null) return; + // 访问优先级:左子树 -> 根结点 -> 右子树 + inOrder(root.left); + list.push(root.val); + inOrder(root.right); + } + + /* 后序遍历 */ + function postOrder(root) { + if (root === null) return; + // 访问优先级:左子树 -> 右子树 -> 根结点 + postOrder(root.left); + postOrder(root.right); + list.push(root.val); + } ``` === "TypeScript"