From 55704871b67c4eae4942bc40aacaa4cdd048a6f5 Mon Sep 17 00:00:00 2001 From: Revone Date: Mon, 18 Sep 2023 22:04:39 +0800 Subject: [PATCH] v1.3.1 published, new Queue implemented from SinglyLinkedList, and ArrayQueue implemented from Array --- README.md | 44 +- package.json | 129 ++- .../binary-tree/abstract-binary-tree.ts | 2 +- src/data-structures/binary-tree/avl-tree.ts | 64 +- .../binary-tree/tree-multiset.ts | 4 +- .../linked-list/singly-linked-list.ts | 14 +- src/data-structures/queue/deque.ts | 48 ++ src/data-structures/queue/queue.ts | 90 +- src/interfaces/avl-tree.ts | 26 +- .../linked-list/singly-linked-list.test.ts | 802 +++++++++--------- 10 files changed, 695 insertions(+), 528 deletions(-) diff --git a/README.md b/README.md index 0c5fa9c..4557519 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@ # What ## Brief -Javascript & TypeScript Data Structure collections. +Data Structures of Javascript & TypeScript. -## Algorithms -DFS, DFSIterative, BFS, morris, Bellman-Ford Algorithm, Dijkstra's Algorithm, Floyd-Warshall Algorithm, Tarjan's Algorithm. Listed only a few out, you can discover more in API docs - -## Code design -By strictly adhering to object-oriented design (BinaryTree -> BST -> AVLTree -> TreeMultiset), you can seamlessly inherit the existing data structures to implement the customized ones you need. Object-oriented design stands as the optimal approach to data structure design. +## Built-in classic algorithms +DFS(Depth-First Search), DFSIterative, BFS(Breadth-First Search), +morris, Bellman-Ford Algorithm, Dijkstra's Algorithm, Floyd-Warshall Algorithm, +Tarjan's Algorithm. # How ## install ### npm ```bash -npm install data-structure-typed +npm install data-structure-typed --save ``` ### yarn ```bash @@ -28,17 +27,13 @@ const {AVLTree} = dataStructureTyped; const {Heap, MinHeap, SinglyLinkedList, Stack, AVLTreeNode, BST, Trie, DirectedGraph, DirectedVertex, TreeMultiset} = dataStructureTyped; ``` -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/dfs-pre-order.webp?raw=true) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/test-avl-tree.webp?raw=true) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/map-graph.webp?raw=true) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/test-graphs.webp?raw=true) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/cut-off-trees-for-golf.webp?raw=true) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/parenthesis-check.webp?raw=true) +![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/binary-tree-array-to-binary-tree.webp) +![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/binary-tree-dfs-in-order.webp) +![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/avl-tree-test.webp) +![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/tree-multiset-test.webp) +![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/matrix-cut-off-tree-for-golf.webp) +![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/directed-graph-test.webp) +![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/map-graph-test.webp) ## API docs & Examples @@ -46,12 +41,10 @@ const {Heap, MinHeap, SinglyLinkedList, Stack, AVLTreeNode, BST, Trie, DirectedG [Live Examples](https://vivid-algorithm.vercel.app) -[//]: # (Live Examples) - Examples Repository +## Code Snippet ### Binary Search Tree (BST) snippet - #### TS ```typescript import {BST, BSTNode} from 'data-structure-typed'; @@ -142,7 +135,6 @@ const {Heap, MinHeap, SinglyLinkedList, Stack, AVLTreeNode, BST, Trie, DirectedG ``` ### Directed Graph simple snippet - #### TS or JS ```typescript import {DirectedGraph} from 'data-structure-typed'; @@ -172,7 +164,6 @@ import {DirectedGraph} from 'data-structure-typed'; ``` ### Undirected Graph snippet - #### TS or JS ```typescript import {UndirectedGraph} from 'data-structure-typed'; @@ -189,6 +180,7 @@ import {UndirectedGraph} from 'data-structure-typed'; const dijkstraResult = graph.dijkstra('A'); Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.id) // ['A', 'B', 'D'] ``` + ## Data Structures @@ -431,7 +423,6 @@ import {UndirectedGraph} from 'data-structure-typed'; ## Complexities ### performance of Big O -
@@ -496,7 +487,6 @@ import {UndirectedGraph} from 'data-structure-typed';
### Data Structure Complexity - @@ -593,7 +583,6 @@ import {UndirectedGraph} from 'data-structure-typed';
### Sorting Complexity - @@ -691,6 +680,9 @@ import {UndirectedGraph} from 'data-structure-typed';
+## Code design +By strictly adhering to object-oriented design (BinaryTree -> BST -> AVLTree -> TreeMultiset), you can seamlessly inherit the existing data structures to implement the customized ones you need. Object-oriented design stands as the optimal approach to data structure design. + [//]: # (![overview diagram](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/assets/overview-diagram-of-data-structures.png?raw=true)) ![complexities](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/assets/complexities-diff.jpg?raw=true) diff --git a/package.json b/package.json index d3c09a1..82323c2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "data-structure-typed", - "version": "1.21.4", - "description": "Javascript & TypeScript Data Structure Library, meticulously crafted to empower developers with a versatile set of essential data structures. Our library includes a wide range of data structures, such as Binary Tree, AVL Tree, Binary Search Tree (BST), Tree Multiset, Segment Tree, Binary Indexed Tree, Graph, Directed Graph, Undirected Graph, Singly Linked List, Hash, CoordinateSet, CoordinateMap, Heap, Doubly Linked List, Priority Queue, Max Priority Queue, Min Priority Queue, Queue, ObjectDeque, ArrayDeque, Stack, and Trie. Each data structure is thoughtfully designed and implemented using TypeScript to provide efficient, reliable, and easy-to-use solutions for your programming needs. Whether you're optimizing algorithms, managing data, or enhancing performance, our TypeScript Data Structure Library is your go-to resource. Elevate your coding experience with these fundamental building blocks for software development.", + "version": "1.3.1", + "description": "Data Structures of Javascript & TypeScript. AVLTree, Binary Search Tree, Binary Tree, Tree Multiset, Graph, Heap, Priority Queue, Linked List.", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", @@ -19,38 +19,111 @@ "url": "git+https://github.com/zrwusa/data-structure-typed.git" }, "keywords": [ + "data structure", + "Data Structure", + "data-structure", + "data structures", + "Data Structures", + "data-structures", + "algorithm", + "binary search tree", + "Binary Search Tree", + "binary-search-tree", + "binary tree", "Binary Tree", - "Binary Search Tree (BST)", + "binary-tree", + "bst", + "BST", "AVL Tree", + "avl tree", + "avl-tree", + "avl", + "tree multiset", "Tree Multiset", - "Segment Tree", - "Binary Indexed Tree", - "Graph", - "Directed Graph", - "Undirected Graph", - "Linked List", - "Singly Linked List", - "Doubly Linked List", - "Queue", - "Object Deque", - "Array Deque", - "Stack", - "Hash", - "Coordinate Set", - "Coordinate Map", - "Heap", - "Priority Queue", - "Max Priority Queue", - "Min Priority Queue", - "Trie", + "tree-multiset", + "Tree Multiset", + "dfs", "DFS", - "DFSIterative", + "dfs iterative", + "DFS Iterative", + "bfs", "BFS", + "graph", + "Graph", + "directed graph", + "Directed Graph", + "directed-graph", + "undirected graph", + "Undirected Graph", + "undirected-graph", + "heap", + "Heap", + "priority queue", + "Priority Queue", + "priority-queue", + "max priority queue", + "Max Priority Queue", + "max-priority-queue", + "min priority queue", + "Min Priority Queue", + "min-priority-queue", + "deque", + "Deque", + "linked list", + "Linked List", + "linked-list", + "trie", + "Trie", + "prefix tree", + "Prefix Tree", + "prefix-tree", + "binary", + "DataStructure", + "DataStructures", + "data", + "structure", + "sort", + "segment tree", + "Segment Tree", + "segment-tree", + "binary indexed tree", + "Binary Indexed Tree", + "binary-indexed-tree", + "linked list", + "Linked List", + "linked-list", + "singly linked list", + "Singly Linked List", + "singly-linked-list", + "doubly linked list", + "Doubly Linked List", + "doubly-linked-list", + "queue", + "array queue", + "Array Queue", + "array-queue", + "Queue", + "object deque", + "Object Deque", + "array deque", + "Array Deque", + "stack", + "Stack", + "hash", + "Hash", "morris", - "Bellman-Ford ", - "Dijkstra's Algorithm", - "Floyd-Warshall Algorithm", - "Tarjan's Algorithm" + "Morris", + "bellman ford", + "Bellman Ford", + "bellman-ford", + "dijkstra", + "Dijkstra", + "floyd warshall", + "Floyd Warshall", + "floyd-warshall", + "Tarjan", + "tarjan", + "Tarjan's" ], "author": "Tyler Zeng zrwusa@gmail.com", "license": "MIT", diff --git a/src/data-structures/binary-tree/abstract-binary-tree.ts b/src/data-structures/binary-tree/abstract-binary-tree.ts index 3d20063..f80da72 100644 --- a/src/data-structures/binary-tree/abstract-binary-tree.ts +++ b/src/data-structures/binary-tree/abstract-binary-tree.ts @@ -725,7 +725,7 @@ export abstract class AbstractBinaryTree = AVLTreeNode> extends B override add(id: BinaryTreeNodeId, val?: N['val']): N | null | undefined { // TODO support node as a param const inserted = super.add(id, val); - if (inserted) this.balancePath(inserted); + if (inserted) this._balancePath(inserted); return inserted; } @@ -66,7 +66,7 @@ export class AVLTree = AVLTreeNode> extends B const deletedResults = super.remove(id, isUpdateAllLeftSum); for (const {needBalanced} of deletedResults) { if (needBalanced) { - this.balancePath(needBalanced); + this._balancePath(needBalanced); } } return deletedResults; @@ -78,7 +78,7 @@ export class AVLTree = AVLTreeNode> extends B * @param node - The parameter "node" is of type N, which represents a node in an AVL tree. * @returns The balance factor of the given AVL tree node. */ - balanceFactor(node: N): number { + protected _balanceFactor(node: N): number { if (!node.right) // node has no right subtree return -node.height; else if (!node.left) // node has no left subtree @@ -91,7 +91,7 @@ export class AVLTree = AVLTreeNode> extends B * The function updates the height of a node in an AVL tree based on the heights of its left and right subtrees. * @param node - The parameter `node` is an AVLTreeNode object, which represents a node in an AVL tree. */ - updateHeight(node: N): void { + protected _updateHeight(node: N): void { if (!node.left && !node.right) // node is a leaf node.height = 0; else if (!node.left) { @@ -105,31 +105,31 @@ export class AVLTree = AVLTreeNode> extends B } /** - * The `balancePath` function balances the AVL tree by performing appropriate rotations based on the balance factor of + * The `_balancePath` function balances the AVL tree by performing appropriate rotations based on the balance factor of * each node in the path from the given node to the root. * @param node - The `node` parameter is an AVLTreeNode object, which represents a node in an AVL tree. */ - balancePath(node: N): void { + protected _balancePath(node: N): void { const path = this.getPathToRoot(node); for (let i = path.length - 1; i >= 0; i--) { const A = path[i]; - this.updateHeight(A); - switch (this.balanceFactor(A)) { + this._updateHeight(A); + switch (this._balanceFactor(A)) { case -2: if (A && A.left) { - if (this.balanceFactor(A.left) <= 0) { - this.balanceLL(A); // Perform LL rotation + if (this._balanceFactor(A.left) <= 0) { + this._balanceLL(A); // Perform LL rotation } else { - this.balanceLR(A); // Perform LR rotation + this._balanceLR(A); // Perform LR rotation } } break; case +2: if (A && A.right) { - if (this.balanceFactor(A.right) >= 0) { - this.balanceRR(A); // Perform RR rotation + if (this._balanceFactor(A.right) >= 0) { + this._balanceRR(A); // Perform RR rotation } else { - this.balanceRL(A); // Perform RL rotation + this._balanceRL(A); // Perform RL rotation } } } @@ -137,10 +137,10 @@ export class AVLTree = AVLTreeNode> extends B } /** - * The `balanceLL` function performs a left-left rotation on an AVL tree to balance it. + * The `_balanceLL` function performs a left-left rotation on an AVL tree to balance it. * @param A - The parameter A is an AVLTreeNode object. */ - balanceLL(A: N): void { + protected _balanceLL(A: N): void { const parentOfA = A.parent; const B = A.left; // A is left-heavy and B is left-heavy A.parent = B; @@ -162,15 +162,15 @@ export class AVLTree = AVLTreeNode> extends B A.left = B.right; // Make T2 the left subtree of A B.right = A; // Make A the left child of B } - this.updateHeight(A); - if (B) this.updateHeight(B); + this._updateHeight(A); + if (B) this._updateHeight(B); } /** - * The `balanceLR` function performs a left-right rotation to balance an AVL tree. + * The `_balanceLR` function performs a left-right rotation to balance an AVL tree. * @param A - A is an AVLTreeNode object. */ - balanceLR(A: N): void { + protected _balanceLR(A: N): void { const parentOfA = A.parent; const B = A.left; // A is left-heavy let C = null; @@ -209,16 +209,16 @@ export class AVLTree = AVLTreeNode> extends B C.right = A; } - this.updateHeight(A); // Adjust heights - B && this.updateHeight(B); - C && this.updateHeight(C); + this._updateHeight(A); // Adjust heights + B && this._updateHeight(B); + C && this._updateHeight(C); } /** - * The `balanceRR` function performs a right-right rotation on an AVL tree to balance it. + * The `_balanceRR` function performs a right-right rotation on an AVL tree to balance it. * @param A - The parameter A is an AVLTreeNode object. */ - balanceRR(A: N): void { + protected _balanceRR(A: N): void { const parentOfA = A.parent; const B = A.right; // A is right-heavy and B is right-heavy A.parent = B; @@ -245,15 +245,15 @@ export class AVLTree = AVLTreeNode> extends B A.right = B.left; // Make T2 the right subtree of A B.left = A; } - this.updateHeight(A); - B && this.updateHeight(B); + this._updateHeight(A); + B && this._updateHeight(B); } /** - * The `balanceRL` function performs a right-left rotation to balance an AVL tree. + * The `_balanceRL` function performs a right-left rotation to balance an AVL tree. * @param A - A is an AVLTreeNode object. */ - balanceRL(A: N): void { + protected _balanceRL(A: N): void { const parentOfA = A.parent; const B = A.right; // A is right-heavy let C = null; @@ -292,9 +292,9 @@ export class AVLTree = AVLTreeNode> extends B if (C) C.left = A; if (C) C.right = B; - this.updateHeight(A); // Adjust heights - B && this.updateHeight(B); - C && this.updateHeight(C); + this._updateHeight(A); // Adjust heights + B && this._updateHeight(B); + C && this._updateHeight(C); } } diff --git a/src/data-structures/binary-tree/tree-multiset.ts b/src/data-structures/binary-tree/tree-multiset.ts index 327ef05..b8f4fe4 100644 --- a/src/data-structures/binary-tree/tree-multiset.ts +++ b/src/data-structures/binary-tree/tree-multiset.ts @@ -173,7 +173,7 @@ export class TreeMultiset = TreeMultiset } } } - if (inserted) this.balancePath(inserted); + if (inserted) this._balancePath(inserted); return inserted; } @@ -351,7 +351,7 @@ export class TreeMultiset = TreeMultiset bstDeletedResult.push({deleted: orgCurrent, needBalanced}); if (needBalanced) { - this.balancePath(needBalanced); + this._balancePath(needBalanced); } return bstDeletedResult; diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts index 1cbbcdd..fa4f651 100644 --- a/src/data-structures/linked-list/singly-linked-list.ts +++ b/src/data-structures/linked-list/singly-linked-list.ts @@ -38,7 +38,7 @@ export class SinglyLinkedListNode { } } -export class SinglyLinkedList { +export class SinglyLinkedList { /** * The constructor initializes the linked list with an empty head, tail, and length. @@ -116,8 +116,8 @@ export class SinglyLinkedList { * @returns The method `pop()` returns the value of the node that is being removed from the end of the linked list. If * the linked list is empty, it returns `null`. */ - pop(): T | null { - if (!this.head) return null; + pop(): T | undefined { + if (!this.head) return undefined; if (this.head === this.tail) { const val = this.head.val; this.head = null; @@ -141,8 +141,8 @@ export class SinglyLinkedList { * The `shift()` function removes and returns the value of the first node in a linked list. * @returns The value of the node that is being removed from the beginning of the linked list. */ - shift(): T | null { - if (!this.head) return null; + shift(): T | undefined { + if (!this.head) return undefined; const removedNode = this.head; this.head = this.head.next; this._length--; @@ -204,8 +204,8 @@ export class SinglyLinkedList { * @returns The method `deleteAt` returns the value of the node that was deleted, or `null` if the index is out of * bounds. */ - deleteAt(index: number): T | null { - if (index < 0 || index >= this.length) return null; + deleteAt(index: number): T | undefined { + if (index < 0 || index >= this.length) return undefined; if (index === 0) return this.shift(); if (index === this.length - 1) return this.pop(); diff --git a/src/data-structures/queue/deque.ts b/src/data-structures/queue/deque.ts index fc62a41..fb4aca7 100644 --- a/src/data-structures/queue/deque.ts +++ b/src/data-structures/queue/deque.ts @@ -63,6 +63,11 @@ export class ObjectDeque { return this._size; } + /** + * The "addFirst" function adds a value to the beginning of an array-like data structure. + * @param {T} value - The `value` parameter represents the value that you want to add to the beginning of the data + * structure. + */ addFirst(value: T) { if (this._size === 0) { const mid = Math.floor(this._capacity / 2); @@ -75,6 +80,10 @@ export class ObjectDeque { this._size++; } + /** + * The addLast function adds a value to the end of an array-like data structure. + * @param {T} value - The `value` parameter represents the value that you want to add to the end of the data structure. + */ addLast(value: T) { if (this._size === 0) { const mid = Math.floor(this._capacity / 2); @@ -87,6 +96,10 @@ export class ObjectDeque { this._size++; } + /** + * The function `pollFirst()` removes and returns the first element in a data structure. + * @returns The value of the first element in the data structure. + */ pollFirst() { if (!this._size) return; const value = this.peekFirst(); @@ -96,10 +109,18 @@ export class ObjectDeque { return value; } + /** + * The `peekFirst` function returns the first element in an array-like data structure if it exists. + * @returns The element at the first position of the `_nodes` array. + */ peekFirst() { if (this._size) return this._nodes[this._first]; } + /** + * The `pollLast()` function removes and returns the last element in a data structure. + * @returns The value that was removed from the data structure. + */ pollLast() { if (!this._size) return; const value = this.peekLast(); @@ -110,14 +131,29 @@ export class ObjectDeque { return value; } + /** + * The `peekLast()` function returns the last element in an array-like data structure. + * @returns The last element in the array "_nodes" is being returned. + */ peekLast() { if (this._size) return this._nodes[this._last]; } + /** + * The get function returns the element at the specified index in an array-like data structure. + * @param {number} index - The index parameter is a number that represents the position of the element you want to + * retrieve from the array. + * @returns The element at the specified index in the `_nodes` array is being returned. If there is no element at that + * index, `null` is returned. + */ get(index: number) { return this._nodes[this._first + index] || null; } + /** + * The function checks if the size of a data structure is less than or equal to zero. + * @returns The method is returning a boolean value indicating whether the size of the object is less than or equal to 0. + */ isEmpty() { return this._size <= 0; } @@ -140,6 +176,10 @@ export class ArrayDeque { return this._nodes.length; } + /** + * O(n) time complexity of adding at the beginning and the end + */ + /** * The function "addLast" adds a value to the end of an array. * @param {T} value - The value parameter represents the value that you want to add to the end of the array. @@ -166,6 +206,10 @@ export class ArrayDeque { return this._nodes.shift() ?? null; } + /** + * O(n) time complexity of adding at the beginning and the end + */ + /** * The function "addFirst" adds a value to the beginning of an array. * @param {T} value - The value parameter represents the value that you want to add to the beginning of the array. @@ -193,6 +237,10 @@ export class ArrayDeque { return this._nodes[this._nodes.length - 1] ?? null; } + /** + * O(1) time complexity of obtaining the value + */ + /** * The get function returns the element at the specified index in an array, or null if the index is out of bounds. * @param {number} index - The index parameter is a number that represents the position of the element you want to diff --git a/src/data-structures/queue/queue.ts b/src/data-structures/queue/queue.ts index de77937..d77e24d 100644 --- a/src/data-structures/queue/queue.ts +++ b/src/data-structures/queue/queue.ts @@ -3,7 +3,45 @@ * @copyright Tyler Zeng * @class */ -export class Queue { +import {SinglyLinkedList} from '../linked-list'; + +export class Queue extends SinglyLinkedList { + + /** + * The enqueue function adds a value to the end of an array. + * @param {T} value - The value parameter represents the value that you want to add to the queue. + */ + enqueue(value: T) { + this.push(value); + } + + /** + * The `dequeue` function removes and returns the first element from a queue, or returns null if the queue is empty. + * @returns The method is returning the element at the front of the queue, or null if the queue is empty. + */ + dequeue(): T | undefined { + return this.shift(); + } + + /** + * The `peek` function returns the value of the head node in a linked list, or `undefined` if the list is empty. + * @returns The `peek()` method is returning the value of the `head` node if it exists, otherwise it returns `undefined`. + */ + peek(): T | undefined { + return this.head?.val; + } + + * [Symbol.iterator]() { + let current = this.head; + + while (current) { + yield current.val; + current = current.next; + } + } +} + +export class ArrayQueue { protected _nodes: T[]; protected _offset: number; @@ -18,6 +56,14 @@ export class Queue { this._offset = 0; } + /** + * The size function returns the number of elements in an array. + * @returns {number} The size of the array, which is the difference between the length of the array and the offset. + */ + get size(): number { + return this._nodes.length - this._offset; + } + /** * The function "fromArray" creates a new Queue object from an array of elements.Creates a queue from an existing array. * @public @@ -26,27 +72,27 @@ export class Queue { * @returns The method is returning a new instance of the Queue class, initialized with the elements from the input * array. */ - static fromArray(elements: T[]): Queue { - return new Queue(elements); + static fromArray(elements: T[]): ArrayQueue { + return new ArrayQueue(elements); } /** - * The add function adds an element to the end of the queue and returns the updated queue.Adds an element at the back of the queue. + * The push function adds an element to the end of the queue and returns the updated queue.Adds an element at the back of the queue. * @param {T} element - The `element` parameter represents the element that you want to add to the queue. * @returns The `add` method is returning a `Queue` object. */ - add(element: T): Queue { + push(element: T): ArrayQueue { this._nodes.push(element); return this; } /** - * The `poll` function removes and returns the first element in the queue, and adjusts the internal data structure if + * The `shift` function removes and returns the first element in the queue, and adjusts the internal data structure if * necessary to optimize performance. - * @returns The function `poll()` returns either the first element in the queue or `null` if the queue is empty. + * @returns The function `shift()` returns either the first element in the queue or `null` if the queue is empty. */ - poll(): T | null { - if (this.size() === 0) return null; + shift(): T | null { + if (this.size === 0) return null; const first = this.peek(); this._offset += 1; @@ -66,7 +112,7 @@ export class Queue { * the `_offset` index. If the data structure is empty (size is 0), it returns `null`. */ peek(): T | null { - return this.size() > 0 ? this._nodes[this._offset] : null; + return this.size > 0 ? this._nodes[this._offset] : null; } /** @@ -75,15 +121,23 @@ export class Queue { * array is empty, it returns `null`. */ peekLast(): T | null { - return this.size() > 0 ? this._nodes[this._nodes.length - 1] : null; + return this.size > 0 ? this._nodes[this._nodes.length - 1] : null; } /** - * The size function returns the number of elements in an array. - * @returns {number} The size of the array, which is the difference between the length of the array and the offset. + * The enqueue function adds a value to the end of a queue. + * @param {T} value - The value parameter represents the value that you want to add to the queue. */ - size(): number { - return this._nodes.length - this._offset; + enqueue(value: T) { + this.push(value); + } + + /** + * The `dequeue` function removes and returns the first element from a queue, or returns null if the queue is empty. + * @returns The method is returning a value of type T or null. + */ + dequeue(): T | null { + return this.shift(); } /** @@ -91,7 +145,7 @@ export class Queue { * @returns {boolean} A boolean value indicating whether the size of the object is 0 or not. */ isEmpty(): boolean { - return this.size() === 0; + return this.size === 0; } /** @@ -114,7 +168,7 @@ export class Queue { * The `clone()` function returns a new Queue object with the same elements as the original Queue. * @returns The `clone()` method is returning a new instance of the `Queue` class. */ - clone(): Queue { - return new Queue(this._nodes.slice(this._offset)); + clone(): ArrayQueue { + return new ArrayQueue(this._nodes.slice(this._offset)); } } diff --git a/src/interfaces/avl-tree.ts b/src/interfaces/avl-tree.ts index d01cd05..159f953 100644 --- a/src/interfaces/avl-tree.ts +++ b/src/interfaces/avl-tree.ts @@ -12,17 +12,17 @@ export interface IAVLTree> extends IBST { remove(id: BinaryTreeNodeId, isUpdateAllLeftSum?: boolean): BinaryTreeDeletedResult[] - balanceFactor(node: N): number - - updateHeight(node: N): void - - balancePath(node: N): void - - balanceLL(A: N): void - - balanceLR(A: N): void - - balanceRR(A: N): void - - balanceRL(A: N): void + // _balanceFactor(node: N): number + // + // _updateHeight(node: N): void + // + // _balancePath(node: N): void + // + // _balanceLL(A: N): void + // + // _balanceLR(A: N): void + // + // _balanceRR(A: N): void + // + // _balanceRL(A: N): void } diff --git a/tests/unit/data-structures/linked-list/singly-linked-list.test.ts b/tests/unit/data-structures/linked-list/singly-linked-list.test.ts index 86cfee5..8716d40 100644 --- a/tests/unit/data-structures/linked-list/singly-linked-list.test.ts +++ b/tests/unit/data-structures/linked-list/singly-linked-list.test.ts @@ -1,401 +1,401 @@ -import {SinglyLinkedList} from '../../../../src'; -import {magnitude} from '../constants'; - -describe('SinglyLinkedList Operation Test', () => { - let list: SinglyLinkedList; - let objectList: SinglyLinkedList<{ keyA: number }>; - beforeEach(() => { - list = new SinglyLinkedList(); - objectList = new SinglyLinkedList<{ keyA: number }>(); - }); - - describe('push', () => { - it('should add elements to the end of the list', () => { - list.push(1); - list.push(2); - expect(list.toArray()).toEqual([1, 2]); - }); - }); - - describe('pop', () => { - it('should remove and return the last element of the list', () => { - list.push(1); - list.push(2); - const popped = list.pop(); - expect(popped).toBe(2); - expect(list.toArray()).toEqual([1]); - }); - - it('should return undefined if the list is empty', () => { - const popped = list.pop(); - expect(popped).toBeNull(); - }); - }); - - describe('shift', () => { - it('should remove and return the first element of the list', () => { - list.push(1); - list.push(2); - const shifted = list.shift(); - expect(shifted).toBe(1); - expect(list.toArray()).toEqual([2]); - }); - - it('should return undefined if the list is empty', () => { - const shifted = list.shift(); - expect(shifted).toBeNull(); - }); - }); - - describe('unshift', () => { - it('should add elements to the beginning of the list', () => { - list.unshift(1); - list.unshift(2); - expect(list.toArray()).toEqual([2, 1]); - }); - }); - - describe('get', () => { - it('should return the element at the specified index', () => { - list.push(1); - list.push(2); - list.push(3); - const element = list.getAt(1); - expect(element).toBe(2); - }); - - it('should return undefined for an out-of-bounds index', () => { - list.push(1); - const element = list.getAt(1); - expect(element).toBeNull(); - }); - }); - - - describe('insertAfter', () => { - it('should insert an element after an existing value', () => { - list.push(1); - list.push(2); - list.push(3); - list.insertAfter(2, 4); - expect(list.toArray()).toEqual([1, 2, 4, 3]); - }); - - it('should return false if the existing value is not found', () => { - list.push(1); - list.push(2); - list.push(3); - const result = list.insertAfter(5, 4); - expect(result).toBe(false); - expect(list.toArray()).toEqual([1, 2, 3]); - }); - }); - - describe('countOccurrences', () => { - it('should count occurrences of a value in the list', () => { - list.push(1); - list.push(2); - list.push(2); - list.push(3); - const count = list.countOccurrences(2); - expect(count).toBe(2); - }); - - it('should return 0 if the value is not found', () => { - list.push(1); - list.push(2); - const count = list.countOccurrences(3); - expect(count).toBe(0); - }); - }); - - describe('removeValue', () => { - it('should remove the first occurrence of a value from the list', () => { - list.push(1); - list.push(2); - list.push(3); - const removed = list.delete(2); - expect(removed).toBe(true); - expect(list.toArray()).toEqual([1, 3]); - }); - - it('should return false if the value is not found', () => { - list.push(1); - list.push(2); - list.push(3); - const removed = list.delete(4); - expect(removed).toBe(false); - expect(list.toArray()).toEqual([1, 2, 3]); - }); - }); - - - describe('isEmpty', () => { - it('should return true for an empty list', () => { - expect(list.isEmpty()).toBe(true); - }); - - it('should return false for a non-empty list', () => { - list.push(1); - expect(list.isEmpty()).toBe(false); - }); - }); - - describe('clear', () => { - it('should clear all elements from the list', () => { - list.push(1); - list.push(2); - list.push(3); - list.clear(); - expect(list.toArray()).toEqual([]); - expect(list.length).toBe(0); - expect(list.isEmpty()).toBe(true); - }); - }); - - describe('reverse', () => { - it('should reverse the order of elements in the list', () => { - list.push(1); - list.push(2); - list.push(3); - list.reverse(); - expect(list.toArray()).toEqual([3, 2, 1]); - }); - - it('should handle an empty list', () => { - list.reverse(); - expect(list.toArray()).toEqual([]); - }); - - it('should handle a list with a single element', () => { - list.push(1); - list.reverse(); - expect(list.toArray()).toEqual([1]); - }); - }); - - describe('indexOf', () => { - it('should return the index of the first occurrence of a value', () => { - list.push(1); - list.push(2); - list.push(3); - const index = list.indexOf(2); - expect(index).toBe(1); - }); - - it('should return -1 if the value is not found', () => { - list.push(1); - list.push(2); - list.push(3); - const index = list.indexOf(4); - expect(index).toBe(-1); - }); - }); - - describe('toArray', () => { - it('should convert the list to an array', () => { - list.push(1); - list.push(2); - list.push(3); - const array = list.toArray(); - expect(array).toEqual([1, 2, 3]); - }); - - it('should return an empty array for an empty list', () => { - const array = list.toArray(); - expect(array).toEqual([]); - }); - }); - - describe('insertBefore', () => { - it('should insert an element before an existing value', () => { - list.push(1); - list.push(2); - list.push(3); - list.insertBefore(2, 4); - expect(list.toArray()).toEqual([1, 4, 2, 3]); - }); - - it('should insert an element at the beginning', () => { - list.push(1); - list.push(2); - list.insertBefore(1, 3); - expect(list.toArray()).toEqual([3, 1, 2]); - }); - - it('should return false if the existing value is not found', () => { - list.push(1); - list.push(2); - list.push(3); - const result = list.insertBefore(5, 4); - expect(result).toBe(false); - expect(list.toArray()).toEqual([1, 2, 3]); - }); - }); - - describe('getLength', () => { - it('should return the correct length of the list', () => { - expect(list.length).toBe(0); - list.push(1); - list.push(2); - expect(list.length).toBe(2); - }); - }); - - describe('remove', () => { - it('should remove and return the element at the specified index', () => { - list.push(1); - list.push(2); - list.push(3); - const removed = list.deleteAt(1); - expect(removed).toBe(2); - expect(list.toArray()).toEqual([1, 3]); - }); - - it('should return undefined for an out-of-bounds index', () => { - list.push(1); - const removed = list.deleteAt(1); - expect(removed).toBeNull(); - }); - - it('should remove and return the first element', () => { - list.push(1); - list.push(2); - const removed = list.deleteAt(0); - expect(removed).toBe(1); - expect(list.toArray()).toEqual([2]); - }); - - it('should remove and return the last element', () => { - list.push(1); - list.push(2); - const removed = list.deleteAt(1); - expect(removed).toBe(2); - expect(list.toArray()).toEqual([1]); - }); - }); - - describe('push and pop', () => { - it('should push and pop elements correctly', () => { - list.push(1); - list.push(2); - expect(list.pop()).toBe(2); - expect(list.pop()).toBe(1); - expect(list.pop()).toBeNull(); - }); - }); - - describe('shift and unshift', () => { - it('should shift and unshift elements correctly', () => { - list.unshift(1); - list.unshift(2); - expect(list.shift()).toBe(2); - expect(list.shift()).toBe(1); - expect(list.shift()).toBeNull(); - }); - }); - - describe('insert and toArray', () => { - it('should insert elements and return array correctly', () => { - list.insertAt(0, 1); - list.insertAt(1, 3); - list.insertAt(1, 2); - expect(list.toArray()).toEqual([1, 2, 3]); - }); - }); - - describe('find', () => { - it('should find elements using a callback function', () => { - list.push(1); - list.push(2); - list.push(3); - const result = list.find((data) => data % 2 === 0); - expect(result).toBe(2); - }); - - it('should return undefined if element is not found', () => { - list.push(1); - list.push(3); - const result = list.find((data) => data % 2 === 0); - expect(result).toBeNull(); - }); - }); - - describe('reverse', () => { - it('should reverse the order of elements', () => { - list.push(1); - list.push(2); - list.push(3); - list.reverse(); - expect(list.toArray()).toEqual([3, 2, 1]); - }); - }); - - describe('countOccurrences', () => { - it('should count occurrences of a value', () => { - list.push(1); - list.push(2); - list.push(2); - list.push(3); - const count = list.countOccurrences(2); - expect(count).toBe(2); - }); - - it('should return 0 if value is not found', () => { - list.push(1); - list.push(2); - const count = list.countOccurrences(3); - expect(count).toBe(0); - }); - }); - - it('should insert and manipulate objects with numeric properties', () => { - const obj1 = {keyA: 1}; - const obj2 = {keyA: 2}; - const obj3 = {keyA: 3}; - - objectList.push(obj1); - objectList.push(obj2); - objectList.push(obj3); - - expect(objectList.toArray()).toEqual([obj1, obj2, obj3]); - - const newObj = {keyA: 2.5}; // Corrected newObj value - const insertSuccess = objectList.insertBefore(obj2, newObj); - expect(insertSuccess).toBe(true); - - const findNode = objectList.findNode(newObj); // Use newObj instead of obj2 - expect(findNode?.val).toEqual(newObj); - - const deleted = objectList.delete(newObj); // Use newObj instead of obj2 - expect(deleted).toBe(true); - - const poppedObj = objectList.pop(); - expect(poppedObj).toBe(obj3); - - const shiftedObj = objectList.shift(); - expect(shiftedObj).toBe(obj1); - }); - -}); - -describe('SinglyLinkedList Performance Test', () => { - describe('should the push and pop methods adhere to a time complexity of O(n) and executed correctly under large scale data', () => { - const list = new SinglyLinkedList(); - - const startPushTime = performance.now(); - for (let i = 0; i < magnitude.LINEAR; i++) { - list.push(i); - } - expect(performance.now() - startPushTime).toBeLessThan(magnitude.LINEAR * 1000); - - const startPopTime = performance.now(); - - for (let i = 0; i < magnitude.LINEAR; i++) { - list.pop(); - } - - expect(performance.now() - startPopTime).toBeLessThan(magnitude.LINEAR * 1000); - - }); -}); \ No newline at end of file +import {SinglyLinkedList} from '../../../../src'; +import {magnitude} from '../constants'; + +describe('SinglyLinkedList Operation Test', () => { + let list: SinglyLinkedList; + let objectList: SinglyLinkedList<{ keyA: number }>; + beforeEach(() => { + list = new SinglyLinkedList(); + objectList = new SinglyLinkedList<{ keyA: number }>(); + }); + + describe('push', () => { + it('should add elements to the end of the list', () => { + list.push(1); + list.push(2); + expect(list.toArray()).toEqual([1, 2]); + }); + }); + + describe('pop', () => { + it('should remove and return the last element of the list', () => { + list.push(1); + list.push(2); + const popped = list.pop(); + expect(popped).toBe(2); + expect(list.toArray()).toEqual([1]); + }); + + it('should return undefined if the list is empty', () => { + const popped = list.pop(); + expect(popped).toBeUndefined(); + }); + }); + + describe('shift', () => { + it('should remove and return the first element of the list', () => { + list.push(1); + list.push(2); + const shifted = list.shift(); + expect(shifted).toBe(1); + expect(list.toArray()).toEqual([2]); + }); + + it('should return undefined if the list is empty', () => { + const shifted = list.shift(); + expect(shifted).toBeUndefined(); + }); + }); + + describe('unshift', () => { + it('should add elements to the beginning of the list', () => { + list.unshift(1); + list.unshift(2); + expect(list.toArray()).toEqual([2, 1]); + }); + }); + + describe('get', () => { + it('should return the element at the specified index', () => { + list.push(1); + list.push(2); + list.push(3); + const element = list.getAt(1); + expect(element).toBe(2); + }); + + it('should return undefined for an out-of-bounds index', () => { + list.push(1); + const element = list.getAt(1); + expect(element).toBeNull(); + }); + }); + + + describe('insertAfter', () => { + it('should insert an element after an existing value', () => { + list.push(1); + list.push(2); + list.push(3); + list.insertAfter(2, 4); + expect(list.toArray()).toEqual([1, 2, 4, 3]); + }); + + it('should return false if the existing value is not found', () => { + list.push(1); + list.push(2); + list.push(3); + const result = list.insertAfter(5, 4); + expect(result).toBe(false); + expect(list.toArray()).toEqual([1, 2, 3]); + }); + }); + + describe('countOccurrences', () => { + it('should count occurrences of a value in the list', () => { + list.push(1); + list.push(2); + list.push(2); + list.push(3); + const count = list.countOccurrences(2); + expect(count).toBe(2); + }); + + it('should return 0 if the value is not found', () => { + list.push(1); + list.push(2); + const count = list.countOccurrences(3); + expect(count).toBe(0); + }); + }); + + describe('removeValue', () => { + it('should remove the first occurrence of a value from the list', () => { + list.push(1); + list.push(2); + list.push(3); + const removed = list.delete(2); + expect(removed).toBe(true); + expect(list.toArray()).toEqual([1, 3]); + }); + + it('should return false if the value is not found', () => { + list.push(1); + list.push(2); + list.push(3); + const removed = list.delete(4); + expect(removed).toBe(false); + expect(list.toArray()).toEqual([1, 2, 3]); + }); + }); + + + describe('isEmpty', () => { + it('should return true for an empty list', () => { + expect(list.isEmpty()).toBe(true); + }); + + it('should return false for a non-empty list', () => { + list.push(1); + expect(list.isEmpty()).toBe(false); + }); + }); + + describe('clear', () => { + it('should clear all elements from the list', () => { + list.push(1); + list.push(2); + list.push(3); + list.clear(); + expect(list.toArray()).toEqual([]); + expect(list.length).toBe(0); + expect(list.isEmpty()).toBe(true); + }); + }); + + describe('reverse', () => { + it('should reverse the order of elements in the list', () => { + list.push(1); + list.push(2); + list.push(3); + list.reverse(); + expect(list.toArray()).toEqual([3, 2, 1]); + }); + + it('should handle an empty list', () => { + list.reverse(); + expect(list.toArray()).toEqual([]); + }); + + it('should handle a list with a single element', () => { + list.push(1); + list.reverse(); + expect(list.toArray()).toEqual([1]); + }); + }); + + describe('indexOf', () => { + it('should return the index of the first occurrence of a value', () => { + list.push(1); + list.push(2); + list.push(3); + const index = list.indexOf(2); + expect(index).toBe(1); + }); + + it('should return -1 if the value is not found', () => { + list.push(1); + list.push(2); + list.push(3); + const index = list.indexOf(4); + expect(index).toBe(-1); + }); + }); + + describe('toArray', () => { + it('should convert the list to an array', () => { + list.push(1); + list.push(2); + list.push(3); + const array = list.toArray(); + expect(array).toEqual([1, 2, 3]); + }); + + it('should return an empty array for an empty list', () => { + const array = list.toArray(); + expect(array).toEqual([]); + }); + }); + + describe('insertBefore', () => { + it('should insert an element before an existing value', () => { + list.push(1); + list.push(2); + list.push(3); + list.insertBefore(2, 4); + expect(list.toArray()).toEqual([1, 4, 2, 3]); + }); + + it('should insert an element at the beginning', () => { + list.push(1); + list.push(2); + list.insertBefore(1, 3); + expect(list.toArray()).toEqual([3, 1, 2]); + }); + + it('should return false if the existing value is not found', () => { + list.push(1); + list.push(2); + list.push(3); + const result = list.insertBefore(5, 4); + expect(result).toBe(false); + expect(list.toArray()).toEqual([1, 2, 3]); + }); + }); + + describe('getLength', () => { + it('should return the correct length of the list', () => { + expect(list.length).toBe(0); + list.push(1); + list.push(2); + expect(list.length).toBe(2); + }); + }); + + describe('remove', () => { + it('should remove and return the element at the specified index', () => { + list.push(1); + list.push(2); + list.push(3); + const removed = list.deleteAt(1); + expect(removed).toBe(2); + expect(list.toArray()).toEqual([1, 3]); + }); + + it('should return undefined for an out-of-bounds index', () => { + list.push(1); + const removed = list.deleteAt(1); + expect(removed).toBeUndefined(); + }); + + it('should remove and return the first element', () => { + list.push(1); + list.push(2); + const removed = list.deleteAt(0); + expect(removed).toBe(1); + expect(list.toArray()).toEqual([2]); + }); + + it('should remove and return the last element', () => { + list.push(1); + list.push(2); + const removed = list.deleteAt(1); + expect(removed).toBe(2); + expect(list.toArray()).toEqual([1]); + }); + }); + + describe('push and pop', () => { + it('should push and pop elements correctly', () => { + list.push(1); + list.push(2); + expect(list.pop()).toBe(2); + expect(list.pop()).toBe(1); + expect(list.pop()).toBeUndefined(); + }); + }); + + describe('shift and unshift', () => { + it('should shift and unshift elements correctly', () => { + list.unshift(1); + list.unshift(2); + expect(list.shift()).toBe(2); + expect(list.shift()).toBe(1); + expect(list.shift()).toBeUndefined(); + }); + }); + + describe('insert and toArray', () => { + it('should insert elements and return array correctly', () => { + list.insertAt(0, 1); + list.insertAt(1, 3); + list.insertAt(1, 2); + expect(list.toArray()).toEqual([1, 2, 3]); + }); + }); + + describe('find', () => { + it('should find elements using a callback function', () => { + list.push(1); + list.push(2); + list.push(3); + const result = list.find((data) => data % 2 === 0); + expect(result).toBe(2); + }); + + it('should return undefined if element is not found', () => { + list.push(1); + list.push(3); + const result = list.find((data) => data % 2 === 0); + expect(result).toBeNull(); + }); + }); + + describe('reverse', () => { + it('should reverse the order of elements', () => { + list.push(1); + list.push(2); + list.push(3); + list.reverse(); + expect(list.toArray()).toEqual([3, 2, 1]); + }); + }); + + describe('countOccurrences', () => { + it('should count occurrences of a value', () => { + list.push(1); + list.push(2); + list.push(2); + list.push(3); + const count = list.countOccurrences(2); + expect(count).toBe(2); + }); + + it('should return 0 if value is not found', () => { + list.push(1); + list.push(2); + const count = list.countOccurrences(3); + expect(count).toBe(0); + }); + }); + + it('should insert and manipulate objects with numeric properties', () => { + const obj1 = {keyA: 1}; + const obj2 = {keyA: 2}; + const obj3 = {keyA: 3}; + + objectList.push(obj1); + objectList.push(obj2); + objectList.push(obj3); + + expect(objectList.toArray()).toEqual([obj1, obj2, obj3]); + + const newObj = {keyA: 2.5}; // Corrected newObj value + const insertSuccess = objectList.insertBefore(obj2, newObj); + expect(insertSuccess).toBe(true); + + const findNode = objectList.findNode(newObj); // Use newObj instead of obj2 + expect(findNode?.val).toEqual(newObj); + + const deleted = objectList.delete(newObj); // Use newObj instead of obj2 + expect(deleted).toBe(true); + + const poppedObj = objectList.pop(); + expect(poppedObj).toBe(obj3); + + const shiftedObj = objectList.shift(); + expect(shiftedObj).toBe(obj1); + }); + +}); + +describe('SinglyLinkedList Performance Test', () => { + describe('should the push and pop methods adhere to a time complexity of O(n) and executed correctly under large scale data', () => { + const list = new SinglyLinkedList(); + + const startPushTime = performance.now(); + for (let i = 0; i < magnitude.LINEAR; i++) { + list.push(i); + } + expect(performance.now() - startPushTime).toBeLessThan(magnitude.LINEAR * 1000); + + const startPopTime = performance.now(); + + for (let i = 0; i < magnitude.LINEAR; i++) { + list.pop(); + } + + expect(performance.now() - startPopTime).toBeLessThan(magnitude.LINEAR * 1000); + + }); +});