diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b7ea9..79e99aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - [Semantic Versioning](https://semver.org/spec/v2.0.0.html) - [`auto-changelog`](https://github.com/CookPete/auto-changelog) -## [v1.49.5](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming) +## [v1.49.6](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming) ### Changes diff --git a/README.md b/README.md index 5d08812..a03f376 100644 --- a/README.md +++ b/README.md @@ -984,43 +984,43 @@ avl2.print(); [//]: # (No deletion!!! Start of Replace Section)
avl-tree
-
test nametime taken (ms)executions per secsample deviation
10,000 add randomly122.818.140.00
10,000 add & delete randomly184.275.430.00
10,000 addMany132.277.560.00
10,000 get51.0419.597.82e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 add randomly126.827.880.01
10,000 add & delete randomly186.365.370.00
10,000 addMany133.157.510.00
10,000 get50.6519.745.59e-4
binary-tree-overall
-
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add5.81172.098.66e-5
10,000 RBTree add & delete randomly15.0366.542.26e-4
10,000 RBTree get18.7653.304.08e-4
10,000 AVLTree add125.397.980.00
10,000 AVLTree add & delete randomly189.095.290.00
10,000 AVLTree get0.921087.752.99e-5
+
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add6.12163.481.94e-4
10,000 RBTree add & delete randomly15.4664.703.64e-4
10,000 RBTree get19.9850.060.00
10,000 AVLTree add130.407.670.02
10,000 AVLTree add & delete randomly193.645.160.01
10,000 AVLTree get0.991005.443.95e-5
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add81.2312.310.00
100,000 add & delete randomly205.004.880.00
100,000 getNode178.145.618.78e-4
100,000 add & iterator110.779.030.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add90.7211.020.03
100,000 add & delete randomly228.774.370.02
100,000 getNode192.255.205.16e-4
100,000 add & iterator112.498.890.01
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109863.411.37e-6
1,000 addEdge6.29159.071.77e-4
1,000 getVertex0.052.15e+44.86e-7
1,000 getEdge23.4642.630.00
tarjan216.874.610.01
tarjan all6549.750.150.03
topologicalSort182.675.470.00
+
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.119250.941.22e-5
1,000 addEdge6.35157.512.88e-4
1,000 getVertex0.052.06e+48.69e-6
1,000 getEdge23.0243.430.00
tarjan213.854.680.01
tarjan all6674.110.150.28
topologicalSort179.095.580.00
hash-map
-
test nametime taken (ms)executions per secsample deviation
1,000,000 set119.498.370.04
Native Map 1,000,000 set222.504.490.02
Native Set 1,000,000 add173.115.780.01
1,000,000 set & get118.378.450.02
Native Map 1,000,000 set & get273.633.650.01
Native Set 1,000,000 add & has175.425.700.02
1,000,000 ObjKey set & get345.122.900.05
Native Map 1,000,000 ObjKey set & get495.132.020.05
Native Set 1,000,000 ObjKey add & has276.793.610.04
+
test nametime taken (ms)executions per secsample deviation
1,000,000 set131.277.620.05
Native Map 1,000,000 set267.343.740.04
Native Set 1,000,000 add207.034.830.06
1,000,000 set & get132.197.560.03
Native Map 1,000,000 set & get276.303.620.01
Native Set 1,000,000 add & has187.745.330.02
1,000,000 ObjKey set & get336.392.970.03
Native Map 1,000,000 ObjKey set & get394.472.540.09
Native Set 1,000,000 ObjKey add & has295.483.380.04
heap
-
test nametime taken (ms)executions per secsample deviation
100,000 add & poll27.5736.270.00
100,000 add & dfs34.4429.042.58e-4
10,000 fib add & pop361.992.760.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add & poll24.1841.356.43e-4
100,000 add & dfs33.6429.720.00
10,000 fib add & pop363.382.750.00
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push214.624.660.03
1,000,000 unshift223.534.470.03
1,000,000 unshift & shift173.705.760.03
1,000,000 addBefore341.892.920.09
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push220.164.540.03
1,000,000 unshift210.844.740.05
1,000,000 unshift & shift189.595.270.07
1,000,000 addBefore412.742.420.17
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift195.745.110.04
10,000 push & pop239.884.170.01
10,000 addBefore257.453.880.01
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift252.063.970.09
10,000 push & pop230.294.340.01
10,000 addBefore261.573.820.01
priority-queue
-
test nametime taken (ms)executions per secsample deviation
100,000 add & poll79.6212.565.34e-4
+
test nametime taken (ms)executions per secsample deviation
100,000 add & poll75.7113.218.95e-4
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push14.2070.431.32e-4
1,000,000 push & pop23.1343.221.71e-4
100,000 push & shift2.40416.672.68e-5
Native Array 100,000 push & shift3050.200.330.25
100,000 unshift & shift2.24446.265.35e-5
Native Array 100,000 unshift & shift5037.860.200.17
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push25.1839.710.01
1,000,000 push & pop33.5229.830.01
100,000 push & shift3.61276.965.50e-4
Native Array 100,000 push & shift2703.160.370.11
100,000 unshift & shift3.73268.148.29e-4
Native Array 100,000 unshift & shift4767.610.210.40
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push47.0921.230.01
100,000 push & shift5.06197.721.25e-4
Native Array 100,000 push & shift3038.510.330.12
Native Array 100,000 push & pop4.44225.231.51e-4
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push50.5619.780.01
100,000 push & shift5.99166.850.00
Native Array 100,000 push & shift2962.430.340.29
Native Array 100,000 push & pop4.49222.693.01e-4
stack
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push45.1622.140.01
1,000,000 push & pop51.3519.470.01
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push59.6816.760.03
1,000,000 push & pop52.0419.220.01
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push44.2022.635.61e-4
100,000 getWords91.8110.890.00
+
test nametime taken (ms)executions per secsample deviation
100,000 push47.7020.960.00
100,000 getWords66.5315.030.00
[//]: # (No deletion!!! End of Replace Section) diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 487c5e2..b401cf3 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -48,17 +48,17 @@ export class AVLTree< extends BST implements IBinaryTree { /** - * The constructor function initializes an AVLTree object with optional nodes and options. - * @param [nodes] - The `nodes` parameter is an optional iterable of `KeyOrNodeOrEntry` + * The constructor function initializes an AVLTree object with optional keysOrNodesOrEntries and options. + * @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry` * objects. It represents a collection of nodes that will be added to the AVL tree during * initialization. * @param [options] - The `options` parameter is an optional object that allows you to customize the * behavior of the AVL tree. It is of type `Partial`, which means that you can * provide only a subset of the properties defined in the `AVLTreeOptions` interface. */ - constructor(nodes?: Iterable>, options?: Partial>) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: AVLTreeOptions) { super([], options); - if (nodes) super.addMany(nodes); + if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries); } /** diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index d1d720a..501f8c3 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -111,29 +111,25 @@ export class BinaryTree< iterationType = IterationType.ITERATIVE; /** - * The constructor function initializes a binary tree object with optional nodes and options. - * @param [nodes] - An optional iterable of KeyOrNodeOrEntry objects. These objects represent the + * The constructor function initializes a binary tree object with optional keysOrNodesOrEntries and options. + * @param [keysOrNodesOrEntries] - An optional iterable of KeyOrNodeOrEntry objects. These objects represent the * nodes to be added to the binary tree. * @param [options] - The `options` parameter is an optional object that can contain additional * configuration options for the binary tree. In this case, it is of type * `Partial`, which means that not all properties of `BinaryTreeOptions` are * required. */ - constructor(nodes?: Iterable>, options?: Partial>) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: BinaryTreeOptions) { super(); if (options) { const { iterationType, extractor } = options; - if (iterationType) { - this.iterationType = iterationType; - } - if (extractor) { - this._extractor = extractor; - } + if (iterationType) this.iterationType = iterationType; + if (extractor) this._extractor = extractor; } this._size = 0; - if (nodes) this.addMany(nodes); + if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); } protected _extractor = (key: K) => Number(key); diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 77a058f..6b38eaa 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -92,25 +92,23 @@ export class BST< implements IBinaryTree { /** * This is the constructor function for a binary search tree class in TypeScript, which initializes - * the tree with optional nodes and options. - * @param [nodes] - An optional iterable of KeyOrNodeOrEntry objects that will be added to the + * the tree with optional keysOrNodesOrEntries and options. + * @param [keysOrNodesOrEntries] - An optional iterable of KeyOrNodeOrEntry objects that will be added to the * binary search tree. * @param [options] - The `options` parameter is an optional object that can contain additional * configuration options for the binary search tree. It can have the following properties: */ - constructor(nodes?: Iterable>, options?: Partial>) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: BSTOptions) { super([], options); if (options) { const { variant } = options; - if (variant) { - this._variant = variant; - } + if (variant) this._variant = variant; } this._root = undefined; - if (nodes) this.addMany(nodes); + if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); } protected override _root?: N; diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 7b06226..3c233d2 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -53,7 +53,7 @@ export class RedBlackTree< /** * This is the constructor function for a Red-Black Tree data structure in TypeScript, which * initializes the tree with optional nodes and options. - * @param [nodes] - The `nodes` parameter is an optional iterable of `KeyOrNodeOrEntry` + * @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry` * objects. It represents the initial nodes that will be added to the RBTree during its * construction. If this parameter is provided, the `addMany` method is called to add all the * nodes to the @@ -61,11 +61,11 @@ export class RedBlackTree< * behavior of the RBTree. It is of type `Partial`, which means that you can provide * only a subset of the properties defined in the `RBTreeOptions` interface. */ - constructor(nodes?: Iterable>, options?: Partial>) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: RBTreeOptions) { super([], options); this._root = this.Sentinel; - if (nodes) super.addMany(nodes); + if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries); } protected _root: N; diff --git a/src/data-structures/binary-tree/tree-multimap.ts b/src/data-structures/binary-tree/tree-multimap.ts index 4a7ddba..8981cde 100644 --- a/src/data-structures/binary-tree/tree-multimap.ts +++ b/src/data-structures/binary-tree/tree-multimap.ts @@ -52,9 +52,9 @@ export class TreeMultimap< > extends AVLTree implements IBinaryTree { - constructor(nodes?: Iterable>, options?: Partial>) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: TreeMultimapOptions) { super([], options); - if (nodes) this.addMany(nodes); + if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); } private _count = 0; diff --git a/src/data-structures/hash/hash-map.ts b/src/data-structures/hash/hash-map.ts index b91b5d9..da85828 100644 --- a/src/data-structures/hash/hash-map.ts +++ b/src/data-structures/hash/hash-map.ts @@ -5,34 +5,35 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type { EntryCallback, HashMapLinkedNode, HashMapOptions, HashMapStoreItem } from '../../types'; +import type { + EntryCallback, + HashMapLinkedNode, + HashMapOptions, + HashMapStoreItem, + LinkedHashMapOptions +} from '../../types'; import { IterableEntryBase } from '../base'; import { isWeakKey, rangeCheck } from '../../utils'; /** * 1. Key-Value Pair Storage: HashMap stores key-value pairs. Each key maps to a value. - * 2. Fast Lookup: It's used when you need to quickly find, insert, or delete elements based on a key. + * 2. Fast Lookup: It's used when you need to quickly find, insert, or delete entries based on a key. * 3. Unique Keys: Keys are unique. If you try to insert another entry with the same key, the old entry will be replaced by the new one. - * 4. Unordered Collection: HashMap does not guarantee the order of elements, and the order may change over time. + * 4. Unordered Collection: HashMap does not guarantee the order of entries, and the order may change over time. */ export class HashMap extends IterableEntryBase { protected _store: { [key: string]: HashMapStoreItem } = {}; protected _objMap: Map = new Map(); /** - * The constructor function initializes a new instance of a class with optional elements and options. - * @param elements - The `elements` parameter is an iterable containing key-value pairs `[K, V]`. It + * The constructor function initializes a new instance of a class with optional entries and options. + * @param entries - The `entries` parameter is an iterable containing key-value pairs `[K, V]`. It * is optional and defaults to an empty array `[]`. This parameter is used to initialize the map with * key-value pairs. * @param [options] - The `options` parameter is an optional object that can contain additional * configuration options for the constructor. In this case, it has one property: */ - constructor( - elements: Iterable<[K, V]> = [], - options?: { - hashFn: (key: K) => string; - } - ) { + constructor(entries: Iterable<[K, V]> = [], options?: HashMapOptions) { super(); if (options) { const { hashFn } = options; @@ -40,8 +41,8 @@ export class HashMap extends IterableEntryBase { this._hashFn = hashFn; } } - if (elements) { - this.setMany(elements); + if (entries) { + this.setMany(entries); } } @@ -88,12 +89,12 @@ export class HashMap extends IterableEntryBase { /** * The function "setMany" sets multiple key-value pairs in a map. - * @param elements - The `elements` parameter is an iterable containing key-value pairs. Each - * key-value pair is represented as an array with two elements: the key and the value. + * @param entries - The `entries` parameter is an iterable containing key-value pairs. Each + * key-value pair is represented as an array with two entries: the key and the value. */ - setMany(elements: Iterable<[K, V]>): boolean[] { + setMany(entries: Iterable<[K, V]>): boolean[] { const results: boolean[] = []; - for (const [key, value] of elements) results.push(this.set(key, value)); + for (const [key, value] of entries) results.push(this.set(key, value)); return results; } @@ -214,10 +215,6 @@ export class HashMap extends IterableEntryBase { return filteredMap; } - print(): void { - console.log([...this.entries()]); - } - put(key: K, value: V): boolean { return this.set(key, value); } @@ -261,8 +258,8 @@ export class HashMap extends IterableEntryBase { } /** - * 1. Maintaining the Order of Element Insertion: Unlike HashMap, LinkedHashMap maintains the order in which elements are inserted. Therefore, when you traverse it, elements will be returned in the order they were inserted into the map. - * 2. Based on Hash Table and Linked List: It combines the structures of a hash table and a linked list, using the hash table to ensure fast access, while maintaining the order of elements through the linked list. + * 1. Maintaining the Order of Element Insertion: Unlike HashMap, LinkedHashMap maintains the order in which entries are inserted. Therefore, when you traverse it, entries will be returned in the order they were inserted into the map. + * 2. Based on Hash Table and Linked List: It combines the structures of a hash table and a linked list, using the hash table to ensure fast access, while maintaining the order of entries through the linked list. * 3. Time Complexity: Similar to HashMap, LinkedHashMap offers constant-time performance for get and put operations in most cases. */ export class LinkedHashMap extends IterableEntryBase { @@ -271,25 +268,20 @@ export class LinkedHashMap extends IterableEntryBase { protected _head: HashMapLinkedNode; protected _tail: HashMapLinkedNode; protected readonly _sentinel: HashMapLinkedNode; - protected _hashFn: (key: K) => string; - protected _objHashFn: (key: K) => object; - constructor( - elements?: Iterable<[K, V]>, - options: HashMapOptions = { - hashFn: (key: K) => String(key), - objHashFn: (key: K) => key - } - ) { + constructor(entries?: Iterable<[K, V]>, options?: LinkedHashMapOptions) { super(); this._sentinel = >{}; this._sentinel.prev = this._sentinel.next = this._head = this._tail = this._sentinel; - const { hashFn, objHashFn } = options; - this._hashFn = hashFn; - this._objHashFn = objHashFn; - if (elements) { - for (const el of elements) { + if (options) { + const { hashFn, objHashFn } = options; + if (hashFn) this._hashFn = hashFn; + if (objHashFn) this._objHashFn = objHashFn; + } + + if (entries) { + for (const el of entries) { this.set(el[0], el[1]); } } @@ -547,7 +539,7 @@ export class LinkedHashMap extends IterableEntryBase { * Time Complexity: O(1) * Space Complexity: O(1) * - * The `clear` function clears all the elements in a data structure and resets its properties. + * The `clear` function clears all the entries in a data structure and resets its properties. */ clear(): void { this._noObjMap = {}; @@ -564,11 +556,6 @@ export class LinkedHashMap extends IterableEntryBase { return cloned; } - /** - * Time Complexity: O(n) - * Space Complexity: O(n) - */ - /** * Time Complexity: O(n) * Space Complexity: O(n) @@ -596,11 +583,6 @@ export class LinkedHashMap extends IterableEntryBase { return filteredMap; } - /** - * Time Complexity: O(n) - * Space Complexity: O(n) - */ - /** * Time Complexity: O(n) * Space Complexity: O(n) @@ -629,12 +611,26 @@ export class LinkedHashMap extends IterableEntryBase { return mappedMap; } + /** + * Time Complexity: O(n) + * Space Complexity: O(n) + */ + put(key: K, value: V): boolean { return this.set(key, value); } /** - * Time Complexity: O(n), where n is the number of elements in the LinkedHashMap. + * Time Complexity: O(n) + * Space Complexity: O(n) + */ + + protected _hashFn: (key: K) => string = (key: K) => String(key); + + protected _objHashFn: (key: K) => object = (key: K) => key; + + /** + * Time Complexity: O(n), where n is the number of entries in the LinkedHashMap. * Space Complexity: O(1) * * The above function is an iterator that yields key-value pairs from a linked list. diff --git a/src/data-structures/hash/hash-table.ts b/src/data-structures/hash/hash-table.ts deleted file mode 100644 index 58bf6eb..0000000 --- a/src/data-structures/hash/hash-table.ts +++ /dev/null @@ -1,318 +0,0 @@ -/** - * data-structure-typed - * - * @author Tyler Zeng - * @copyright Copyright (c) 2022 Tyler Zeng - * @license MIT License - */ - -import type { HashFunction } from '../../types'; - -export class HashTableNode { - key: K; - value: V; - next: HashTableNode | undefined; - - constructor(key: K, value: V) { - this.key = key; - this.value = value; - this.next = undefined; - } -} - -export class HashTable { - protected static readonly DEFAULT_CAPACITY = 16; - protected static readonly LOAD_FACTOR = 0.75; - - constructor(capacity: number = HashTable.DEFAULT_CAPACITY, hashFn?: HashFunction) { - this._hashFn = hashFn || this._defaultHashFn; - this._capacity = Math.max(capacity, HashTable.DEFAULT_CAPACITY); - this._size = 0; - this._buckets = new Array | undefined>(this._capacity).fill(undefined); - } - - protected _capacity: number; - - get capacity(): number { - return this._capacity; - } - - protected _size: number; - - get size(): number { - return this._size; - } - - protected _buckets: Array | undefined>; - - get buckets(): Array | undefined> { - return this._buckets; - } - - protected _hashFn: HashFunction; - - get hashFn(): HashFunction { - return this._hashFn; - } - - /** - * The set function adds a key-value pair to the hash table, handling collisions and resizing if necessary. - * @param {K} key - The key parameter represents the key of the key-value pair that you want to insert into the hash - * table. It is of type K, which is a generic type representing the key's data type. - * @param {V} value - The parameter `value` represents the value that you want to associate with the given key in the hash - * table. - * @returns Nothing is being returned. The return type of the `put` method is `void`, which means it does not return any - * value. - */ - set(key: K, value: V): void { - const index = this._hash(key); - const newNode = new HashTableNode(key, value); - - if (!this._buckets[index]) { - this._buckets[index] = newNode; - } else { - // Handle collisions, consider using open addressing, etc. - let currentNode = this._buckets[index]!; - while (currentNode) { - if (currentNode.key === key) { - // If the key already exists, update the value - currentNode.value = value; - return; - } - if (!currentNode.next) { - break; - } - currentNode = currentNode.next; - } - // Add to the end of the linked list - currentNode.next = newNode; - } - this._size++; - - // If the load factor is too high, resize the hash table - if (this._size / this._capacity >= HashTable.LOAD_FACTOR) { - this._expand(); - } - } - - /** - * The `get` function retrieves the value associated with a given key from a hash table. - * @param {K} key - The `key` parameter represents the key of the element that we want to retrieve from the data - * structure. - * @returns The method is returning the value associated with the given key if it exists in the hash table. If the key is - * not found, it returns `undefined`. - */ - get(key: K): V | undefined { - const index = this._hash(key); - let currentNode = this._buckets[index]; - - while (currentNode) { - if (currentNode.key === key) { - return currentNode.value; - } - currentNode = currentNode.next; - } - return undefined; // Key not found - } - - /** - * The delete function removes a key-value pair from a hash table. - * @param {K} key - The `key` parameter represents the key of the key-value pair that needs to be removed from the hash - * table. - * @returns Nothing is being returned. The `delete` method has a return type of `void`, which means it does not return - * any value. - */ - delete(key: K): void { - const index = this._hash(key); - let currentNode = this._buckets[index]; - let prevNode: HashTableNode | undefined = undefined; - - while (currentNode) { - if (currentNode.key === key) { - if (prevNode) { - prevNode.next = currentNode.next; - } else { - this._buckets[index] = currentNode.next; - } - this._size--; - currentNode.next = undefined; // Release memory - return; - } - prevNode = currentNode; - currentNode = currentNode.next; - } - } - - * [Symbol.iterator](): Generator<[K, V], void, undefined> { - for (const bucket of this._buckets) { - let currentNode = bucket; - while (currentNode) { - yield [currentNode.key, currentNode.value]; - currentNode = currentNode.next; - } - } - } - - forEach(callback: (entry: [K, V], index: number, table: HashTable) => void): void { - let index = 0; - for (const entry of this) { - callback(entry, index, this); - index++; - } - } - - filter(predicate: (entry: [K, V], index: number, table: HashTable) => boolean): HashTable { - const newTable = new HashTable(); - let index = 0; - for (const [key, value] of this) { - if (predicate([key, value], index, this)) { - newTable.set(key, value); - } - index++; - } - return newTable; - } - - map(callback: (entry: [K, V], index: number, table: HashTable) => T): HashTable { - const newTable = new HashTable(); - let index = 0; - for (const [key, value] of this) { - newTable.set(key, callback([key, value], index, this)); - index++; - } - return newTable; - } - - reduce(callback: (accumulator: T, entry: [K, V], index: number, table: HashTable) => T, initialValue: T): T { - let accumulator = initialValue; - let index = 0; - for (const entry of this) { - accumulator = callback(accumulator, entry, index, this); - index++; - } - return accumulator; - } - - /** - * The function `_defaultHashFn` calculates the hash value of a given key and returns the remainder when divided by the - * capacity of the data structure. - * @param {K} key - The `key` parameter is the input value that needs to be hashed. It can be of any type, but in this - * code snippet, it is checked whether the key is a string or an object. If it is a string, the `_murmurStringHashFn` - * function is used to - * @returns the hash value of the key modulo the capacity of the data structure. - */ - protected _defaultHashFn(key: K): number { - // Can be replaced with other hash functions as needed - const hashValue = typeof key === 'string' ? this._murmurStringHashFn(key) : this._objectHash(key); - return hashValue % this._capacity; - } - - /** - * The `_multiplicativeStringHashFn` function calculates a hash value for a given string key using the multiplicative - * string hash function. - * @param {K} key - The `key` parameter is the input value for which we want to calculate the hash. It can be of any - * type, as it is generic (`K`). The function converts the `key` to a string using the `String()` function. - * @returns a number, which is the result of the multiplicative string hash function applied to the input key. - */ - protected _multiplicativeStringHashFn(key: K): number { - const keyString = String(key); - let hash = 0; - for (let i = 0; i < keyString.length; i++) { - const charCode = keyString.charCodeAt(i); - // Some constants for adjusting the hash function - const A = 0.618033988749895; - const M = 1 << 30; // 2^30 - hash = (hash * A + charCode) % M; - } - return Math.abs(hash); // Take absolute value to ensure non-negative numbers - } - - /** - * The function `_murmurStringHashFn` calculates a hash value for a given string key using the MurmurHash algorithm. - * @param {K} key - The `key` parameter is the input value for which you want to calculate the hash. It can be of any - * type, but it will be converted to a string using the `String()` function before calculating the hash. - * @returns a number, which is the hash value calculated for the given key. - */ - protected _murmurStringHashFn(key: K): number { - const keyString = String(key); - const seed = 0; - let hash = seed; - - for (let i = 0; i < keyString.length; i++) { - const char = keyString.charCodeAt(i); - hash = (hash ^ char) * 0x5bd1e995; - hash = (hash ^ (hash >>> 15)) * 0x27d4eb2d; - hash = hash ^ (hash >>> 15); - } - - return Math.abs(hash); - } - - /** - * The _hash function takes a key and returns a number. - * @param {K} key - The parameter "key" is of type K, which represents the type of the key that will be hashed. - * @returns The hash function is returning a number. - */ - protected _hash(key: K): number { - return this.hashFn(key); - } - - /** - * The function calculates a hash value for a given string using the djb2 algorithm. - * @param {string} key - The `key` parameter in the `stringHash` function is a string value that represents the input for - * which we want to calculate the hash value. - * @returns a number, which is the hash value of the input string. - */ - protected _stringHash(key: string): number { - let hash = 0; - for (let i = 0; i < key.length; i++) { - hash = (hash * 31 + key.charCodeAt(i)) & 0xffffffff; - } - return hash; - } - - /** - * The function `_objectHash` takes a key and returns a hash value, using a custom hash function for objects. - * @param {K} key - The parameter "key" is of type "K", which means it can be any type. It could be a string, number, - * boolean, object, or any other type of value. The purpose of the objectHash function is to generate a hash value for - * the key, which can be used for - * @returns a number, which is the hash value of the key. - */ - protected _objectHash(key: K): number { - // If the key is an object, you can write a custom hash function - // For example, convert the object's properties to a string and use string hashing - // This is just an example; you should write a specific object hash function as needed - return this._stringHash(JSON.stringify(key)); - } - - /** - * The `expand` function increases the capacity of a hash table by creating a new array of buckets with double the - * capacity and rehashing all the existing key-value pairs into the new buckets. - */ - protected _expand(): void { - const newCapacity = this._capacity * 2; - const newBuckets = new Array | undefined>(newCapacity).fill(undefined); - - for (const bucket of this._buckets) { - let currentNode = bucket; - while (currentNode) { - const newIndex = this._hash(currentNode.key); - const newNode = new HashTableNode(currentNode.key, currentNode.value); - - if (!newBuckets[newIndex]) { - newBuckets[newIndex] = newNode; - } else { - let currentNewNode = newBuckets[newIndex]!; - while (currentNewNode.next) { - currentNewNode = currentNewNode.next; - } - currentNewNode.next = newNode; - } - currentNode = currentNode.next; - } - } - - this._buckets = newBuckets; - this._capacity = newCapacity; - } -} diff --git a/src/data-structures/hash/index.ts b/src/data-structures/hash/index.ts index 5d7849d..e4fd2c4 100644 --- a/src/data-structures/hash/index.ts +++ b/src/data-structures/hash/index.ts @@ -1,2 +1 @@ -export * from './hash-table'; export * from './hash-map'; diff --git a/src/data-structures/heap/heap.ts b/src/data-structures/heap/heap.ts index 5914ef0..8e2d9fe 100644 --- a/src/data-structures/heap/heap.ts +++ b/src/data-structures/heap/heap.ts @@ -21,23 +21,12 @@ import { IterableElementBase } from '../base'; * 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prim's minimum spanning tree algorithm, which use heaps to improve performance. */ export class Heap extends IterableElementBase { - options: HeapOptions; - - constructor(elements?: Iterable, options?: HeapOptions) { + constructor(elements: Iterable = [], options?: HeapOptions) { super(); - const defaultComparator = (a: E, b: E) => { - if (!(typeof a === 'number' && typeof b === 'number')) { - throw new Error('The a, b params of compare function must be number'); - } else { - return a - b; - } - }; + if (options) { - this.options = options; - } else { - this.options = { - comparator: defaultComparator - }; + const { comparator } = options; + if (comparator) this._comparator = comparator; } if (elements) { @@ -48,6 +37,18 @@ export class Heap extends IterableElementBase { } } + protected _comparator = (a: E, b: E) => { + if (!(typeof a === 'number' && typeof b === 'number')) { + throw new Error('The a, b params of compare function must be number'); + } else { + return a - b; + } + }; + + get comparator() { + return this._comparator; + } + protected _elements: E[] = []; get elements(): E[] { @@ -278,7 +279,7 @@ export class Heap extends IterableElementBase { * @returns A new Heap instance containing the same elements. */ clone(): Heap { - const clonedHeap = new Heap([], this.options); + const clonedHeap = new Heap([], { comparator: this.comparator }); clonedHeap._elements = [...this.elements]; return clonedHeap; } @@ -413,7 +414,7 @@ export class Heap extends IterableElementBase { while (index > 0) { const parent = (index - 1) >> 1; const parentItem = this.elements[parent]; - if (this.options.comparator(parentItem, element) <= 0) break; + if (this.comparator(parentItem, element) <= 0) break; this.elements[index] = parentItem; index = parent; } @@ -435,11 +436,11 @@ export class Heap extends IterableElementBase { let left = (index << 1) | 1; const right = left + 1; let minItem = this.elements[left]; - if (right < this.elements.length && this.options.comparator(minItem, this.elements[right]) > 0) { + if (right < this.elements.length && this.comparator(minItem, this.elements[right]) > 0) { left = right; minItem = this.elements[right]; } - if (this.options.comparator(minItem, element) >= 0) break; + if (this.comparator(minItem, element) >= 0) break; this.elements[index] = minItem; index = left; } diff --git a/src/data-structures/heap/max-heap.ts b/src/data-structures/heap/max-heap.ts index d8def84..89ebef2 100644 --- a/src/data-structures/heap/max-heap.ts +++ b/src/data-structures/heap/max-heap.ts @@ -20,7 +20,7 @@ import { Heap } from './heap'; */ export class MaxHeap extends Heap { constructor( - elements?: Iterable, + elements: Iterable = [], options: HeapOptions = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { diff --git a/src/data-structures/heap/min-heap.ts b/src/data-structures/heap/min-heap.ts index e43dc9e..2ddce15 100644 --- a/src/data-structures/heap/min-heap.ts +++ b/src/data-structures/heap/min-heap.ts @@ -20,7 +20,7 @@ import { Heap } from './heap'; */ export class MinHeap extends Heap { constructor( - elements?: Iterable, + elements: Iterable = [], options: HeapOptions = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { diff --git a/src/data-structures/linked-list/doubly-linked-list.ts b/src/data-structures/linked-list/doubly-linked-list.ts index 89273b4..caaffc9 100644 --- a/src/data-structures/linked-list/doubly-linked-list.ts +++ b/src/data-structures/linked-list/doubly-linked-list.ts @@ -35,7 +35,7 @@ export class DoublyLinkedList extends IterableElementBase { /** * The constructor initializes the linked list with an empty head, tail, and size. */ - constructor(elements?: Iterable) { + constructor(elements: Iterable = []) { super(); this._head = undefined; this._tail = undefined; diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts index a29bcc5..aa32809 100644 --- a/src/data-structures/linked-list/singly-linked-list.ts +++ b/src/data-structures/linked-list/singly-linked-list.ts @@ -27,11 +27,8 @@ export class SinglyLinkedList extends IterableElementBase { /** * The constructor initializes the linked list with an empty head, tail, and length. */ - constructor(elements?: Iterable) { + constructor(elements: Iterable = []) { super(); - this._head = undefined; - this._tail = undefined; - this._size = 0; if (elements) { for (const el of elements) this.push(el); } @@ -49,7 +46,7 @@ export class SinglyLinkedList extends IterableElementBase { return this._tail; } - protected _size: number; + protected _size: number = 0; get size(): number { return this._size; diff --git a/src/data-structures/linked-list/skip-linked-list.ts b/src/data-structures/linked-list/skip-linked-list.ts index 8759a63..da03ad4 100644 --- a/src/data-structures/linked-list/skip-linked-list.ts +++ b/src/data-structures/linked-list/skip-linked-list.ts @@ -5,6 +5,7 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ +import type { SkipLinkedListOptions } from '../../types'; export class SkipListNode { key: K; @@ -19,39 +20,37 @@ export class SkipListNode { } export class SkipList { - /** - * The constructor initializes a SkipList with a specified maximum level and probability. - * @param [maxLevel=16] - The `maxLevel` parameter represents the maximum level that a skip list can have. It determines - * the maximum number of levels that can be created in the skip list. - * @param [probability=0.5] - The probability parameter represents the probability of a node being promoted to a higher - * level in the skip list. It is used to determine the height of each node in the skip list. - */ - constructor(maxLevel = 16, probability = 0.5) { - this._head = new SkipListNode(undefined as any, undefined as any, maxLevel); - this._level = 0; - this._maxLevel = maxLevel; - this._probability = probability; + constructor(elements: Iterable<[K, V]> = [], options?: SkipLinkedListOptions) { + if (options) { + const { maxLevel, probability } = options; + if (typeof maxLevel === 'number') this._maxLevel = maxLevel; + if (typeof probability === 'number') this._probability = probability; + } + + if (elements) { + for (const [key, value] of elements) this.add(key, value); + } } - protected _head: SkipListNode; + protected _head: SkipListNode = new SkipListNode(undefined as any, undefined as any, this.maxLevel); get head(): SkipListNode { return this._head; } - protected _level: number; + protected _level: number = 0; get level(): number { return this._level; } - protected _maxLevel: number; + protected _maxLevel: number = 16; get maxLevel(): number { return this._maxLevel; } - protected _probability: number; + protected _probability: number = 0.5; get probability(): number { return this._probability; diff --git a/src/data-structures/matrix/matrix.ts b/src/data-structures/matrix/matrix.ts index 9ad7bd0..21a988d 100644 --- a/src/data-structures/matrix/matrix.ts +++ b/src/data-structures/matrix/matrix.ts @@ -5,6 +5,7 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ +import type { MatrixOptions } from '../../types'; export class Matrix { /** @@ -14,16 +15,7 @@ export class Matrix { * @param [options] - The `options` parameter is an optional object that can contain the following * properties: */ - constructor( - data: number[][], - options?: { - rows?: number; - cols?: number; - addFn?: (a: number, b: number) => any; - subtractFn?: (a: number, b: number) => any; - multiplyFn?: (a: number, b: number) => any; - } - ) { + constructor(data: number[][], options?: MatrixOptions) { if (options) { const { rows, cols, addFn, subtractFn, multiplyFn } = options; if (typeof rows === 'number' && rows > 0) this._rows = rows; diff --git a/src/data-structures/priority-queue/max-priority-queue.ts b/src/data-structures/priority-queue/max-priority-queue.ts index bb5c5c5..287b206 100644 --- a/src/data-structures/priority-queue/max-priority-queue.ts +++ b/src/data-structures/priority-queue/max-priority-queue.ts @@ -10,7 +10,7 @@ import { PriorityQueue } from './priority-queue'; export class MaxPriorityQueue extends PriorityQueue { constructor( - elements?: Iterable, + elements: Iterable = [], options: PriorityQueueOptions = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { diff --git a/src/data-structures/priority-queue/min-priority-queue.ts b/src/data-structures/priority-queue/min-priority-queue.ts index 93d5278..8e12bce 100644 --- a/src/data-structures/priority-queue/min-priority-queue.ts +++ b/src/data-structures/priority-queue/min-priority-queue.ts @@ -10,7 +10,7 @@ import { PriorityQueue } from './priority-queue'; export class MinPriorityQueue extends PriorityQueue { constructor( - elements?: Iterable, + elements: Iterable = [], options: PriorityQueueOptions = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { diff --git a/src/data-structures/priority-queue/priority-queue.ts b/src/data-structures/priority-queue/priority-queue.ts index 6e454a6..d4c15e7 100644 --- a/src/data-structures/priority-queue/priority-queue.ts +++ b/src/data-structures/priority-queue/priority-queue.ts @@ -17,7 +17,7 @@ import { Heap } from '../heap'; * 6. Kth Largest Element in a Data Stream: Used to maintain a min-heap of size K for quickly finding the Kth largest element in stream data */ export class PriorityQueue extends Heap { - constructor(elements?: Iterable, options?: PriorityQueueOptions) { + constructor(elements: Iterable = [], options?: PriorityQueueOptions) { super(elements, options); } } diff --git a/src/data-structures/queue/deque.ts b/src/data-structures/queue/deque.ts index 25b0cb5..02aa162 100644 --- a/src/data-structures/queue/deque.ts +++ b/src/data-structures/queue/deque.ts @@ -5,7 +5,7 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type { ElementCallback, IterableWithSizeOrLength } from '../../types'; +import type { DequeOptions, ElementCallback, IterableWithSizeOrLength } from '../../types'; import { IterableElementBase } from '../base'; import { calcMinUnitsRequired, rangeCheck } from '../../utils'; @@ -22,19 +22,16 @@ export class Deque extends IterableElementBase { protected _bucketLast = 0; protected _lastInBucket = 0; protected _bucketCount = 0; - protected readonly _bucketSize: number; + protected readonly _bucketSize: number = 1 << 12; - /** - * The constructor initializes a data structure with a specified bucket size and populates it with - * elements from an iterable. - * @param elements - The `elements` parameter is an iterable object (such as an array or a Set) that - * contains the initial elements to be stored in the data structure. It can also be an object with a - * `length` property or a `size` property, which represents the number of elements in the iterable. - * @param bucketSize - The `bucketSize` parameter is the maximum number of elements that can be - * stored in each bucket. It determines the size of each bucket in the data structure. - */ - constructor(elements: IterableWithSizeOrLength = [], bucketSize = 1 << 12) { + constructor(elements: IterableWithSizeOrLength = [], options?: DequeOptions) { super(); + + if (options) { + const { bucketSize } = options; + if (typeof bucketSize === 'number') this._bucketSize = bucketSize; + } + let _size: number; if ('length' in elements) { if (elements.length instanceof Function) _size = elements.length(); @@ -44,7 +41,6 @@ export class Deque extends IterableElementBase { else _size = elements.size; } - this._bucketSize = bucketSize; this._bucketCount = calcMinUnitsRequired(_size, this._bucketSize) || 1; for (let i = 0; i < this._bucketCount; ++i) { this._buckets.push(new Array(this._bucketSize)); @@ -637,7 +633,7 @@ export class Deque extends IterableElementBase { * satisfy the given predicate function. */ filter(predicate: ElementCallback, thisArg?: any): Deque { - const newDeque = new Deque([], this._bucketSize); + const newDeque = new Deque([], { bucketSize: this._bucketSize }); let index = 0; for (const el of this) { if (predicate.call(thisArg, el, index, this)) { @@ -666,7 +662,7 @@ export class Deque extends IterableElementBase { * @returns a new Deque object with the mapped values. */ map(callback: ElementCallback, thisArg?: any): Deque { - const newDeque = new Deque([], this._bucketSize); + const newDeque = new Deque([], { bucketSize: this._bucketSize }); let index = 0; for (const el of this) { newDeque.push(callback.call(thisArg, el, index, this)); diff --git a/src/data-structures/queue/queue.ts b/src/data-structures/queue/queue.ts index f23a669..1c2d4e7 100644 --- a/src/data-structures/queue/queue.ts +++ b/src/data-structures/queue/queue.ts @@ -13,29 +13,30 @@ import { SinglyLinkedList } from '../linked-list'; * 3. Uses: Queues are commonly used to manage a series of tasks or elements that need to be processed in order. For example, managing task queues in a multi-threaded environment, or in algorithms for data structures like trees and graphs for breadth-first search. * 4. Task Scheduling: Managing the order of task execution in operating systems or applications. * 5. Data Buffering: Acting as a buffer for data packets in network communication. - * 6. Breadth-First Search (BFS): In traversal algorithms for graphs and trees, queues store nodes that are to be visited. + * 6. Breadth-First Search (BFS): In traversal algorithms for graphs and trees, queues store elements that are to be visited. * 7. Real-time Queuing: Like queuing systems in banks or supermarkets. */ export class Queue extends IterableElementBase { /** * The constructor initializes an instance of a class with an optional array of elements and sets the offset to 0. * @param {E[]} [elements] - The `elements` parameter is an optional array of elements of type `E`. If provided, it - * will be used to initialize the `_nodes` property of the class. If not provided, the `_nodes` property will be + * will be used to initialize the `_elements` property of the class. If not provided, the `_elements` property will be * initialized as an empty array. */ - constructor(elements?: E[]) { + constructor(elements: Iterable = []) { super(); - this._nodes = elements || []; - this._offset = 0; + if (elements) { + for (const el of elements) this.push(el); + } } - protected _nodes: E[]; + protected _elements: E[] = []; - get nodes(): E[] { - return this._nodes; + get elements(): E[] { + return this._elements; } - protected _offset: number; + protected _offset: number = 0; get offset(): number { return this._offset; @@ -46,19 +47,19 @@ export class Queue extends IterableElementBase { * @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; + return this.elements.length - this.offset; } /** * Time Complexity: O(1) - constant time as it retrieves the value at the current offset. * Space Complexity: O(1) - no additional space is used. * - * The `first` function returns the first element of the array `_nodes` if it exists, otherwise it returns `undefined`. - * @returns The `get first()` method returns the first element of the data structure, represented by the `_nodes` array at + * The `first` function returns the first element of the array `_elements` if it exists, otherwise it returns `undefined`. + * @returns The `get first()` method returns the first element of the data structure, represented by the `_elements` array at * the `_offset` index. If the data structure is empty (size is 0), it returns `undefined`. */ get first(): E | undefined { - return this.size > 0 ? this.nodes[this.offset] : undefined; + return this.size > 0 ? this.elements[this.offset] : undefined; } /** @@ -71,11 +72,11 @@ export class Queue extends IterableElementBase { * Space Complexity: O(1) - no additional space is used. * * The `last` function returns the last element in an array-like data structure, or undefined if the structure is empty. - * @returns The method `get last()` returns the last element of the `_nodes` array if the array is not empty. If the + * @returns The method `get last()` returns the last element of the `_elements` array if the array is not empty. If the * array is empty, it returns `undefined`. */ get last(): E | undefined { - return this.size > 0 ? this.nodes[this.nodes.length - 1] : undefined; + return this.size > 0 ? this.elements[this.elements.length - 1] : undefined; } /** @@ -109,7 +110,7 @@ export class Queue extends IterableElementBase { * @returns The `add` method is returning a `Queue` object. */ push(element: E): boolean { - this.nodes.push(element); + this.elements.push(element); return true; } @@ -132,11 +133,11 @@ export class Queue extends IterableElementBase { const first = this.first; this._offset += 1; - if (this.offset * 2 < this.nodes.length) return first; + if (this.offset * 2 < this.elements.length) return first; // only delete dequeued elements when reaching half size // to decrease latency of shifting elements. - this._nodes = this.nodes.slice(this.offset); + this._elements = this.elements.slice(this.offset); this._offset = 0; return first; } @@ -150,8 +151,8 @@ export class Queue extends IterableElementBase { * Time Complexity: O(1) - constant time as it retrieves the value at the current offset. * Space Complexity: O(1) - no additional space is used. * - * The `peek` function returns the first element of the array `_nodes` if it exists, otherwise it returns `undefined`. - * @returns The `peek()` method returns the first element of the data structure, represented by the `_nodes` array at + * The `peek` function returns the first element of the array `_elements` if it exists, otherwise it returns `undefined`. + * @returns The `peek()` method returns the first element of the data structure, represented by the `_elements` array at * the `_offset` index. If the data structure is empty (size is 0), it returns `undefined`. */ peek(): E | undefined { @@ -168,7 +169,7 @@ export class Queue extends IterableElementBase { * Space Complexity: O(1) - no additional space is used. * * The `peekLast` function returns the last element in an array-like data structure, or undefined if the structure is empty. - * @returns The method `peekLast()` returns the last element of the `_nodes` array if the array is not empty. If the + * @returns The method `peekLast()` returns the last element of the `_elements` array if the array is not empty. If the * array is empty, it returns `undefined`. */ peekLast(): E | undefined { @@ -219,7 +220,7 @@ export class Queue extends IterableElementBase { * @param index */ getAt(index: number): E | undefined { - return this.nodes[index]; + return this.elements[index]; } /** @@ -247,18 +248,18 @@ export class Queue extends IterableElementBase { * Time Complexity: O(1) - constant time as it returns a shallow copy of the internal array. * Space Complexity: O(n) - where n is the number of elements in the queue. * - * The toArray() function returns an array of elements from the current offset to the end of the _nodes array. + * The toArray() function returns an array of elements from the current offset to the end of the _elements array. * @returns An array of type E is being returned. */ toArray(): E[] { - return this.nodes.slice(this.offset); + return this.elements.slice(this.offset); } /** - * The clear function resets the nodes array and offset to their initial values. + * The clear function resets the elements array and offset to their initial values. */ clear(): void { - this._nodes = []; + this._elements = []; this._offset = 0; } @@ -275,7 +276,7 @@ export class Queue extends IterableElementBase { * @returns The `clone()` method is returning a new instance of the `Queue` class. */ clone(): Queue { - return new Queue(this.nodes.slice(this.offset)); + return new Queue(this.elements.slice(this.offset)); } /** @@ -345,7 +346,7 @@ export class Queue extends IterableElementBase { */ protected* _getIterator(): IterableIterator { - for (const item of this.nodes) { + for (const item of this.elements) { yield item; } } diff --git a/src/data-structures/stack/stack.ts b/src/data-structures/stack/stack.ts index d3adcbd..63cabfa 100644 --- a/src/data-structures/stack/stack.ts +++ b/src/data-structures/stack/stack.ts @@ -23,17 +23,14 @@ export class Stack extends IterableElementBase { * of elements of type `E`. It is used to initialize the `_elements` property of the class. If the `elements` parameter * is provided and is an array, it is assigned to the `_elements */ - constructor(elements?: Iterable) { + constructor(elements: Iterable = []) { super(); - this._elements = []; if (elements) { - for (const el of elements) { - this.push(el); - } + for (const el of elements) this.push(el); } } - protected _elements: E[]; + protected _elements: E[] = []; get elements(): E[] { return this._elements; diff --git a/src/data-structures/trie/trie.ts b/src/data-structures/trie/trie.ts index d87d6e8..d4a1eb9 100644 --- a/src/data-structures/trie/trie.ts +++ b/src/data-structures/trie/trie.ts @@ -5,7 +5,7 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type { ElementCallback } from '../../types'; +import type { ElementCallback, TrieOptions } from '../../types'; import { IterableElementBase } from '../base'; /** @@ -38,31 +38,30 @@ export class TrieNode { * 11. Text Word Frequency Count: Counting and storing the frequency of words in a large amount of text data." */ export class Trie extends IterableElementBase { - constructor(words?: string[], caseSensitive = true) { + constructor(words: Iterable = [], options?: TrieOptions) { super(); - this._root = new TrieNode(''); - this._caseSensitive = caseSensitive; - this._size = 0; + if (options) { + const { caseSensitive } = options; + if (caseSensitive !== undefined) this._caseSensitive = caseSensitive; + } if (words) { - for (const word of words) { - this.add(word); - } + for (const word of words) this.add(word); } } - protected _size: number; + protected _size: number = 0; get size(): number { return this._size; } - protected _caseSensitive: boolean; + protected _caseSensitive: boolean = true; get caseSensitive(): boolean { return this._caseSensitive; } - protected _root: TrieNode; + protected _root: TrieNode = new TrieNode(''); get root() { return this._root; diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index afbf50b..0d86710 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -6,6 +6,6 @@ export type BinaryTreeNodeNested = BinaryTreeNode> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BinaryTreeOptions = { - iterationType: IterationType, - extractor: (key: K) => number + iterationType?: IterationType, + extractor?: (key: K) => number } diff --git a/src/types/data-structures/binary-tree/bst.ts b/src/types/data-structures/binary-tree/bst.ts index 73f50ba..131f9a2 100644 --- a/src/types/data-structures/binary-tree/bst.ts +++ b/src/types/data-structures/binary-tree/bst.ts @@ -7,5 +7,5 @@ export type BSTNodeNested = BSTNode> = BST>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BSTOptions = BinaryTreeOptions & { - variant: BSTVariant + variant?: BSTVariant } diff --git a/src/types/data-structures/binary-tree/tree-multimap.ts b/src/types/data-structures/binary-tree/tree-multimap.ts index ffc7172..c1bb3c0 100644 --- a/src/types/data-structures/binary-tree/tree-multimap.ts +++ b/src/types/data-structures/binary-tree/tree-multimap.ts @@ -5,4 +5,4 @@ export type TreeMultimapNodeNested = TreeMultimapNode> = TreeMultimap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type TreeMultimapOptions = Omit, 'isMergeDuplicatedNodeByKey'> & {} +export type TreeMultimapOptions = AVLTreeOptions & {} diff --git a/src/types/data-structures/hash/hash-map.ts b/src/types/data-structures/hash/hash-map.ts index f5d749b..d672839 100644 --- a/src/types/data-structures/hash/hash-map.ts +++ b/src/types/data-structures/hash/hash-map.ts @@ -5,9 +5,13 @@ export type HashMapLinkedNode = { prev: HashMapLinkedNode; }; +export type LinkedHashMapOptions = { + hashFn?: (key: K) => string; + objHashFn?: (key: K) => object; +}; + export type HashMapOptions = { - hashFn: (key: K) => string; - objHashFn: (key: K) => object; + hashFn?: (key: K) => string; }; export type HashMapStoreItem = { key: K; value: V }; diff --git a/src/types/data-structures/hash/hash-table.ts b/src/types/data-structures/hash/hash-table.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/src/types/data-structures/hash/hash-table.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/src/types/data-structures/hash/index.ts b/src/types/data-structures/hash/index.ts index bda3af6..e07d6cd 100644 --- a/src/types/data-structures/hash/index.ts +++ b/src/types/data-structures/hash/index.ts @@ -1,4 +1,3 @@ export * from './hash-map'; -export * from './hash-table'; export type HashFunction = (key: K) => number; diff --git a/src/types/data-structures/heap/heap.ts b/src/types/data-structures/heap/heap.ts index 86602f4..4631285 100644 --- a/src/types/data-structures/heap/heap.ts +++ b/src/types/data-structures/heap/heap.ts @@ -1,3 +1,3 @@ import { Comparator } from '../../common'; -export type HeapOptions = { comparator: Comparator }; +export type HeapOptions = { comparator?: Comparator }; diff --git a/src/types/data-structures/linked-list/index.ts b/src/types/data-structures/linked-list/index.ts index 0bdd0b6..e489ecc 100644 --- a/src/types/data-structures/linked-list/index.ts +++ b/src/types/data-structures/linked-list/index.ts @@ -1,2 +1,3 @@ export * from './singly-linked-list'; export * from './doubly-linked-list'; +export * from './skip-linked-list'; diff --git a/src/types/data-structures/linked-list/skip-linked-list.ts b/src/types/data-structures/linked-list/skip-linked-list.ts index cb0ff5c..f581911 100644 --- a/src/types/data-structures/linked-list/skip-linked-list.ts +++ b/src/types/data-structures/linked-list/skip-linked-list.ts @@ -1 +1 @@ -export {}; +export type SkipLinkedListOptions = { maxLevel?: number; probability?: number }; diff --git a/src/types/data-structures/matrix/index.ts b/src/types/data-structures/matrix/index.ts index 8716d60..64c4fa3 100644 --- a/src/types/data-structures/matrix/index.ts +++ b/src/types/data-structures/matrix/index.ts @@ -1 +1,2 @@ export * from './navigator'; +export * from './matrix'; diff --git a/src/types/data-structures/matrix/matrix.ts b/src/types/data-structures/matrix/matrix.ts index cb0ff5c..4f9cc77 100644 --- a/src/types/data-structures/matrix/matrix.ts +++ b/src/types/data-structures/matrix/matrix.ts @@ -1 +1,7 @@ -export {}; +export type MatrixOptions = { + rows?: number; + cols?: number; + addFn?: (a: number, b: number) => any; + subtractFn?: (a: number, b: number) => any; + multiplyFn?: (a: number, b: number) => any; +}; diff --git a/src/types/data-structures/matrix/matrix2d.ts b/src/types/data-structures/matrix/matrix2d.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/src/types/data-structures/matrix/matrix2d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/src/types/data-structures/matrix/vector2d.ts b/src/types/data-structures/matrix/vector2d.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/src/types/data-structures/matrix/vector2d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/src/types/data-structures/queue/deque.ts b/src/types/data-structures/queue/deque.ts index cb0ff5c..8134e3e 100644 --- a/src/types/data-structures/queue/deque.ts +++ b/src/types/data-structures/queue/deque.ts @@ -1 +1 @@ -export {}; +export type DequeOptions = { bucketSize?: number }; diff --git a/src/types/data-structures/trie/trie.ts b/src/types/data-structures/trie/trie.ts index cb0ff5c..bbdd970 100644 --- a/src/types/data-structures/trie/trie.ts +++ b/src/types/data-structures/trie/trie.ts @@ -1 +1 @@ -export {}; +export type TrieOptions = { caseSensitive?: boolean }; diff --git a/test/unit/data-structures/hash/hash-table.test.ts b/test/unit/data-structures/hash/hash-table.test.ts deleted file mode 100644 index ae88b48..0000000 --- a/test/unit/data-structures/hash/hash-table.test.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { HashTable, HashTableNode } from '../../../../src'; - -describe('HashNode', () => { - it('should create a HashNode with key and value', () => { - const key = 'testKey'; - const value = 'testValue'; - const hashNode = new HashTableNode(key, value); - - expect(hashNode.key).toBe(key); - expect(hashNode.value).toBe(value); - expect(hashNode.next).toBe(undefined); - }); -}); - -describe('HashTable', () => { - it('should initialize with default capacity', () => { - const hashTable = new HashTable(); - expect(hashTable.capacity).toBe(16); - expect(hashTable.buckets).toEqual(new Array(16).fill(undefined)); - expect(hashTable.hashFn('a')).toBe(6); - expect(hashTable.capacity).toBe(16); - expect(hashTable.size).toBe(0); - expect(hashTable.buckets.length).toBe(16); - }); - - it('should initialize with custom capacity', () => { - const customCapacity = 500; - const hashTable = new HashTable(customCapacity); - - expect(hashTable.capacity).toBe(customCapacity); - expect(hashTable.size).toBe(0); - expect(hashTable.buckets.length).toBe(customCapacity); - }); - - it('should put and get values correctly', () => { - const hashTable = new HashTable(); - const key = 'testKey'; - const value = 'testValue'; - - hashTable.set(key, value); - const retrievedValue = hashTable.get(key); - - expect(retrievedValue).toBe(value); - }); - - it('should handle collisions by chaining', () => { - const hashTable = new HashTable(); - const key1 = 'testKey1'; - const value1 = 'testValue1'; - const key2 = 'testKey2'; - const value2 = 'testValue2'; - - hashTable.set(key1, value1); - hashTable.set(key2, value2); - - const retrievedValue1 = hashTable.get(key1); - const retrievedValue2 = hashTable.get(key2); - - expect(retrievedValue1).toBe(value1); - expect(retrievedValue2).toBe(value2); - }); - - it('should update value for an existing key', () => { - const hashTable = new HashTable(); - const key = 'testKey'; - const initialValue = 'testValue1'; - const updatedValue = 'testValue2'; - - hashTable.set(key, initialValue); - hashTable.set(key, updatedValue); - - const retrievedValue = hashTable.get(key); - - expect(retrievedValue).toBe(updatedValue); - }); - - it('should return undefined for non-existent key', () => { - const hashTable = new HashTable(); - const key = 'nonExistentKey'; - - const retrievedValue = hashTable.get(key); - - expect(retrievedValue).toBeUndefined(); - }); - - it('should delete key-value pair correctly', () => { - const hashTable = new HashTable(); - const key = 'testKey'; - const value = 'testValue'; - - hashTable.set(key, value); - hashTable.delete(key); - - const retrievedValue = hashTable.get(key); - - expect(retrievedValue).toBeUndefined(); - expect(hashTable.size).toBe(0); - }); -}); - -describe('HashTable', () => { - let hashTable: HashTable; - - beforeEach(() => { - hashTable = new HashTable(); - }); - - it('should insert and retrieve values correctly', () => { - hashTable.set('one', 1); - hashTable.set('two', 2); - - expect(hashTable.get('one')).toBe(1); - expect(hashTable.get('two')).toBe(2); - }); - - it('should update values correctly', () => { - hashTable.set('one', 1); - expect(hashTable.get('one')).toBe(1); - - hashTable.set('one', 100); // Update the value - expect(hashTable.get('one')).toBe(100); - }); - - it('should handle collisions correctly', () => { - hashTable = new HashTable(1); // Set a small capacity to force collisions - hashTable.set('one', 1); - hashTable.set('two', 2); - - expect(hashTable.get('one')).toBe(1); - expect(hashTable.get('two')).toBe(2); - }); - - it('should delete values correctly', () => { - hashTable.set('one', 1); - hashTable.set('two', 2); - hashTable.delete('one'); - - expect(hashTable.get('one')).toBeUndefined(); - expect(hashTable.get('two')).toBe(2); - }); - - it('should handle non-existent keys correctly', () => { - expect(hashTable.get('non-existent')).toBeUndefined(); - hashTable.delete('non-existent'); // Removing a non-existent key should not cause errors - }); - - it('should handle custom hash function correctly', () => { - // const customHashFn = () => { - // // Custom hash function that returns a fixed value for all keys - // return 42; - // }; - - hashTable = new HashTable(16); - hashTable.set('one', 1); - expect(hashTable.get('one')).toBe(1); - expect(hashTable.get('two')).toBeUndefined(); - }); - - it('should expand when load factor exceeds threshold', () => { - hashTable = new HashTable(2); // Set a small capacity to trigger expansion - hashTable.set('one', 1); - hashTable.set('two', 2); - hashTable.set('three', 3); // This should trigger an expansion - - expect(hashTable.capacity).toBe(16); - expect(hashTable.get('one')).toBe(1); - expect(hashTable.get('two')).toBe(2); - expect(hashTable.get('three')).toBe(3); - }); -}); - -describe('HashTable performance', function () { - it('Items set performance', function () { - const mag = 100000; - const ht = new HashTable(); - // const s = performance.now(); - for (let i = 0; i < mag; i++) { - ht.set(i, i); - } - // const s1 = performance.now(); - const map = new Map(); - for (let i = 0; i < mag; i++) { - map.set(i, i); - } - }); -}); - -describe('HashTable methods', () => { - let hashTable: HashTable; - - beforeEach(() => { - hashTable = new HashTable(); - for (let i = 0; i < 10; i++) { - hashTable.set(`key${i}`, `value${i}`); - } - }); - - test('should retrieve correct values with get method', () => { - for (let i = 0; i < 10; i++) { - expect(hashTable.get(`key${i}`)).toBe(`value${i}`); - } - }); - - // test('forEach should apply a function to each key-value pair', () => { - // const mockCallback = jest.fn(); - // hashTable.forEach(mockCallback); - // - // expect(mockCallback.mock.calls.length).toBe(10); - // for (let i = 0; i < 10; i++) { - // // Check whether each key-value pair has been called before, regardless of the order - // const call = mockCallback.mock.calls.find(call => call[1] === `value${i}`); - // expect(call).toBeTruthy(); - // expect(call[0]).toBe(`key${i}`); - // } - // }); - - test('filter should return a new HashTable with elements that satisfy the condition', () => { - const filtered = hashTable.filter(([key]) => key.endsWith('1') || key.endsWith('3')); - - expect(filtered.size).toBe(2); - expect(filtered.get('key1')).toBe('value1'); - expect(filtered.get('key3')).toBe('value3'); - }); - - test('map should return a new HashTable with mapped values', () => { - const mapped = hashTable.map(([, value]) => value.toUpperCase()); - - for (let i = 0; i < 10; i++) { - expect(mapped.get(`key${i}`)).toBe(`value${i}`.toUpperCase()); - } - }); - - test('reduce should accumulate values based on the reducer function', () => { - const result = hashTable.reduce((acc, [, value]) => `${acc}-${value}`, ''); - - expect(result).toBe('-value5-value7-value3-value4-value6-value0-value2-value8-value1-value9'); - }); -}); diff --git a/test/unit/data-structures/linked-list/skip-list.test.ts b/test/unit/data-structures/linked-list/skip-list.test.ts index 1afc111..435758e 100644 --- a/test/unit/data-structures/linked-list/skip-list.test.ts +++ b/test/unit/data-structures/linked-list/skip-list.test.ts @@ -32,7 +32,7 @@ describe('SkipList', () => { skipList.delete(2); - expect(skipList.get(2)).toBeUndefined(); // 修改这里的断言 + expect(skipList.get(2)).toBeUndefined(); }); it('should handle random data correctly', () => { diff --git a/test/unit/data-structures/queue/deque.test.ts b/test/unit/data-structures/queue/deque.test.ts index 2d4946a..b5830bb 100644 --- a/test/unit/data-structures/queue/deque.test.ts +++ b/test/unit/data-structures/queue/deque.test.ts @@ -231,10 +231,10 @@ describe('Deque - Additional Operations', () => { }); describe('Deque - push Method', () => { let deque: Deque; - const bucketSize = 10; // 假设的 bucket 大小 + const bucketSize = 10; beforeEach(() => { - deque = new Deque([], bucketSize); + deque = new Deque([], { bucketSize }); }); test('push should add an element when deque is empty', () => { @@ -276,7 +276,7 @@ describe('Deque - pop Method', () => { const bucketSize = 10; beforeEach(() => { - deque = new Deque([], bucketSize); + deque = new Deque([], { bucketSize }); }); test('pop should remove and return the last element', () => { @@ -307,7 +307,7 @@ describe('Deque - unshift Method', () => { const bucketSize = 10; beforeEach(() => { - deque = new Deque([], bucketSize); + deque = new Deque([], { bucketSize }); }); test('unshift should add an element to the beginning when deque is empty', () => { @@ -339,7 +339,7 @@ describe('Deque - shift Method', () => { const bucketSize = 10; beforeEach(() => { - deque = new Deque([], bucketSize); + deque = new Deque([], { bucketSize }); }); test('shift should remove and return the first element', () => { diff --git a/test/unit/data-structures/trie/trie.test.ts b/test/unit/data-structures/trie/trie.test.ts index 45c299a..32a1e19 100644 --- a/test/unit/data-structures/trie/trie.test.ts +++ b/test/unit/data-structures/trie/trie.test.ts @@ -778,7 +778,7 @@ describe('Trie operations', () => { }); it('Case Sensitivity', () => { - const caseInsensitiveTrie = new Trie(['apple', 'Banana'], false); + const caseInsensitiveTrie = new Trie(['apple', 'Banana'], { caseSensitive: false }); expect(caseInsensitiveTrie.has('APPLE')).toBe(true); expect(caseInsensitiveTrie.has('banana')).toBe(true); expect(caseInsensitiveTrie.has('Cherry')).toBe(false);