bst-typed 2.0.0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-structures/graph/abstract-graph.js +14 -14
- package/dist/data-structures/hash/hash-map.d.ts +46 -0
- package/dist/data-structures/hash/hash-map.js +46 -0
- package/dist/data-structures/linked-list/singly-linked-list.d.ts +66 -0
- package/dist/data-structures/linked-list/singly-linked-list.js +66 -0
- package/dist/data-structures/queue/queue.d.ts +47 -0
- package/dist/data-structures/queue/queue.js +47 -0
- package/dist/data-structures/stack/stack.d.ts +121 -0
- package/dist/data-structures/stack/stack.js +121 -0
- package/package.json +3 -3
- package/src/data-structures/graph/abstract-graph.ts +14 -14
- package/src/data-structures/hash/hash-map.ts +46 -0
- package/src/data-structures/linked-list/singly-linked-list.ts +66 -0
- package/src/data-structures/queue/queue.ts +47 -0
- package/src/data-structures/stack/stack.ts +121 -0
|
@@ -250,7 +250,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
250
250
|
isWeight = false;
|
|
251
251
|
if (isWeight) {
|
|
252
252
|
const allPaths = this.getAllPathsBetween(v1, v2);
|
|
253
|
-
let min =
|
|
253
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
254
254
|
for (const path of allPaths) {
|
|
255
255
|
min = Math.min(this.getPathSumWeight(path), min);
|
|
256
256
|
}
|
|
@@ -315,7 +315,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
315
315
|
if (isWeight) {
|
|
316
316
|
if (isDFS) {
|
|
317
317
|
const allPaths = this.getAllPathsBetween(v1, v2, 10000);
|
|
318
|
-
let min =
|
|
318
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
319
319
|
let minIndex = -1;
|
|
320
320
|
let index = 0;
|
|
321
321
|
for (const path of allPaths) {
|
|
@@ -379,7 +379,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
379
379
|
* @returns The function `dijkstraWithoutHeap` returns an object of type `DijkstraResult<VO>`.
|
|
380
380
|
*/
|
|
381
381
|
dijkstraWithoutHeap(src, dest = undefined, getMinDist = false, genPaths = false) {
|
|
382
|
-
let minDist =
|
|
382
|
+
let minDist = Number.MAX_SAFE_INTEGER;
|
|
383
383
|
let minDest = undefined;
|
|
384
384
|
let minPath = [];
|
|
385
385
|
const paths = [];
|
|
@@ -395,12 +395,12 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
395
395
|
for (const vertex of vertexMap) {
|
|
396
396
|
const vertexOrKey = vertex[1];
|
|
397
397
|
if (vertexOrKey instanceof AbstractVertex)
|
|
398
|
-
distMap.set(vertexOrKey,
|
|
398
|
+
distMap.set(vertexOrKey, Number.MAX_SAFE_INTEGER);
|
|
399
399
|
}
|
|
400
400
|
distMap.set(srcVertex, 0);
|
|
401
401
|
preMap.set(srcVertex, undefined);
|
|
402
402
|
const getMinOfNoSeen = () => {
|
|
403
|
-
let min =
|
|
403
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
404
404
|
let minV = undefined;
|
|
405
405
|
for (const [key, value] of distMap) {
|
|
406
406
|
if (!seen.has(key)) {
|
|
@@ -435,7 +435,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
435
435
|
seen.add(cur);
|
|
436
436
|
if (destVertex && destVertex === cur) {
|
|
437
437
|
if (getMinDist) {
|
|
438
|
-
minDist = distMap.get(destVertex) ||
|
|
438
|
+
minDist = distMap.get(destVertex) || Number.MAX_SAFE_INTEGER;
|
|
439
439
|
}
|
|
440
440
|
if (genPaths) {
|
|
441
441
|
getPaths(destVertex);
|
|
@@ -497,7 +497,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
497
497
|
*/
|
|
498
498
|
dijkstra(src, dest = undefined, getMinDist = false, genPaths = false) {
|
|
499
499
|
var _a;
|
|
500
|
-
let minDist =
|
|
500
|
+
let minDist = Number.MAX_SAFE_INTEGER;
|
|
501
501
|
let minDest = undefined;
|
|
502
502
|
let minPath = [];
|
|
503
503
|
const paths = [];
|
|
@@ -512,7 +512,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
512
512
|
for (const vertex of vertexMap) {
|
|
513
513
|
const vertexOrKey = vertex[1];
|
|
514
514
|
if (vertexOrKey instanceof AbstractVertex)
|
|
515
|
-
distMap.set(vertexOrKey,
|
|
515
|
+
distMap.set(vertexOrKey, Number.MAX_SAFE_INTEGER);
|
|
516
516
|
}
|
|
517
517
|
const heap = new heap_1.Heap([], { comparator: (a, b) => a.key - b.key });
|
|
518
518
|
heap.add({ key: 0, value: srcVertex });
|
|
@@ -549,7 +549,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
549
549
|
seen.add(cur);
|
|
550
550
|
if (destVertex && destVertex === cur) {
|
|
551
551
|
if (getMinDist) {
|
|
552
|
-
minDist = distMap.get(destVertex) ||
|
|
552
|
+
minDist = distMap.get(destVertex) || Number.MAX_SAFE_INTEGER;
|
|
553
553
|
}
|
|
554
554
|
if (genPaths) {
|
|
555
555
|
getPaths(destVertex);
|
|
@@ -618,7 +618,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
618
618
|
const paths = [];
|
|
619
619
|
const distMap = new Map();
|
|
620
620
|
const preMap = new Map(); // predecessor
|
|
621
|
-
let min =
|
|
621
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
622
622
|
let minPath = [];
|
|
623
623
|
// TODO
|
|
624
624
|
let hasNegativeCycle;
|
|
@@ -631,7 +631,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
631
631
|
const edgeMap = this.edgeSet();
|
|
632
632
|
const numOfEdges = edgeMap.length;
|
|
633
633
|
this._vertexMap.forEach(vertex => {
|
|
634
|
-
distMap.set(vertex,
|
|
634
|
+
distMap.set(vertex, Number.MAX_SAFE_INTEGER);
|
|
635
635
|
});
|
|
636
636
|
distMap.set(srcVertex, 0);
|
|
637
637
|
for (let i = 1; i < numOfVertices; ++i) {
|
|
@@ -643,7 +643,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
643
643
|
const sWeight = distMap.get(s);
|
|
644
644
|
const dWeight = distMap.get(d);
|
|
645
645
|
if (sWeight !== undefined && dWeight !== undefined) {
|
|
646
|
-
if (distMap.get(s) !==
|
|
646
|
+
if (distMap.get(s) !== Number.MAX_SAFE_INTEGER && sWeight + weight < dWeight) {
|
|
647
647
|
distMap.set(d, sWeight + weight);
|
|
648
648
|
if (genPath)
|
|
649
649
|
preMap.set(d, s);
|
|
@@ -688,7 +688,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
688
688
|
const weight = edgeMap[j].weight;
|
|
689
689
|
const sWeight = distMap.get(s);
|
|
690
690
|
if (sWeight) {
|
|
691
|
-
if (sWeight !==
|
|
691
|
+
if (sWeight !== Number.MAX_SAFE_INTEGER && sWeight + weight < sWeight)
|
|
692
692
|
hasNegativeCycle = true;
|
|
693
693
|
}
|
|
694
694
|
}
|
|
@@ -738,7 +738,7 @@ class AbstractGraph extends base_1.IterableEntryBase {
|
|
|
738
738
|
}
|
|
739
739
|
for (let i = 0; i < n; i++) {
|
|
740
740
|
for (let j = 0; j < n; j++) {
|
|
741
|
-
costs[i][j] = ((_a = this.getEdge(idAndVertices[i][1], idAndVertices[j][1])) === null || _a === void 0 ? void 0 : _a.weight) ||
|
|
741
|
+
costs[i][j] = ((_a = this.getEdge(idAndVertices[i][1], idAndVertices[j][1])) === null || _a === void 0 ? void 0 : _a.weight) || Number.MAX_SAFE_INTEGER;
|
|
742
742
|
}
|
|
743
743
|
}
|
|
744
744
|
for (let k = 0; k < n; k++) {
|
|
@@ -13,6 +13,52 @@ import { IterableEntryBase } from '../base';
|
|
|
13
13
|
* 3. Unique Keys: Keys are unique.
|
|
14
14
|
* If you try to insert another entry with the same key, the new one will replace the old entry.
|
|
15
15
|
* 4. Unordered Collection: HashMap does not guarantee the order of entries, and the order may change over time.
|
|
16
|
+
* @example
|
|
17
|
+
* // should maintain insertion order
|
|
18
|
+
* const linkedHashMap = new LinkedHashMap<number, string>();
|
|
19
|
+
* linkedHashMap.set(1, 'A');
|
|
20
|
+
* linkedHashMap.set(2, 'B');
|
|
21
|
+
* linkedHashMap.set(3, 'C');
|
|
22
|
+
*
|
|
23
|
+
* const result = Array.from(linkedHashMap);
|
|
24
|
+
* console.log(result); // [
|
|
25
|
+
* // [1, 'A'],
|
|
26
|
+
* // [2, 'B'],
|
|
27
|
+
* // [3, 'C']
|
|
28
|
+
* // ]
|
|
29
|
+
* @example
|
|
30
|
+
* // fast lookup of values by key
|
|
31
|
+
* const hashMap = new HashMap<number, string>();
|
|
32
|
+
* hashMap.set(1, 'A');
|
|
33
|
+
* hashMap.set(2, 'B');
|
|
34
|
+
* hashMap.set(3, 'C');
|
|
35
|
+
*
|
|
36
|
+
* console.log(hashMap.get(1)); // 'A'
|
|
37
|
+
* console.log(hashMap.get(2)); // 'B'
|
|
38
|
+
* console.log(hashMap.get(3)); // 'C'
|
|
39
|
+
* console.log(hashMap.get(99)); // undefined
|
|
40
|
+
* @example
|
|
41
|
+
* // remove duplicates when adding multiple entries
|
|
42
|
+
* const hashMap = new HashMap<number, string>();
|
|
43
|
+
* hashMap.set(1, 'A');
|
|
44
|
+
* hashMap.set(2, 'B');
|
|
45
|
+
* hashMap.set(1, 'C'); // Update value for key 1
|
|
46
|
+
*
|
|
47
|
+
* console.log(hashMap.size); // 2
|
|
48
|
+
* console.log(hashMap.get(1)); // 'C'
|
|
49
|
+
* console.log(hashMap.get(2)); // 'B'
|
|
50
|
+
* @example
|
|
51
|
+
* // count occurrences of keys
|
|
52
|
+
* const data = [1, 2, 1, 3, 2, 1];
|
|
53
|
+
*
|
|
54
|
+
* const countMap = new HashMap<number, number>();
|
|
55
|
+
* for (const key of data) {
|
|
56
|
+
* countMap.set(key, (countMap.get(key) || 0) + 1);
|
|
57
|
+
* }
|
|
58
|
+
*
|
|
59
|
+
* console.log(countMap.get(1)); // 3
|
|
60
|
+
* console.log(countMap.get(2)); // 2
|
|
61
|
+
* console.log(countMap.get(3)); // 1
|
|
16
62
|
*/
|
|
17
63
|
export declare class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K, V> {
|
|
18
64
|
/**
|
|
@@ -9,6 +9,52 @@ const utils_1 = require("../../utils");
|
|
|
9
9
|
* 3. Unique Keys: Keys are unique.
|
|
10
10
|
* If you try to insert another entry with the same key, the new one will replace the old entry.
|
|
11
11
|
* 4. Unordered Collection: HashMap does not guarantee the order of entries, and the order may change over time.
|
|
12
|
+
* @example
|
|
13
|
+
* // should maintain insertion order
|
|
14
|
+
* const linkedHashMap = new LinkedHashMap<number, string>();
|
|
15
|
+
* linkedHashMap.set(1, 'A');
|
|
16
|
+
* linkedHashMap.set(2, 'B');
|
|
17
|
+
* linkedHashMap.set(3, 'C');
|
|
18
|
+
*
|
|
19
|
+
* const result = Array.from(linkedHashMap);
|
|
20
|
+
* console.log(result); // [
|
|
21
|
+
* // [1, 'A'],
|
|
22
|
+
* // [2, 'B'],
|
|
23
|
+
* // [3, 'C']
|
|
24
|
+
* // ]
|
|
25
|
+
* @example
|
|
26
|
+
* // fast lookup of values by key
|
|
27
|
+
* const hashMap = new HashMap<number, string>();
|
|
28
|
+
* hashMap.set(1, 'A');
|
|
29
|
+
* hashMap.set(2, 'B');
|
|
30
|
+
* hashMap.set(3, 'C');
|
|
31
|
+
*
|
|
32
|
+
* console.log(hashMap.get(1)); // 'A'
|
|
33
|
+
* console.log(hashMap.get(2)); // 'B'
|
|
34
|
+
* console.log(hashMap.get(3)); // 'C'
|
|
35
|
+
* console.log(hashMap.get(99)); // undefined
|
|
36
|
+
* @example
|
|
37
|
+
* // remove duplicates when adding multiple entries
|
|
38
|
+
* const hashMap = new HashMap<number, string>();
|
|
39
|
+
* hashMap.set(1, 'A');
|
|
40
|
+
* hashMap.set(2, 'B');
|
|
41
|
+
* hashMap.set(1, 'C'); // Update value for key 1
|
|
42
|
+
*
|
|
43
|
+
* console.log(hashMap.size); // 2
|
|
44
|
+
* console.log(hashMap.get(1)); // 'C'
|
|
45
|
+
* console.log(hashMap.get(2)); // 'B'
|
|
46
|
+
* @example
|
|
47
|
+
* // count occurrences of keys
|
|
48
|
+
* const data = [1, 2, 1, 3, 2, 1];
|
|
49
|
+
*
|
|
50
|
+
* const countMap = new HashMap<number, number>();
|
|
51
|
+
* for (const key of data) {
|
|
52
|
+
* countMap.set(key, (countMap.get(key) || 0) + 1);
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* console.log(countMap.get(1)); // 3
|
|
56
|
+
* console.log(countMap.get(2)); // 2
|
|
57
|
+
* console.log(countMap.get(3)); // 1
|
|
12
58
|
*/
|
|
13
59
|
class HashMap extends base_1.IterableEntryBase {
|
|
14
60
|
/**
|
|
@@ -25,6 +25,72 @@ export declare class SinglyLinkedListNode<E = any> extends LinkedListNode<E> {
|
|
|
25
25
|
* 4. High Efficiency in Insertion and Deletion: Adding or removing elements in a linked list does not require moving other elements, making these operations more efficient than in arrays.
|
|
26
26
|
* Caution: Although our linked list classes provide methods such as at, setAt, addAt, and indexOf that are based on array indices, their time complexity, like that of the native Array.lastIndexOf, is 𝑂(𝑛). If you need to use these methods frequently, you might want to consider other data structures, such as Deque or Queue (designed for random access). Similarly, since the native Array.shift method has a time complexity of 𝑂(𝑛), using an array to simulate a queue can be inefficient. In such cases, you should use Queue or Deque, as these data structures leverage deferred array rearrangement, effectively reducing the average time complexity to 𝑂(1).
|
|
27
27
|
*
|
|
28
|
+
* @example
|
|
29
|
+
* // implementation of a basic text editor
|
|
30
|
+
* class TextEditor {
|
|
31
|
+
* private content: SinglyLinkedList<string>;
|
|
32
|
+
* private cursorIndex: number;
|
|
33
|
+
* private undoStack: Stack<{ operation: string; data?: any }>;
|
|
34
|
+
*
|
|
35
|
+
* constructor() {
|
|
36
|
+
* this.content = new SinglyLinkedList<string>();
|
|
37
|
+
* this.cursorIndex = 0; // Cursor starts at the beginning
|
|
38
|
+
* this.undoStack = new Stack<{ operation: string; data?: any }>(); // Stack to keep track of operations for undo
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* insert(char: string) {
|
|
42
|
+
* this.content.addAt(this.cursorIndex, char);
|
|
43
|
+
* this.cursorIndex++;
|
|
44
|
+
* this.undoStack.push({ operation: 'insert', data: { index: this.cursorIndex - 1 } });
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* delete() {
|
|
48
|
+
* if (this.cursorIndex === 0) return; // Nothing to delete
|
|
49
|
+
* const deleted = this.content.deleteAt(this.cursorIndex - 1);
|
|
50
|
+
* this.cursorIndex--;
|
|
51
|
+
* this.undoStack.push({ operation: 'delete', data: { index: this.cursorIndex, char: deleted } });
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* moveCursor(index: number) {
|
|
55
|
+
* this.cursorIndex = Math.max(0, Math.min(index, this.content.length));
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
* undo() {
|
|
59
|
+
* if (this.undoStack.size === 0) return; // No operations to undo
|
|
60
|
+
* const lastAction = this.undoStack.pop();
|
|
61
|
+
*
|
|
62
|
+
* if (lastAction!.operation === 'insert') {
|
|
63
|
+
* this.content.deleteAt(lastAction!.data.index);
|
|
64
|
+
* this.cursorIndex = lastAction!.data.index;
|
|
65
|
+
* } else if (lastAction!.operation === 'delete') {
|
|
66
|
+
* this.content.addAt(lastAction!.data.index, lastAction!.data.char);
|
|
67
|
+
* this.cursorIndex = lastAction!.data.index + 1;
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* getText(): string {
|
|
72
|
+
* return [...this.content].join('');
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* // Example Usage
|
|
77
|
+
* const editor = new TextEditor();
|
|
78
|
+
* editor.insert('H');
|
|
79
|
+
* editor.insert('e');
|
|
80
|
+
* editor.insert('l');
|
|
81
|
+
* editor.insert('l');
|
|
82
|
+
* editor.insert('o');
|
|
83
|
+
* console.log(editor.getText()); // 'Hello' // Output: "Hello"
|
|
84
|
+
*
|
|
85
|
+
* editor.delete();
|
|
86
|
+
* console.log(editor.getText()); // 'Hell' // Output: "Hell"
|
|
87
|
+
*
|
|
88
|
+
* editor.undo();
|
|
89
|
+
* console.log(editor.getText()); // 'Hello' // Output: "Hello"
|
|
90
|
+
*
|
|
91
|
+
* editor.moveCursor(1);
|
|
92
|
+
* editor.insert('a');
|
|
93
|
+
* console.log(editor.getText()); // 'Haello'
|
|
28
94
|
*/
|
|
29
95
|
export declare class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, SinglyLinkedListNode<E>> {
|
|
30
96
|
constructor(elements?: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>>, options?: SinglyLinkedListOptions<E, R>);
|
|
@@ -28,6 +28,72 @@ exports.SinglyLinkedListNode = SinglyLinkedListNode;
|
|
|
28
28
|
* 4. High Efficiency in Insertion and Deletion: Adding or removing elements in a linked list does not require moving other elements, making these operations more efficient than in arrays.
|
|
29
29
|
* Caution: Although our linked list classes provide methods such as at, setAt, addAt, and indexOf that are based on array indices, their time complexity, like that of the native Array.lastIndexOf, is 𝑂(𝑛). If you need to use these methods frequently, you might want to consider other data structures, such as Deque or Queue (designed for random access). Similarly, since the native Array.shift method has a time complexity of 𝑂(𝑛), using an array to simulate a queue can be inefficient. In such cases, you should use Queue or Deque, as these data structures leverage deferred array rearrangement, effectively reducing the average time complexity to 𝑂(1).
|
|
30
30
|
*
|
|
31
|
+
* @example
|
|
32
|
+
* // implementation of a basic text editor
|
|
33
|
+
* class TextEditor {
|
|
34
|
+
* private content: SinglyLinkedList<string>;
|
|
35
|
+
* private cursorIndex: number;
|
|
36
|
+
* private undoStack: Stack<{ operation: string; data?: any }>;
|
|
37
|
+
*
|
|
38
|
+
* constructor() {
|
|
39
|
+
* this.content = new SinglyLinkedList<string>();
|
|
40
|
+
* this.cursorIndex = 0; // Cursor starts at the beginning
|
|
41
|
+
* this.undoStack = new Stack<{ operation: string; data?: any }>(); // Stack to keep track of operations for undo
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* insert(char: string) {
|
|
45
|
+
* this.content.addAt(this.cursorIndex, char);
|
|
46
|
+
* this.cursorIndex++;
|
|
47
|
+
* this.undoStack.push({ operation: 'insert', data: { index: this.cursorIndex - 1 } });
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* delete() {
|
|
51
|
+
* if (this.cursorIndex === 0) return; // Nothing to delete
|
|
52
|
+
* const deleted = this.content.deleteAt(this.cursorIndex - 1);
|
|
53
|
+
* this.cursorIndex--;
|
|
54
|
+
* this.undoStack.push({ operation: 'delete', data: { index: this.cursorIndex, char: deleted } });
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* moveCursor(index: number) {
|
|
58
|
+
* this.cursorIndex = Math.max(0, Math.min(index, this.content.length));
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* undo() {
|
|
62
|
+
* if (this.undoStack.size === 0) return; // No operations to undo
|
|
63
|
+
* const lastAction = this.undoStack.pop();
|
|
64
|
+
*
|
|
65
|
+
* if (lastAction!.operation === 'insert') {
|
|
66
|
+
* this.content.deleteAt(lastAction!.data.index);
|
|
67
|
+
* this.cursorIndex = lastAction!.data.index;
|
|
68
|
+
* } else if (lastAction!.operation === 'delete') {
|
|
69
|
+
* this.content.addAt(lastAction!.data.index, lastAction!.data.char);
|
|
70
|
+
* this.cursorIndex = lastAction!.data.index + 1;
|
|
71
|
+
* }
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* getText(): string {
|
|
75
|
+
* return [...this.content].join('');
|
|
76
|
+
* }
|
|
77
|
+
* }
|
|
78
|
+
*
|
|
79
|
+
* // Example Usage
|
|
80
|
+
* const editor = new TextEditor();
|
|
81
|
+
* editor.insert('H');
|
|
82
|
+
* editor.insert('e');
|
|
83
|
+
* editor.insert('l');
|
|
84
|
+
* editor.insert('l');
|
|
85
|
+
* editor.insert('o');
|
|
86
|
+
* console.log(editor.getText()); // 'Hello' // Output: "Hello"
|
|
87
|
+
*
|
|
88
|
+
* editor.delete();
|
|
89
|
+
* console.log(editor.getText()); // 'Hell' // Output: "Hell"
|
|
90
|
+
*
|
|
91
|
+
* editor.undo();
|
|
92
|
+
* console.log(editor.getText()); // 'Hello' // Output: "Hello"
|
|
93
|
+
*
|
|
94
|
+
* editor.moveCursor(1);
|
|
95
|
+
* editor.insert('a');
|
|
96
|
+
* console.log(editor.getText()); // 'Haello'
|
|
31
97
|
*/
|
|
32
98
|
class SinglyLinkedList extends linear_base_1.LinearLinkedBase {
|
|
33
99
|
constructor(elements = [], options) {
|
|
@@ -14,6 +14,53 @@ import { LinearBase } from '../base/linear-base';
|
|
|
14
14
|
* 5. Data Buffering: Acting as a buffer for data packets in network communication.
|
|
15
15
|
* 6. Breadth-First Search (BFS): In traversal algorithms for graphs and trees, queues store elements that are to be visited.
|
|
16
16
|
* 7. Real-time Queuing: Like queuing systems in banks or supermarkets.
|
|
17
|
+
* @example
|
|
18
|
+
* // Sliding Window using Queue
|
|
19
|
+
* const nums = [2, 3, 4, 1, 5];
|
|
20
|
+
* const k = 2;
|
|
21
|
+
* const queue = new Queue<number>();
|
|
22
|
+
*
|
|
23
|
+
* let maxSum = 0;
|
|
24
|
+
* let currentSum = 0;
|
|
25
|
+
*
|
|
26
|
+
* nums.forEach((num) => {
|
|
27
|
+
* queue.push(num);
|
|
28
|
+
* currentSum += num;
|
|
29
|
+
*
|
|
30
|
+
* if (queue.length > k) {
|
|
31
|
+
* currentSum -= queue.shift()!;
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* if (queue.length === k) {
|
|
35
|
+
* maxSum = Math.max(maxSum, currentSum);
|
|
36
|
+
* }
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* console.log(maxSum); // 7
|
|
40
|
+
* @example
|
|
41
|
+
* // Breadth-First Search (BFS) using Queue
|
|
42
|
+
* const graph: { [key in number]: number[] } = {
|
|
43
|
+
* 1: [2, 3],
|
|
44
|
+
* 2: [4, 5],
|
|
45
|
+
* 3: [],
|
|
46
|
+
* 4: [],
|
|
47
|
+
* 5: []
|
|
48
|
+
* };
|
|
49
|
+
*
|
|
50
|
+
* const queue = new Queue<number>();
|
|
51
|
+
* const visited: number[] = [];
|
|
52
|
+
*
|
|
53
|
+
* queue.push(1);
|
|
54
|
+
*
|
|
55
|
+
* while (!queue.isEmpty()) {
|
|
56
|
+
* const node = queue.shift()!;
|
|
57
|
+
* if (!visited.includes(node)) {
|
|
58
|
+
* visited.push(node);
|
|
59
|
+
* graph[node].forEach(neighbor => queue.push(neighbor));
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* console.log(visited); // [1, 2, 3, 4, 5]
|
|
17
64
|
*/
|
|
18
65
|
export declare class Queue<E = any, R = any> extends LinearBase<E, R> {
|
|
19
66
|
constructor(elements?: Iterable<E> | Iterable<R>, options?: QueueOptions<E, R>);
|
|
@@ -11,6 +11,53 @@ const linear_base_1 = require("../base/linear-base");
|
|
|
11
11
|
* 5. Data Buffering: Acting as a buffer for data packets in network communication.
|
|
12
12
|
* 6. Breadth-First Search (BFS): In traversal algorithms for graphs and trees, queues store elements that are to be visited.
|
|
13
13
|
* 7. Real-time Queuing: Like queuing systems in banks or supermarkets.
|
|
14
|
+
* @example
|
|
15
|
+
* // Sliding Window using Queue
|
|
16
|
+
* const nums = [2, 3, 4, 1, 5];
|
|
17
|
+
* const k = 2;
|
|
18
|
+
* const queue = new Queue<number>();
|
|
19
|
+
*
|
|
20
|
+
* let maxSum = 0;
|
|
21
|
+
* let currentSum = 0;
|
|
22
|
+
*
|
|
23
|
+
* nums.forEach((num) => {
|
|
24
|
+
* queue.push(num);
|
|
25
|
+
* currentSum += num;
|
|
26
|
+
*
|
|
27
|
+
* if (queue.length > k) {
|
|
28
|
+
* currentSum -= queue.shift()!;
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* if (queue.length === k) {
|
|
32
|
+
* maxSum = Math.max(maxSum, currentSum);
|
|
33
|
+
* }
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* console.log(maxSum); // 7
|
|
37
|
+
* @example
|
|
38
|
+
* // Breadth-First Search (BFS) using Queue
|
|
39
|
+
* const graph: { [key in number]: number[] } = {
|
|
40
|
+
* 1: [2, 3],
|
|
41
|
+
* 2: [4, 5],
|
|
42
|
+
* 3: [],
|
|
43
|
+
* 4: [],
|
|
44
|
+
* 5: []
|
|
45
|
+
* };
|
|
46
|
+
*
|
|
47
|
+
* const queue = new Queue<number>();
|
|
48
|
+
* const visited: number[] = [];
|
|
49
|
+
*
|
|
50
|
+
* queue.push(1);
|
|
51
|
+
*
|
|
52
|
+
* while (!queue.isEmpty()) {
|
|
53
|
+
* const node = queue.shift()!;
|
|
54
|
+
* if (!visited.includes(node)) {
|
|
55
|
+
* visited.push(node);
|
|
56
|
+
* graph[node].forEach(neighbor => queue.push(neighbor));
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* console.log(visited); // [1, 2, 3, 4, 5]
|
|
14
61
|
*/
|
|
15
62
|
class Queue extends linear_base_1.LinearBase {
|
|
16
63
|
constructor(elements = [], options) {
|
|
@@ -14,6 +14,127 @@ import { IterableElementBase } from '../base';
|
|
|
14
14
|
* 4. Function Calls: In most modern programming languages, the records of function calls are managed through a stack. When a function is called, its record (including parameters, local variables, and return address) is 'pushed' into the stack. When the function returns, its record is 'popped' from the stack.
|
|
15
15
|
* 5. Expression Evaluation: Used for the evaluation of arithmetic or logical expressions, especially when dealing with parenthesis matching and operator precedence.
|
|
16
16
|
* 6. Backtracking Algorithms: In problems where multiple branches need to be explored but only one branch can be explored at a time, stacks can be used to save the state at each branching point.
|
|
17
|
+
* @example
|
|
18
|
+
* // Balanced Parentheses or Brackets
|
|
19
|
+
* type ValidCharacters = ')' | '(' | ']' | '[' | '}' | '{';
|
|
20
|
+
*
|
|
21
|
+
* const stack = new Stack<string>();
|
|
22
|
+
* const input: ValidCharacters[] = '[({})]'.split('') as ValidCharacters[];
|
|
23
|
+
* const matches: { [key in ValidCharacters]?: ValidCharacters } = { ')': '(', ']': '[', '}': '{' };
|
|
24
|
+
* for (const char of input) {
|
|
25
|
+
* if ('([{'.includes(char)) {
|
|
26
|
+
* stack.push(char);
|
|
27
|
+
* } else if (')]}'.includes(char)) {
|
|
28
|
+
* if (stack.pop() !== matches[char]) {
|
|
29
|
+
* fail('Parentheses are not balanced');
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* console.log(stack.isEmpty()); // true
|
|
34
|
+
* @example
|
|
35
|
+
* // Expression Evaluation and Conversion
|
|
36
|
+
* const stack = new Stack<number>();
|
|
37
|
+
* const expression = [5, 3, '+']; // Equivalent to 5 + 3
|
|
38
|
+
* expression.forEach(token => {
|
|
39
|
+
* if (typeof token === 'number') {
|
|
40
|
+
* stack.push(token);
|
|
41
|
+
* } else {
|
|
42
|
+
* const b = stack.pop()!;
|
|
43
|
+
* const a = stack.pop()!;
|
|
44
|
+
* stack.push(token === '+' ? a + b : 0); // Only handling '+' here
|
|
45
|
+
* }
|
|
46
|
+
* });
|
|
47
|
+
* console.log(stack.pop()); // 8
|
|
48
|
+
* @example
|
|
49
|
+
* // Depth-First Search (DFS)
|
|
50
|
+
* const stack = new Stack<number>();
|
|
51
|
+
* const graph: { [key in number]: number[] } = { 1: [2, 3], 2: [4], 3: [5], 4: [], 5: [] };
|
|
52
|
+
* const visited: number[] = [];
|
|
53
|
+
* stack.push(1);
|
|
54
|
+
* while (!stack.isEmpty()) {
|
|
55
|
+
* const node = stack.pop()!;
|
|
56
|
+
* if (!visited.includes(node)) {
|
|
57
|
+
* visited.push(node);
|
|
58
|
+
* graph[node].forEach(neighbor => stack.push(neighbor));
|
|
59
|
+
* }
|
|
60
|
+
* }
|
|
61
|
+
* console.log(visited); // [1, 3, 5, 2, 4]
|
|
62
|
+
* @example
|
|
63
|
+
* // Backtracking Algorithms
|
|
64
|
+
* const stack = new Stack<[number, number]>();
|
|
65
|
+
* const maze = [
|
|
66
|
+
* ['S', ' ', 'X'],
|
|
67
|
+
* ['X', ' ', 'X'],
|
|
68
|
+
* [' ', ' ', 'E']
|
|
69
|
+
* ];
|
|
70
|
+
* const start: [number, number] = [0, 0];
|
|
71
|
+
* const end = [2, 2];
|
|
72
|
+
* const directions = [
|
|
73
|
+
* [0, 1], // To the right
|
|
74
|
+
* [1, 0], // down
|
|
75
|
+
* [0, -1], // left
|
|
76
|
+
* [-1, 0] // up
|
|
77
|
+
* ];
|
|
78
|
+
*
|
|
79
|
+
* const visited = new Set<string>(); // Used to record visited nodes
|
|
80
|
+
* stack.push(start);
|
|
81
|
+
* const path: number[][] = [];
|
|
82
|
+
*
|
|
83
|
+
* while (!stack.isEmpty()) {
|
|
84
|
+
* const [x, y] = stack.pop()!;
|
|
85
|
+
* if (visited.has(`${x},${y}`)) continue; // Skip already visited nodes
|
|
86
|
+
* visited.add(`${x},${y}`);
|
|
87
|
+
*
|
|
88
|
+
* path.push([x, y]);
|
|
89
|
+
*
|
|
90
|
+
* if (x === end[0] && y === end[1]) {
|
|
91
|
+
* break; // Find the end point and exit
|
|
92
|
+
* }
|
|
93
|
+
*
|
|
94
|
+
* for (const [dx, dy] of directions) {
|
|
95
|
+
* const nx = x + dx;
|
|
96
|
+
* const ny = y + dy;
|
|
97
|
+
* if (
|
|
98
|
+
* maze[nx]?.[ny] === ' ' || // feasible path
|
|
99
|
+
* maze[nx]?.[ny] === 'E' // destination
|
|
100
|
+
* ) {
|
|
101
|
+
* stack.push([nx, ny]);
|
|
102
|
+
* }
|
|
103
|
+
* }
|
|
104
|
+
* }
|
|
105
|
+
*
|
|
106
|
+
* expect(path).toContainEqual(end);
|
|
107
|
+
* @example
|
|
108
|
+
* // Function Call Stack
|
|
109
|
+
* const functionStack = new Stack<string>();
|
|
110
|
+
* functionStack.push('main');
|
|
111
|
+
* functionStack.push('foo');
|
|
112
|
+
* functionStack.push('bar');
|
|
113
|
+
* console.log(functionStack.pop()); // 'bar'
|
|
114
|
+
* console.log(functionStack.pop()); // 'foo'
|
|
115
|
+
* console.log(functionStack.pop()); // 'main'
|
|
116
|
+
* @example
|
|
117
|
+
* // Simplify File Paths
|
|
118
|
+
* const stack = new Stack<string>();
|
|
119
|
+
* const path = '/a/./b/../../c';
|
|
120
|
+
* path.split('/').forEach(segment => {
|
|
121
|
+
* if (segment === '..') stack.pop();
|
|
122
|
+
* else if (segment && segment !== '.') stack.push(segment);
|
|
123
|
+
* });
|
|
124
|
+
* console.log(stack.elements.join('/')); // 'c'
|
|
125
|
+
* @example
|
|
126
|
+
* // Stock Span Problem
|
|
127
|
+
* const stack = new Stack<number>();
|
|
128
|
+
* const prices = [100, 80, 60, 70, 60, 75, 85];
|
|
129
|
+
* const spans: number[] = [];
|
|
130
|
+
* prices.forEach((price, i) => {
|
|
131
|
+
* while (!stack.isEmpty() && prices[stack.peek()!] <= price) {
|
|
132
|
+
* stack.pop();
|
|
133
|
+
* }
|
|
134
|
+
* spans.push(stack.isEmpty() ? i + 1 : i - stack.peek()!);
|
|
135
|
+
* stack.push(i);
|
|
136
|
+
* });
|
|
137
|
+
* console.log(spans); // [1, 1, 1, 2, 1, 4, 6]
|
|
17
138
|
*/
|
|
18
139
|
export declare class Stack<E = any, R = any> extends IterableElementBase<E, R> {
|
|
19
140
|
constructor(elements?: Iterable<E> | Iterable<R>, options?: StackOptions<E, R>);
|
|
@@ -9,6 +9,127 @@ const base_1 = require("../base");
|
|
|
9
9
|
* 4. Function Calls: In most modern programming languages, the records of function calls are managed through a stack. When a function is called, its record (including parameters, local variables, and return address) is 'pushed' into the stack. When the function returns, its record is 'popped' from the stack.
|
|
10
10
|
* 5. Expression Evaluation: Used for the evaluation of arithmetic or logical expressions, especially when dealing with parenthesis matching and operator precedence.
|
|
11
11
|
* 6. Backtracking Algorithms: In problems where multiple branches need to be explored but only one branch can be explored at a time, stacks can be used to save the state at each branching point.
|
|
12
|
+
* @example
|
|
13
|
+
* // Balanced Parentheses or Brackets
|
|
14
|
+
* type ValidCharacters = ')' | '(' | ']' | '[' | '}' | '{';
|
|
15
|
+
*
|
|
16
|
+
* const stack = new Stack<string>();
|
|
17
|
+
* const input: ValidCharacters[] = '[({})]'.split('') as ValidCharacters[];
|
|
18
|
+
* const matches: { [key in ValidCharacters]?: ValidCharacters } = { ')': '(', ']': '[', '}': '{' };
|
|
19
|
+
* for (const char of input) {
|
|
20
|
+
* if ('([{'.includes(char)) {
|
|
21
|
+
* stack.push(char);
|
|
22
|
+
* } else if (')]}'.includes(char)) {
|
|
23
|
+
* if (stack.pop() !== matches[char]) {
|
|
24
|
+
* fail('Parentheses are not balanced');
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* console.log(stack.isEmpty()); // true
|
|
29
|
+
* @example
|
|
30
|
+
* // Expression Evaluation and Conversion
|
|
31
|
+
* const stack = new Stack<number>();
|
|
32
|
+
* const expression = [5, 3, '+']; // Equivalent to 5 + 3
|
|
33
|
+
* expression.forEach(token => {
|
|
34
|
+
* if (typeof token === 'number') {
|
|
35
|
+
* stack.push(token);
|
|
36
|
+
* } else {
|
|
37
|
+
* const b = stack.pop()!;
|
|
38
|
+
* const a = stack.pop()!;
|
|
39
|
+
* stack.push(token === '+' ? a + b : 0); // Only handling '+' here
|
|
40
|
+
* }
|
|
41
|
+
* });
|
|
42
|
+
* console.log(stack.pop()); // 8
|
|
43
|
+
* @example
|
|
44
|
+
* // Depth-First Search (DFS)
|
|
45
|
+
* const stack = new Stack<number>();
|
|
46
|
+
* const graph: { [key in number]: number[] } = { 1: [2, 3], 2: [4], 3: [5], 4: [], 5: [] };
|
|
47
|
+
* const visited: number[] = [];
|
|
48
|
+
* stack.push(1);
|
|
49
|
+
* while (!stack.isEmpty()) {
|
|
50
|
+
* const node = stack.pop()!;
|
|
51
|
+
* if (!visited.includes(node)) {
|
|
52
|
+
* visited.push(node);
|
|
53
|
+
* graph[node].forEach(neighbor => stack.push(neighbor));
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* console.log(visited); // [1, 3, 5, 2, 4]
|
|
57
|
+
* @example
|
|
58
|
+
* // Backtracking Algorithms
|
|
59
|
+
* const stack = new Stack<[number, number]>();
|
|
60
|
+
* const maze = [
|
|
61
|
+
* ['S', ' ', 'X'],
|
|
62
|
+
* ['X', ' ', 'X'],
|
|
63
|
+
* [' ', ' ', 'E']
|
|
64
|
+
* ];
|
|
65
|
+
* const start: [number, number] = [0, 0];
|
|
66
|
+
* const end = [2, 2];
|
|
67
|
+
* const directions = [
|
|
68
|
+
* [0, 1], // To the right
|
|
69
|
+
* [1, 0], // down
|
|
70
|
+
* [0, -1], // left
|
|
71
|
+
* [-1, 0] // up
|
|
72
|
+
* ];
|
|
73
|
+
*
|
|
74
|
+
* const visited = new Set<string>(); // Used to record visited nodes
|
|
75
|
+
* stack.push(start);
|
|
76
|
+
* const path: number[][] = [];
|
|
77
|
+
*
|
|
78
|
+
* while (!stack.isEmpty()) {
|
|
79
|
+
* const [x, y] = stack.pop()!;
|
|
80
|
+
* if (visited.has(`${x},${y}`)) continue; // Skip already visited nodes
|
|
81
|
+
* visited.add(`${x},${y}`);
|
|
82
|
+
*
|
|
83
|
+
* path.push([x, y]);
|
|
84
|
+
*
|
|
85
|
+
* if (x === end[0] && y === end[1]) {
|
|
86
|
+
* break; // Find the end point and exit
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* for (const [dx, dy] of directions) {
|
|
90
|
+
* const nx = x + dx;
|
|
91
|
+
* const ny = y + dy;
|
|
92
|
+
* if (
|
|
93
|
+
* maze[nx]?.[ny] === ' ' || // feasible path
|
|
94
|
+
* maze[nx]?.[ny] === 'E' // destination
|
|
95
|
+
* ) {
|
|
96
|
+
* stack.push([nx, ny]);
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* expect(path).toContainEqual(end);
|
|
102
|
+
* @example
|
|
103
|
+
* // Function Call Stack
|
|
104
|
+
* const functionStack = new Stack<string>();
|
|
105
|
+
* functionStack.push('main');
|
|
106
|
+
* functionStack.push('foo');
|
|
107
|
+
* functionStack.push('bar');
|
|
108
|
+
* console.log(functionStack.pop()); // 'bar'
|
|
109
|
+
* console.log(functionStack.pop()); // 'foo'
|
|
110
|
+
* console.log(functionStack.pop()); // 'main'
|
|
111
|
+
* @example
|
|
112
|
+
* // Simplify File Paths
|
|
113
|
+
* const stack = new Stack<string>();
|
|
114
|
+
* const path = '/a/./b/../../c';
|
|
115
|
+
* path.split('/').forEach(segment => {
|
|
116
|
+
* if (segment === '..') stack.pop();
|
|
117
|
+
* else if (segment && segment !== '.') stack.push(segment);
|
|
118
|
+
* });
|
|
119
|
+
* console.log(stack.elements.join('/')); // 'c'
|
|
120
|
+
* @example
|
|
121
|
+
* // Stock Span Problem
|
|
122
|
+
* const stack = new Stack<number>();
|
|
123
|
+
* const prices = [100, 80, 60, 70, 60, 75, 85];
|
|
124
|
+
* const spans: number[] = [];
|
|
125
|
+
* prices.forEach((price, i) => {
|
|
126
|
+
* while (!stack.isEmpty() && prices[stack.peek()!] <= price) {
|
|
127
|
+
* stack.pop();
|
|
128
|
+
* }
|
|
129
|
+
* spans.push(stack.isEmpty() ? i + 1 : i - stack.peek()!);
|
|
130
|
+
* stack.push(i);
|
|
131
|
+
* });
|
|
132
|
+
* console.log(spans); // [1, 1, 1, 2, 1, 4, 6]
|
|
12
133
|
*/
|
|
13
134
|
class Stack extends base_1.IterableElementBase {
|
|
14
135
|
constructor(elements = [], options) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bst-typed",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.3",
|
|
4
|
+
"description": "Binary Search Tree",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "rm -rf dist && npx tsc",
|
|
@@ -144,6 +144,6 @@
|
|
|
144
144
|
"typescript": "^4.9.5"
|
|
145
145
|
},
|
|
146
146
|
"dependencies": {
|
|
147
|
-
"data-structure-typed": "^2.0.
|
|
147
|
+
"data-structure-typed": "^2.0.3"
|
|
148
148
|
}
|
|
149
149
|
}
|
|
@@ -339,7 +339,7 @@ export abstract class AbstractGraph<
|
|
|
339
339
|
|
|
340
340
|
if (isWeight) {
|
|
341
341
|
const allPaths = this.getAllPathsBetween(v1, v2);
|
|
342
|
-
let min =
|
|
342
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
343
343
|
for (const path of allPaths) {
|
|
344
344
|
min = Math.min(this.getPathSumWeight(path), min);
|
|
345
345
|
}
|
|
@@ -404,7 +404,7 @@ export abstract class AbstractGraph<
|
|
|
404
404
|
if (isWeight) {
|
|
405
405
|
if (isDFS) {
|
|
406
406
|
const allPaths = this.getAllPathsBetween(v1, v2, 10000);
|
|
407
|
-
let min =
|
|
407
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
408
408
|
let minIndex = -1;
|
|
409
409
|
let index = 0;
|
|
410
410
|
for (const path of allPaths) {
|
|
@@ -475,7 +475,7 @@ export abstract class AbstractGraph<
|
|
|
475
475
|
getMinDist: boolean = false,
|
|
476
476
|
genPaths: boolean = false
|
|
477
477
|
): DijkstraResult<VO> {
|
|
478
|
-
let minDist =
|
|
478
|
+
let minDist = Number.MAX_SAFE_INTEGER;
|
|
479
479
|
let minDest: VO | undefined = undefined;
|
|
480
480
|
let minPath: VO[] = [];
|
|
481
481
|
const paths: VO[][] = [];
|
|
@@ -494,13 +494,13 @@ export abstract class AbstractGraph<
|
|
|
494
494
|
|
|
495
495
|
for (const vertex of vertexMap) {
|
|
496
496
|
const vertexOrKey = vertex[1];
|
|
497
|
-
if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey,
|
|
497
|
+
if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Number.MAX_SAFE_INTEGER);
|
|
498
498
|
}
|
|
499
499
|
distMap.set(srcVertex, 0);
|
|
500
500
|
preMap.set(srcVertex, undefined);
|
|
501
501
|
|
|
502
502
|
const getMinOfNoSeen = () => {
|
|
503
|
-
let min =
|
|
503
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
504
504
|
let minV: VO | undefined = undefined;
|
|
505
505
|
for (const [key, value] of distMap) {
|
|
506
506
|
if (!seen.has(key)) {
|
|
@@ -537,7 +537,7 @@ export abstract class AbstractGraph<
|
|
|
537
537
|
seen.add(cur);
|
|
538
538
|
if (destVertex && destVertex === cur) {
|
|
539
539
|
if (getMinDist) {
|
|
540
|
-
minDist = distMap.get(destVertex) ||
|
|
540
|
+
minDist = distMap.get(destVertex) || Number.MAX_SAFE_INTEGER;
|
|
541
541
|
}
|
|
542
542
|
if (genPaths) {
|
|
543
543
|
getPaths(destVertex);
|
|
@@ -605,7 +605,7 @@ export abstract class AbstractGraph<
|
|
|
605
605
|
getMinDist: boolean = false,
|
|
606
606
|
genPaths: boolean = false
|
|
607
607
|
): DijkstraResult<VO> {
|
|
608
|
-
let minDist =
|
|
608
|
+
let minDist = Number.MAX_SAFE_INTEGER;
|
|
609
609
|
let minDest: VO | undefined = undefined;
|
|
610
610
|
let minPath: VO[] = [];
|
|
611
611
|
const paths: VO[][] = [];
|
|
@@ -621,7 +621,7 @@ export abstract class AbstractGraph<
|
|
|
621
621
|
|
|
622
622
|
for (const vertex of vertexMap) {
|
|
623
623
|
const vertexOrKey = vertex[1];
|
|
624
|
-
if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey,
|
|
624
|
+
if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Number.MAX_SAFE_INTEGER);
|
|
625
625
|
}
|
|
626
626
|
|
|
627
627
|
const heap = new Heap<{ key: number; value: VO }>([], { comparator: (a, b) => a.key - b.key });
|
|
@@ -661,7 +661,7 @@ export abstract class AbstractGraph<
|
|
|
661
661
|
seen.add(cur);
|
|
662
662
|
if (destVertex && destVertex === cur) {
|
|
663
663
|
if (getMinDist) {
|
|
664
|
-
minDist = distMap.get(destVertex) ||
|
|
664
|
+
minDist = distMap.get(destVertex) || Number.MAX_SAFE_INTEGER;
|
|
665
665
|
}
|
|
666
666
|
if (genPaths) {
|
|
667
667
|
getPaths(destVertex);
|
|
@@ -732,7 +732,7 @@ export abstract class AbstractGraph<
|
|
|
732
732
|
const paths: VO[][] = [];
|
|
733
733
|
const distMap: Map<VO, number> = new Map();
|
|
734
734
|
const preMap: Map<VO, VO> = new Map(); // predecessor
|
|
735
|
-
let min =
|
|
735
|
+
let min = Number.MAX_SAFE_INTEGER;
|
|
736
736
|
let minPath: VO[] = [];
|
|
737
737
|
// TODO
|
|
738
738
|
let hasNegativeCycle: boolean | undefined;
|
|
@@ -745,7 +745,7 @@ export abstract class AbstractGraph<
|
|
|
745
745
|
const numOfEdges = edgeMap.length;
|
|
746
746
|
|
|
747
747
|
this._vertexMap.forEach(vertex => {
|
|
748
|
-
distMap.set(vertex,
|
|
748
|
+
distMap.set(vertex, Number.MAX_SAFE_INTEGER);
|
|
749
749
|
});
|
|
750
750
|
|
|
751
751
|
distMap.set(srcVertex, 0);
|
|
@@ -759,7 +759,7 @@ export abstract class AbstractGraph<
|
|
|
759
759
|
const sWeight = distMap.get(s);
|
|
760
760
|
const dWeight = distMap.get(d);
|
|
761
761
|
if (sWeight !== undefined && dWeight !== undefined) {
|
|
762
|
-
if (distMap.get(s) !==
|
|
762
|
+
if (distMap.get(s) !== Number.MAX_SAFE_INTEGER && sWeight + weight < dWeight) {
|
|
763
763
|
distMap.set(d, sWeight + weight);
|
|
764
764
|
if (genPath) preMap.set(d, s);
|
|
765
765
|
}
|
|
@@ -804,7 +804,7 @@ export abstract class AbstractGraph<
|
|
|
804
804
|
const weight = edgeMap[j].weight;
|
|
805
805
|
const sWeight = distMap.get(s);
|
|
806
806
|
if (sWeight) {
|
|
807
|
-
if (sWeight !==
|
|
807
|
+
if (sWeight !== Number.MAX_SAFE_INTEGER && sWeight + weight < sWeight) hasNegativeCycle = true;
|
|
808
808
|
}
|
|
809
809
|
}
|
|
810
810
|
}
|
|
@@ -860,7 +860,7 @@ export abstract class AbstractGraph<
|
|
|
860
860
|
|
|
861
861
|
for (let i = 0; i < n; i++) {
|
|
862
862
|
for (let j = 0; j < n; j++) {
|
|
863
|
-
costs[i][j] = this.getEdge(idAndVertices[i][1], idAndVertices[j][1])?.weight ||
|
|
863
|
+
costs[i][j] = this.getEdge(idAndVertices[i][1], idAndVertices[j][1])?.weight || Number.MAX_SAFE_INTEGER;
|
|
864
864
|
}
|
|
865
865
|
}
|
|
866
866
|
|
|
@@ -21,6 +21,52 @@ import { isWeakKey, rangeCheck } from '../../utils';
|
|
|
21
21
|
* 3. Unique Keys: Keys are unique.
|
|
22
22
|
* If you try to insert another entry with the same key, the new one will replace the old entry.
|
|
23
23
|
* 4. Unordered Collection: HashMap does not guarantee the order of entries, and the order may change over time.
|
|
24
|
+
* @example
|
|
25
|
+
* // should maintain insertion order
|
|
26
|
+
* const linkedHashMap = new LinkedHashMap<number, string>();
|
|
27
|
+
* linkedHashMap.set(1, 'A');
|
|
28
|
+
* linkedHashMap.set(2, 'B');
|
|
29
|
+
* linkedHashMap.set(3, 'C');
|
|
30
|
+
*
|
|
31
|
+
* const result = Array.from(linkedHashMap);
|
|
32
|
+
* console.log(result); // [
|
|
33
|
+
* // [1, 'A'],
|
|
34
|
+
* // [2, 'B'],
|
|
35
|
+
* // [3, 'C']
|
|
36
|
+
* // ]
|
|
37
|
+
* @example
|
|
38
|
+
* // fast lookup of values by key
|
|
39
|
+
* const hashMap = new HashMap<number, string>();
|
|
40
|
+
* hashMap.set(1, 'A');
|
|
41
|
+
* hashMap.set(2, 'B');
|
|
42
|
+
* hashMap.set(3, 'C');
|
|
43
|
+
*
|
|
44
|
+
* console.log(hashMap.get(1)); // 'A'
|
|
45
|
+
* console.log(hashMap.get(2)); // 'B'
|
|
46
|
+
* console.log(hashMap.get(3)); // 'C'
|
|
47
|
+
* console.log(hashMap.get(99)); // undefined
|
|
48
|
+
* @example
|
|
49
|
+
* // remove duplicates when adding multiple entries
|
|
50
|
+
* const hashMap = new HashMap<number, string>();
|
|
51
|
+
* hashMap.set(1, 'A');
|
|
52
|
+
* hashMap.set(2, 'B');
|
|
53
|
+
* hashMap.set(1, 'C'); // Update value for key 1
|
|
54
|
+
*
|
|
55
|
+
* console.log(hashMap.size); // 2
|
|
56
|
+
* console.log(hashMap.get(1)); // 'C'
|
|
57
|
+
* console.log(hashMap.get(2)); // 'B'
|
|
58
|
+
* @example
|
|
59
|
+
* // count occurrences of keys
|
|
60
|
+
* const data = [1, 2, 1, 3, 2, 1];
|
|
61
|
+
*
|
|
62
|
+
* const countMap = new HashMap<number, number>();
|
|
63
|
+
* for (const key of data) {
|
|
64
|
+
* countMap.set(key, (countMap.get(key) || 0) + 1);
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* console.log(countMap.get(1)); // 3
|
|
68
|
+
* console.log(countMap.get(2)); // 2
|
|
69
|
+
* console.log(countMap.get(3)); // 1
|
|
24
70
|
*/
|
|
25
71
|
export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K, V> {
|
|
26
72
|
/**
|
|
@@ -38,6 +38,72 @@ export class SinglyLinkedListNode<E = any> extends LinkedListNode<E> {
|
|
|
38
38
|
* 4. High Efficiency in Insertion and Deletion: Adding or removing elements in a linked list does not require moving other elements, making these operations more efficient than in arrays.
|
|
39
39
|
* Caution: Although our linked list classes provide methods such as at, setAt, addAt, and indexOf that are based on array indices, their time complexity, like that of the native Array.lastIndexOf, is 𝑂(𝑛). If you need to use these methods frequently, you might want to consider other data structures, such as Deque or Queue (designed for random access). Similarly, since the native Array.shift method has a time complexity of 𝑂(𝑛), using an array to simulate a queue can be inefficient. In such cases, you should use Queue or Deque, as these data structures leverage deferred array rearrangement, effectively reducing the average time complexity to 𝑂(1).
|
|
40
40
|
*
|
|
41
|
+
* @example
|
|
42
|
+
* // implementation of a basic text editor
|
|
43
|
+
* class TextEditor {
|
|
44
|
+
* private content: SinglyLinkedList<string>;
|
|
45
|
+
* private cursorIndex: number;
|
|
46
|
+
* private undoStack: Stack<{ operation: string; data?: any }>;
|
|
47
|
+
*
|
|
48
|
+
* constructor() {
|
|
49
|
+
* this.content = new SinglyLinkedList<string>();
|
|
50
|
+
* this.cursorIndex = 0; // Cursor starts at the beginning
|
|
51
|
+
* this.undoStack = new Stack<{ operation: string; data?: any }>(); // Stack to keep track of operations for undo
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* insert(char: string) {
|
|
55
|
+
* this.content.addAt(this.cursorIndex, char);
|
|
56
|
+
* this.cursorIndex++;
|
|
57
|
+
* this.undoStack.push({ operation: 'insert', data: { index: this.cursorIndex - 1 } });
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* delete() {
|
|
61
|
+
* if (this.cursorIndex === 0) return; // Nothing to delete
|
|
62
|
+
* const deleted = this.content.deleteAt(this.cursorIndex - 1);
|
|
63
|
+
* this.cursorIndex--;
|
|
64
|
+
* this.undoStack.push({ operation: 'delete', data: { index: this.cursorIndex, char: deleted } });
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* moveCursor(index: number) {
|
|
68
|
+
* this.cursorIndex = Math.max(0, Math.min(index, this.content.length));
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* undo() {
|
|
72
|
+
* if (this.undoStack.size === 0) return; // No operations to undo
|
|
73
|
+
* const lastAction = this.undoStack.pop();
|
|
74
|
+
*
|
|
75
|
+
* if (lastAction!.operation === 'insert') {
|
|
76
|
+
* this.content.deleteAt(lastAction!.data.index);
|
|
77
|
+
* this.cursorIndex = lastAction!.data.index;
|
|
78
|
+
* } else if (lastAction!.operation === 'delete') {
|
|
79
|
+
* this.content.addAt(lastAction!.data.index, lastAction!.data.char);
|
|
80
|
+
* this.cursorIndex = lastAction!.data.index + 1;
|
|
81
|
+
* }
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* getText(): string {
|
|
85
|
+
* return [...this.content].join('');
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* // Example Usage
|
|
90
|
+
* const editor = new TextEditor();
|
|
91
|
+
* editor.insert('H');
|
|
92
|
+
* editor.insert('e');
|
|
93
|
+
* editor.insert('l');
|
|
94
|
+
* editor.insert('l');
|
|
95
|
+
* editor.insert('o');
|
|
96
|
+
* console.log(editor.getText()); // 'Hello' // Output: "Hello"
|
|
97
|
+
*
|
|
98
|
+
* editor.delete();
|
|
99
|
+
* console.log(editor.getText()); // 'Hell' // Output: "Hell"
|
|
100
|
+
*
|
|
101
|
+
* editor.undo();
|
|
102
|
+
* console.log(editor.getText()); // 'Hello' // Output: "Hello"
|
|
103
|
+
*
|
|
104
|
+
* editor.moveCursor(1);
|
|
105
|
+
* editor.insert('a');
|
|
106
|
+
* console.log(editor.getText()); // 'Haello'
|
|
41
107
|
*/
|
|
42
108
|
export class SinglyLinkedList<E = any, R = any> extends LinearLinkedBase<E, R, SinglyLinkedListNode<E>> {
|
|
43
109
|
constructor(
|
|
@@ -15,6 +15,53 @@ import { LinearBase } from '../base/linear-base';
|
|
|
15
15
|
* 5. Data Buffering: Acting as a buffer for data packets in network communication.
|
|
16
16
|
* 6. Breadth-First Search (BFS): In traversal algorithms for graphs and trees, queues store elements that are to be visited.
|
|
17
17
|
* 7. Real-time Queuing: Like queuing systems in banks or supermarkets.
|
|
18
|
+
* @example
|
|
19
|
+
* // Sliding Window using Queue
|
|
20
|
+
* const nums = [2, 3, 4, 1, 5];
|
|
21
|
+
* const k = 2;
|
|
22
|
+
* const queue = new Queue<number>();
|
|
23
|
+
*
|
|
24
|
+
* let maxSum = 0;
|
|
25
|
+
* let currentSum = 0;
|
|
26
|
+
*
|
|
27
|
+
* nums.forEach((num) => {
|
|
28
|
+
* queue.push(num);
|
|
29
|
+
* currentSum += num;
|
|
30
|
+
*
|
|
31
|
+
* if (queue.length > k) {
|
|
32
|
+
* currentSum -= queue.shift()!;
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* if (queue.length === k) {
|
|
36
|
+
* maxSum = Math.max(maxSum, currentSum);
|
|
37
|
+
* }
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* console.log(maxSum); // 7
|
|
41
|
+
* @example
|
|
42
|
+
* // Breadth-First Search (BFS) using Queue
|
|
43
|
+
* const graph: { [key in number]: number[] } = {
|
|
44
|
+
* 1: [2, 3],
|
|
45
|
+
* 2: [4, 5],
|
|
46
|
+
* 3: [],
|
|
47
|
+
* 4: [],
|
|
48
|
+
* 5: []
|
|
49
|
+
* };
|
|
50
|
+
*
|
|
51
|
+
* const queue = new Queue<number>();
|
|
52
|
+
* const visited: number[] = [];
|
|
53
|
+
*
|
|
54
|
+
* queue.push(1);
|
|
55
|
+
*
|
|
56
|
+
* while (!queue.isEmpty()) {
|
|
57
|
+
* const node = queue.shift()!;
|
|
58
|
+
* if (!visited.includes(node)) {
|
|
59
|
+
* visited.push(node);
|
|
60
|
+
* graph[node].forEach(neighbor => queue.push(neighbor));
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* console.log(visited); // [1, 2, 3, 4, 5]
|
|
18
65
|
*/
|
|
19
66
|
export class Queue<E = any, R = any> extends LinearBase<E, R> {
|
|
20
67
|
constructor(elements: Iterable<E> | Iterable<R> = [], options?: QueueOptions<E, R>) {
|
|
@@ -15,6 +15,127 @@ import { IterableElementBase } from '../base';
|
|
|
15
15
|
* 4. Function Calls: In most modern programming languages, the records of function calls are managed through a stack. When a function is called, its record (including parameters, local variables, and return address) is 'pushed' into the stack. When the function returns, its record is 'popped' from the stack.
|
|
16
16
|
* 5. Expression Evaluation: Used for the evaluation of arithmetic or logical expressions, especially when dealing with parenthesis matching and operator precedence.
|
|
17
17
|
* 6. Backtracking Algorithms: In problems where multiple branches need to be explored but only one branch can be explored at a time, stacks can be used to save the state at each branching point.
|
|
18
|
+
* @example
|
|
19
|
+
* // Balanced Parentheses or Brackets
|
|
20
|
+
* type ValidCharacters = ')' | '(' | ']' | '[' | '}' | '{';
|
|
21
|
+
*
|
|
22
|
+
* const stack = new Stack<string>();
|
|
23
|
+
* const input: ValidCharacters[] = '[({})]'.split('') as ValidCharacters[];
|
|
24
|
+
* const matches: { [key in ValidCharacters]?: ValidCharacters } = { ')': '(', ']': '[', '}': '{' };
|
|
25
|
+
* for (const char of input) {
|
|
26
|
+
* if ('([{'.includes(char)) {
|
|
27
|
+
* stack.push(char);
|
|
28
|
+
* } else if (')]}'.includes(char)) {
|
|
29
|
+
* if (stack.pop() !== matches[char]) {
|
|
30
|
+
* fail('Parentheses are not balanced');
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* console.log(stack.isEmpty()); // true
|
|
35
|
+
* @example
|
|
36
|
+
* // Expression Evaluation and Conversion
|
|
37
|
+
* const stack = new Stack<number>();
|
|
38
|
+
* const expression = [5, 3, '+']; // Equivalent to 5 + 3
|
|
39
|
+
* expression.forEach(token => {
|
|
40
|
+
* if (typeof token === 'number') {
|
|
41
|
+
* stack.push(token);
|
|
42
|
+
* } else {
|
|
43
|
+
* const b = stack.pop()!;
|
|
44
|
+
* const a = stack.pop()!;
|
|
45
|
+
* stack.push(token === '+' ? a + b : 0); // Only handling '+' here
|
|
46
|
+
* }
|
|
47
|
+
* });
|
|
48
|
+
* console.log(stack.pop()); // 8
|
|
49
|
+
* @example
|
|
50
|
+
* // Depth-First Search (DFS)
|
|
51
|
+
* const stack = new Stack<number>();
|
|
52
|
+
* const graph: { [key in number]: number[] } = { 1: [2, 3], 2: [4], 3: [5], 4: [], 5: [] };
|
|
53
|
+
* const visited: number[] = [];
|
|
54
|
+
* stack.push(1);
|
|
55
|
+
* while (!stack.isEmpty()) {
|
|
56
|
+
* const node = stack.pop()!;
|
|
57
|
+
* if (!visited.includes(node)) {
|
|
58
|
+
* visited.push(node);
|
|
59
|
+
* graph[node].forEach(neighbor => stack.push(neighbor));
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
* console.log(visited); // [1, 3, 5, 2, 4]
|
|
63
|
+
* @example
|
|
64
|
+
* // Backtracking Algorithms
|
|
65
|
+
* const stack = new Stack<[number, number]>();
|
|
66
|
+
* const maze = [
|
|
67
|
+
* ['S', ' ', 'X'],
|
|
68
|
+
* ['X', ' ', 'X'],
|
|
69
|
+
* [' ', ' ', 'E']
|
|
70
|
+
* ];
|
|
71
|
+
* const start: [number, number] = [0, 0];
|
|
72
|
+
* const end = [2, 2];
|
|
73
|
+
* const directions = [
|
|
74
|
+
* [0, 1], // To the right
|
|
75
|
+
* [1, 0], // down
|
|
76
|
+
* [0, -1], // left
|
|
77
|
+
* [-1, 0] // up
|
|
78
|
+
* ];
|
|
79
|
+
*
|
|
80
|
+
* const visited = new Set<string>(); // Used to record visited nodes
|
|
81
|
+
* stack.push(start);
|
|
82
|
+
* const path: number[][] = [];
|
|
83
|
+
*
|
|
84
|
+
* while (!stack.isEmpty()) {
|
|
85
|
+
* const [x, y] = stack.pop()!;
|
|
86
|
+
* if (visited.has(`${x},${y}`)) continue; // Skip already visited nodes
|
|
87
|
+
* visited.add(`${x},${y}`);
|
|
88
|
+
*
|
|
89
|
+
* path.push([x, y]);
|
|
90
|
+
*
|
|
91
|
+
* if (x === end[0] && y === end[1]) {
|
|
92
|
+
* break; // Find the end point and exit
|
|
93
|
+
* }
|
|
94
|
+
*
|
|
95
|
+
* for (const [dx, dy] of directions) {
|
|
96
|
+
* const nx = x + dx;
|
|
97
|
+
* const ny = y + dy;
|
|
98
|
+
* if (
|
|
99
|
+
* maze[nx]?.[ny] === ' ' || // feasible path
|
|
100
|
+
* maze[nx]?.[ny] === 'E' // destination
|
|
101
|
+
* ) {
|
|
102
|
+
* stack.push([nx, ny]);
|
|
103
|
+
* }
|
|
104
|
+
* }
|
|
105
|
+
* }
|
|
106
|
+
*
|
|
107
|
+
* expect(path).toContainEqual(end);
|
|
108
|
+
* @example
|
|
109
|
+
* // Function Call Stack
|
|
110
|
+
* const functionStack = new Stack<string>();
|
|
111
|
+
* functionStack.push('main');
|
|
112
|
+
* functionStack.push('foo');
|
|
113
|
+
* functionStack.push('bar');
|
|
114
|
+
* console.log(functionStack.pop()); // 'bar'
|
|
115
|
+
* console.log(functionStack.pop()); // 'foo'
|
|
116
|
+
* console.log(functionStack.pop()); // 'main'
|
|
117
|
+
* @example
|
|
118
|
+
* // Simplify File Paths
|
|
119
|
+
* const stack = new Stack<string>();
|
|
120
|
+
* const path = '/a/./b/../../c';
|
|
121
|
+
* path.split('/').forEach(segment => {
|
|
122
|
+
* if (segment === '..') stack.pop();
|
|
123
|
+
* else if (segment && segment !== '.') stack.push(segment);
|
|
124
|
+
* });
|
|
125
|
+
* console.log(stack.elements.join('/')); // 'c'
|
|
126
|
+
* @example
|
|
127
|
+
* // Stock Span Problem
|
|
128
|
+
* const stack = new Stack<number>();
|
|
129
|
+
* const prices = [100, 80, 60, 70, 60, 75, 85];
|
|
130
|
+
* const spans: number[] = [];
|
|
131
|
+
* prices.forEach((price, i) => {
|
|
132
|
+
* while (!stack.isEmpty() && prices[stack.peek()!] <= price) {
|
|
133
|
+
* stack.pop();
|
|
134
|
+
* }
|
|
135
|
+
* spans.push(stack.isEmpty() ? i + 1 : i - stack.peek()!);
|
|
136
|
+
* stack.push(i);
|
|
137
|
+
* });
|
|
138
|
+
* console.log(spans); // [1, 1, 1, 2, 1, 4, 6]
|
|
18
139
|
*/
|
|
19
140
|
export class Stack<E = any, R = any> extends IterableElementBase<E, R> {
|
|
20
141
|
constructor(elements: Iterable<E> | Iterable<R> = [], options?: StackOptions<E, R>) {
|