From 04f1f7712e2bee04ad1371c8f9d25b133de2e7e3 Mon Sep 17 00:00:00 2001 From: kevin <18392136187@163.com> Date: Mon, 8 May 2023 23:20:53 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BF=AE=E6=94=B9=E5=86=B2=E7=AA=81=20?= =?UTF-8?q?feat:=20add=20BinaryTree=20(#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: 修改(prettier/prettier) 报错 * docs: update README.md * docs: update no-console 报 error * docs: update no-constant-condition 报 error * docs: update no-constant-condition 报 error * docs: 添加 IPv6 正则表达式 * docs: 添加 IPv6 正则表达式 * docs: delete no-console * docs: dele ipv6 * feat: 添加数据脱敏 * feat: 添加数据脱敏 * feat: 添加数据脱敏 * feat: 添加 双向链表 * fix: 修改冲突 * feat: add 表单数据转成JSON * feat: add 表单数据转成JSON * fix: 修改build报错 * docs: prefer-const * docs: add BothLinkedList index.md * feat: add BinaryTree * feat: add BinaryTree --- .eslintrc | 2 +- src/BinaryTree/index.md | 50 +++++++++ src/BinaryTree/index.ts | 206 ++++++++++++++++++++++++++++++++++++ src/BothLinkedList/index.md | 44 ++++++++ src/BothLinkedList/index.ts | 175 ++++++++++++++++++++++++++++++ src/index.ts | 3 +- test/format.test.js | 22 +++- 7 files changed, 499 insertions(+), 3 deletions(-) create mode 100644 src/BinaryTree/index.md create mode 100644 src/BinaryTree/index.ts create mode 100644 src/BothLinkedList/index.md create mode 100644 src/BothLinkedList/index.ts diff --git a/.eslintrc b/.eslintrc index 139395b..262c2ed 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,7 +20,7 @@ "@typescript-eslint/no-explicit-any": "off", "no-constant-condition": "off", "@typescript-eslint/no-this-alias": "off", - "prefer-const": "off", + "prefer-const": "off" }, "ignorePatterns": ["**/*.test.js"] } diff --git a/src/BinaryTree/index.md b/src/BinaryTree/index.md new file mode 100644 index 0000000..0c1b0e2 --- /dev/null +++ b/src/BinaryTree/index.md @@ -0,0 +1,50 @@ +--- +title: BinaryTree - 二叉树 +nav: + title: 使用文档 + path: /lib +group: + path: /format + title: 数据结构相关 + order: 2 +--- + +## BinaryTree + +> 二叉树 +> +> bt.insert(param):  param 是 需要插入的元素
+> bt.find(param):  param 是 需要寻找的元素,返回值为所找到的节点
+> bt.removeNode(param):  param 需要删除的元素
+> bt.getRoot():  获取二叉树的根节点
+> bt.getMinNode():  获取二叉树 某个节点的最小值
+> bt.getMaxNode():  获取二叉树 某个节点的最小值
+> bt.preOrderTraversal(bt.getRoot()):  需要传入进行遍历的节点, 遍历结束之后, 可以通过 bt.getRoot().preOrder 获取先序遍历的结果集
+> bt.centerOrderTraversal(bt.getRoot()): 需要传入进行遍历的节点, 遍历结束之后, 可以通过 bt.getRoot().centerOrder 获取中序遍历的结果集
+> bt.postOrderTraversal(bt.getRoot()): 需要传入进行遍历的节点, 遍历结束之后, 可以通过 bt.getRoot().postOrder 获取后序遍历的结果集
+ +Demo: + +```tsx | pure +import { BinaryTree } from 'xijs'; +const bt = new BinaryTree(); +bt.insert(1); +bt.insert(2); +bt.insert(0); +// bt.insert(4); +// bt.insert(-1); + +const res = bt.find(2); +console.log('find: ', res) +bt.removeNode(2) +console.log('getRoot: ', bt.getRoot()) +console.log('getMinNode: ', bt.getMinNode()) +console.log('getMaxNode: ', bt.getMaxNode()) +bt.preOrderTraversal(bt.getRoot()) +bt.centerOrderTraversal(bt.getRoot()) +bt.postOrderTraversal(bt.getRoot()) +console.log(`preOrder: `, bt.getRoot().preOrder) +console.log(`centerOrder: `, bt.getRoot().centerOrder) +console.log(`postOrder: `, bt.getRoot().postOrder) + +``` diff --git a/src/BinaryTree/index.ts b/src/BinaryTree/index.ts new file mode 100644 index 0000000..180126c --- /dev/null +++ b/src/BinaryTree/index.ts @@ -0,0 +1,206 @@ +/** + * 定义二叉树的结构 + */ +class TreeNode { + data: T; + left: TreeNode | undefined; + right: TreeNode | undefined; + count = 0; + preOrder: Array = []; + centerOrder: Array = []; + postOrder: Array = []; + constructor( + data: T, + left: TreeNode | undefined, + right: TreeNode | undefined, + ) { + this.data = data; + this.left = left; + this.right = right; + } +} + +/** + * 定义二叉树的操作 + */ +class BinaryTree { + root: TreeNode | undefined; + + constructor() { + this.root = undefined; + } + + insert(data: T): void { + const newTreeNode: TreeNode = new TreeNode(data, undefined, undefined); + if (this.root === undefined) { + this.root = newTreeNode; + this.root.count++; + } else { + let curNode = this.root; + let parentNode = undefined; + while (true) { + parentNode = curNode; + if (curNode.data > data) { + curNode = curNode.left!; + if (!curNode) { + parentNode.left = newTreeNode; + this.root.count++; + break; + } + } else if (curNode.data < data) { + curNode = curNode.right!; + if (!curNode) { + parentNode.right = newTreeNode; + this.root.count++; + break; + } + } else if (newTreeNode.data === curNode.data) { + // 如果给定的数据再次出现,就更新计数值 + curNode.count++; + break; + } + } + } + } + + /** + * 根据元素值 寻找 节点 + * @param data + */ + find(data: T): TreeNode | undefined { + let currNode: TreeNode | undefined = this.root; + while (currNode) { + if (currNode.data == data) { + return currNode; + } else if (data < currNode.data) { + currNode = currNode.left; + } else { + currNode = currNode.right; + } + } + return undefined; + } + + /** + * 获取根节点 + */ + getRoot(): TreeNode | undefined { + return this.root; + } + + /** + * 删除节点 + */ + removeNode(data: T) { + this._removeNode(this.root, data); + } + _removeNode( + curNode: TreeNode | undefined, + data: T, + ): TreeNode | undefined { + if (curNode === undefined) { + return undefined; + } + if (curNode.data === data) { + /** + * 左子树和右子树都为空,说明是叶子节点,可以直接删除 + */ + if (curNode.left === undefined && curNode.right === undefined) { + return undefined; + } + if (curNode.left === undefined) { + return curNode.right; + } + if (curNode.right === undefined) { + return curNode.left; + } + /** + * 如果两个节点都存在,则需要寻找右子树上的最小值,因为二叉树从左子树到根节点再到右子树,是从小到大排序 + */ + let tmpNode = this.getMinNode(curNode.right); + curNode.data = tmpNode.data; + curNode.right = this._removeNode(curNode.right, tmpNode.data); + this.root!.count--; + return curNode; + } else if (curNode.data > data) { + /** + * 如果当前节点值 > 需要寻找的 data,则需要在左子树继续寻找 + */ + curNode.left = this._removeNode(curNode.left, data); + this.root!.count--; + return curNode; + } else if (curNode.data < data) { + /** + * 如果当前节点值 < 需要寻找的 data,则需要在右子树继续寻找 + */ + curNode.right = this._removeNode(curNode.right, data); + this.root!.count--; + return curNode; + } + } + + /** + * 获取最大节点 + * @param curNode + */ + getMaxNode(node = this.root!) { + let curNode = node; + while (curNode.right) { + curNode = curNode.right; + } + return curNode; + } + + /** + * 获取最小节点 + * @param curNode + */ + getMinNode(node = this.root!) { + let curNode = node; + while (curNode.left) { + curNode = curNode.left; + } + return curNode; + } + + /** + * 先序遍历,遍历结束之后,可以通过 bt.getRoot().preOrder 获取遍历以后的元素数组 + * @param root + */ + preOrderTraversal(root: TreeNode) { + if (root === undefined) { + return undefined; + } + this.preOrderTraversal(root.left!); + this.root!.preOrder.push(root.data); + this.preOrderTraversal(root.right!); + } + + /** + * 中序遍历,遍历结束之后,可以通过 bt.getRoot().centerOrder 获取遍历以后的元素数组 + * @param root + */ + centerOrderTraversal(root: TreeNode) { + if (root === undefined) { + return undefined; + } + this.root!.centerOrder.push(root.data); + this.centerOrderTraversal(root.left!); + this.centerOrderTraversal(root.right!); + } + + /** + * 后序遍序遍历,遍历结束之后,可以通过 bt.getRoot().postOrder 获取遍历以后的元素数组 + * @param root + */ + postOrderTraversal(root: TreeNode) { + if (root === undefined) { + return undefined; + } + this.postOrderTraversal(root.left!); + this.postOrderTraversal(root.right!); + this.root!.postOrder.push(root.data); + } +} + +export default BinaryTree; diff --git a/src/BothLinkedList/index.md b/src/BothLinkedList/index.md new file mode 100644 index 0000000..5a4387d --- /dev/null +++ b/src/BothLinkedList/index.md @@ -0,0 +1,44 @@ +--- +title: bothLinkedList - 双向链表 +nav: + title: 使用文档 + path: /lib +group: + path: /format + title: 数据结构相关 + order: 2 +--- + +## bothLinkedList + +> 双向链表 +> +> bothLinkedList.insertHead(param): param 是 需要插入的元素,插入到元素链表尾部
+> bothLinkedList.insertIndex(param, index): param 是 需要插入的元素,index: 需要插入的位置
+> bothLinkedList.getHead(): 从头开始遍历链表
+> bothLinkedList.getTail(): 从尾开始遍历链表
+> bothLinkedList.getData(index): index: 通过索引获取元素值
+> bothLinkedList.getSize(): 获取链表长度
+> bothLinkedList.deleteFrom(index): index 通过索引删除元素节点
+> bothLinkedList.deleteData(param): index 通过元素值删除元素节点
+> +> 返回值:Node 类型: +> +> { data: 1, next: undefined, prev: undefined } + +Demo: + +```tsx | pure +import { BothLinkedList } from 'xijs'; + +let bothLinkedList = new BothLinkedList() +bothLinkedList.insertHead(1); +bothLinkedList.insertHead(2); +// bothLinkedList.insertHead(3) +console.log(bothLinkedList.getHead()) +bothLinkedList.deleteFrom(1) +bothLinkedList.deleteData(1) +console.log(bothLinkedList.getHead()) +// console.log(bothLinkedList.getTail()) +// console.log(bothLinkedList.getData(3)) +``` diff --git a/src/BothLinkedList/index.ts b/src/BothLinkedList/index.ts new file mode 100644 index 0000000..ab7867a --- /dev/null +++ b/src/BothLinkedList/index.ts @@ -0,0 +1,175 @@ +/** + * 定义链表结构 + * @param data + * @param next + * @constructor + */ +class Node { + data: T; + next: Node | undefined; + prev: Node | undefined; + + constructor(data: T) { + this.data = data; + } +} +class BothLinkedList { + private head: Node | undefined; + private tail: Node | undefined; + private size = 0; + + /** + * 插入元素 采用尾插法 + * @param data + */ + insertHead(data: T): void { + const node = new Node(data); + if (this.size === 0) { + this.head = node; + this.tail = node; + } else { + node.prev = this.tail; + this.tail!.next = node; + this.tail = node; + } + this.size++; + } + + /** + * 根据索引插入元素 + * @param data + * @param index + */ + insertIndex(data: T, index: number): void { + if (index < 0 || index > this.size) { + throw new Error('要插入的索引已经超过了链表的最大长度'); + } + const node = new Node(data); + /** + * 如果链表为空,直接插入 + */ + if (this.head === null) { + this.head = node; + this.tail = node; + } + /** + * 如果 index === 0 则插入的位置是头 + * 如果 index === this.size 则插入的位置是尾 + */ + if (index === 0) { + node.next = this.head; + this.head!.prev = node; + this.head = node; + } else if (index === this.size) { + node.prev = this.tail; + this.tail!.next = node; + this.tail = node; + } else { + let current = this.head!; + for (let i = 0; i < index - 1; i++) { + current = current.next!; + } + node.prev = current; + node.next = current.next; + current.next!.prev = node; + current.next = node; + } + this.size++; + } + + /** + * 删除元素(根据元素值进行删除) + */ + deleteData(data: T): void { + if (this.size === 0) { + throw Error('双向链表为空,不能删除'); + } + let current = this.head!; + while (current) { + if (current.data === data) { + if (this.size === 1) { + this.head = undefined; + this.tail = undefined; + } else if (current === this.head) { + this.head = this.head!.next; + this.head!.prev = undefined; + } else if (current === this.tail) { + this.tail = this.tail!.prev; + this.tail!.next = undefined; + } else { + current.prev!.next = current.next; + current.next!.prev = current.prev; + } + + this.size--; + return; + } + current = current!.next!; + } + } + + /** + * 根据元素索引删除 + * @param index + */ + deleteFrom(index: number): void { + if (index < 0 || index >= this.size) { + throw new Error('双向链表越界'); + } + if (this.size === 1) { + this.head = undefined; + this.tail = undefined; + } else if (index === 0) { + this.head = this.head!.next!; + this.head!.prev = undefined; + } else if (index === this.size - 1) { + this.tail = this.tail!.prev; + this.tail!.next = undefined; + } else { + let current = this.head!; + for (let i = 0; i < index; i++) { + current = current.next!; + } + current.prev!.next = current.next; + current.next!.prev = current.prev; + } + this.size--; + } + + /** + * 正向遍历 + */ + getHead(): Node | undefined { + return this.head; + } + + /** + * 反向遍历 + */ + getTail(): Node | undefined { + return this.tail; + } + + /** + * 获取链表长度 + */ + getSize(): number { + return this.size; + } + + /** + * 更具索引,获取元素值 + * @param index + */ + getData(index: number): T | undefined { + if (index < 0 || index >= this.size) { + throw Error('要查找的索引已经超过了链表的最大长度'); + } + let current = this.head; + for (let i = 0; i < index; i++) { + current = current!.next; + } + return current!.data; + } +} +export default BothLinkedList; diff --git a/src/index.ts b/src/index.ts index 7740452..e39aca4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -75,8 +75,9 @@ export { default as completeIp } from './completeIp'; export { default as capitalizedAmount } from './capitalizedAmount'; export { default as dataDesensitization } from './dataDesensitization'; -// export { default as BothLinkedList } from './BothLinkedList'; +export { default as BothLinkedList } from './BothLinkedList'; export { default as XCookie } from './XCookie'; export { default as formDataToJson } from './formDataToJson'; +export { default as BinaryTree } from './BinaryTree'; diff --git a/test/format.test.js b/test/format.test.js index 9c24eff..8803fcf 100644 --- a/test/format.test.js +++ b/test/format.test.js @@ -10,7 +10,8 @@ import { dateCalculate, timeSub, timeCutStr, - formDataToJson + formDataToJson, + BinaryTree } from '../src/index'; describe('数据结构相关测试', () => { test('数据深拷贝', () => { @@ -280,4 +281,23 @@ describe('数据结构相关测试', () => { const data1 = new FormData(); expect(formDataToJson(data1)).toEqual('{}'); }) + + test('二叉树', () => { + const bt = new BinaryTree(); + expect(bt.insert(1)).toEqual(); + expect(bt.getRoot()).toEqual({"centerOrder": [], "count": 1, "data": 1, "left": undefined, "postOrder": [], "preOrder": [], "right": undefined}); + expect(bt.insert(2)).toEqual(); + expect(bt.insert(0)).toEqual(); + expect(bt.find(2)).toEqual({"centerOrder": [], "count": 0, "data": 2, "left": undefined, "postOrder": [], "preOrder": [], "right": undefined}); + expect(bt.getMinNode(bt.getRoot())).toEqual({"centerOrder": [], "count": 0, "data": 0, "left": undefined, "postOrder": [], "preOrder": [], "right": undefined}); + expect(bt.getMaxNode(bt.getRoot())).toEqual({"centerOrder": [], "count": 0, "data": 2, "left": undefined, "postOrder": [], "preOrder": [], "right": undefined}); + expect(bt.removeNode(2)).toEqual(undefined); + bt.insert(2) + bt.preOrderTraversal(bt.getRoot()) + bt.centerOrderTraversal(bt.getRoot()) + bt.postOrderTraversal(bt.getRoot()) + expect(bt.getRoot().preOrder).toEqual([0, 1, 2]); + expect(bt.getRoot().centerOrder).toEqual([1, 0, 2]); + expect(bt.getRoot().postOrder).toEqual([0, 2, 1]); + }) });