typescript-ds-lib 0.2.7 → 0.2.8
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/package.json +2 -4
- package/lib/binary-search-tree.ts +0 -218
- package/lib/deque.ts +0 -86
- package/lib/hash-table.ts +0 -179
- package/lib/linked-list.ts +0 -314
- package/lib/map.ts +0 -55
- package/lib/matrix.ts +0 -427
- package/lib/priority-queue.ts +0 -71
- package/lib/queue.ts +0 -62
- package/lib/red-black-tree.ts +0 -350
- package/lib/set.ts +0 -83
- package/lib/stack.ts +0 -59
- package/types/index.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typescript-ds-lib",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "A collection of TypeScript data structure implementations",
|
|
5
5
|
"author": "Artiom Baloian <artiom.baloian@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,14 +8,12 @@
|
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
-
"lib",
|
|
12
|
-
"types",
|
|
13
11
|
"README.md",
|
|
14
12
|
"LICENSE"
|
|
15
13
|
],
|
|
16
14
|
"scripts": {
|
|
17
15
|
"build": "rm -rf dist && tsc",
|
|
18
|
-
"prepare": "npm run build",
|
|
16
|
+
"prepare": "npm run build",
|
|
19
17
|
"test": "rm -rf dist && tsc && jest"
|
|
20
18
|
},
|
|
21
19
|
"repository": {
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import { Comparator } from '../types';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export interface BinarySearchTree<T> {
|
|
5
|
-
insert(element: T): void;
|
|
6
|
-
remove(element: T): void;
|
|
7
|
-
find(element: T): boolean;
|
|
8
|
-
min(): T | undefined;
|
|
9
|
-
max(): T | undefined;
|
|
10
|
-
forEach(callback: (element: T) => void, traversal?: 'inorder' | 'preorder' | 'postorder'): void;
|
|
11
|
-
isEmpty(): boolean;
|
|
12
|
-
clear(): void;
|
|
13
|
-
count(): number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class TreeNode<T> {
|
|
18
|
-
value: T;
|
|
19
|
-
left: TreeNode<T> | null;
|
|
20
|
-
right: TreeNode<T> | null;
|
|
21
|
-
|
|
22
|
-
constructor(value: T) {
|
|
23
|
-
this.value = value;
|
|
24
|
-
this.left = null;
|
|
25
|
-
this.right = null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export class BinarySearchTree<T> implements BinarySearchTree<T> {
|
|
31
|
-
private root: TreeNode<T> | null;
|
|
32
|
-
private comparator: Comparator<T>;
|
|
33
|
-
|
|
34
|
-
constructor(comparator: Comparator<T> = (a: T, b: T) => a < b) {
|
|
35
|
-
this.root = null;
|
|
36
|
-
this.comparator = comparator;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Inserts a value into the BST
|
|
41
|
-
*/
|
|
42
|
-
insert(value: T): void {
|
|
43
|
-
const newNode = new TreeNode(value);
|
|
44
|
-
if (!this.root) {
|
|
45
|
-
this.root = newNode;
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
let current = this.root;
|
|
49
|
-
while (true) {
|
|
50
|
-
if (this.comparator(value, current.value)) {
|
|
51
|
-
if (current.left === null) {
|
|
52
|
-
current.left = newNode;
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
current = current.left;
|
|
56
|
-
} else {
|
|
57
|
-
if (current.right === null) {
|
|
58
|
-
current.right = newNode;
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
current = current.right;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Searches for a value in the BST. Returns true if found, false otherwise
|
|
68
|
-
*/
|
|
69
|
-
find(value: T): boolean {
|
|
70
|
-
let current = this.root;
|
|
71
|
-
while (current !== null) {
|
|
72
|
-
if (this.isEqual(value, current.value)) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
if (this.comparator(value, current.value)) {
|
|
76
|
-
current = current.left;
|
|
77
|
-
} else {
|
|
78
|
-
current = current.right;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Returns the minimum value in the BST, or undefined if tree is empty
|
|
86
|
-
*/
|
|
87
|
-
min(): T | undefined {
|
|
88
|
-
if (!this.root) return undefined;
|
|
89
|
-
let current = this.root;
|
|
90
|
-
while (current.left !== null) {
|
|
91
|
-
current = current.left;
|
|
92
|
-
}
|
|
93
|
-
return current.value;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Returns the maximum value in the BST, or undefined if tree is empty
|
|
98
|
-
*/
|
|
99
|
-
max(): T | undefined {
|
|
100
|
-
if (!this.root) return undefined;
|
|
101
|
-
let current = this.root;
|
|
102
|
-
while (current.right !== null) {
|
|
103
|
-
current = current.right;
|
|
104
|
-
}
|
|
105
|
-
return current.value;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Removes a value from the BST if it exists
|
|
110
|
-
*/
|
|
111
|
-
remove(value: T): void {
|
|
112
|
-
this.root = this.removeNode(this.root, value);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private removeNode(node: TreeNode<T> | null, value: T): TreeNode<T> | null {
|
|
116
|
-
if (node === null) return null;
|
|
117
|
-
if (this.comparator(value, node.value)) {
|
|
118
|
-
node.left = this.removeNode(node.left, value);
|
|
119
|
-
return node;
|
|
120
|
-
} else if (this.comparator(node.value, value)) {
|
|
121
|
-
node.right = this.removeNode(node.right, value);
|
|
122
|
-
return node;
|
|
123
|
-
} else {
|
|
124
|
-
// Node to delete found
|
|
125
|
-
// Case 1: Leaf node
|
|
126
|
-
if (node.left === null && node.right === null) {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
// Case 2: Node with one child
|
|
130
|
-
if (node.left === null) return node.right;
|
|
131
|
-
if (node.right === null) return node.left;
|
|
132
|
-
// Case 3: Node with two children
|
|
133
|
-
const minNode = this.findMin(node.right);
|
|
134
|
-
node.value = minNode.value;
|
|
135
|
-
node.right = this.removeNode(node.right, minNode.value);
|
|
136
|
-
return node;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private findMin(node: TreeNode<T>): TreeNode<T> {
|
|
141
|
-
let current = node;
|
|
142
|
-
while (current.left !== null) {
|
|
143
|
-
current = current.left;
|
|
144
|
-
}
|
|
145
|
-
return current;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private isEqual(a: T, b: T): boolean {
|
|
149
|
-
// Two values are equal if neither is less than the other
|
|
150
|
-
return !this.comparator(a, b) && !this.comparator(b, a);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Executes a callback function for each element in the BST in-order traversal
|
|
155
|
-
*/
|
|
156
|
-
forEach(callback: (element: T) => void, traversal: 'inorder' | 'preorder' | 'postorder' = 'inorder'): void {
|
|
157
|
-
switch (traversal) {
|
|
158
|
-
case 'inorder':
|
|
159
|
-
this.inorderTraversal(this.root, callback);
|
|
160
|
-
break;
|
|
161
|
-
case 'preorder':
|
|
162
|
-
this.preorderTraversal(this.root, callback);
|
|
163
|
-
break;
|
|
164
|
-
case 'postorder':
|
|
165
|
-
this.postorderTraversal(this.root, callback);
|
|
166
|
-
break;
|
|
167
|
-
default:
|
|
168
|
-
this.inorderTraversal(this.root, callback);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private inorderTraversal(node: TreeNode<T> | null, callback: (element: T) => void): void {
|
|
173
|
-
if (node === null) return;
|
|
174
|
-
this.inorderTraversal(node.left, callback);
|
|
175
|
-
callback(node.value);
|
|
176
|
-
this.inorderTraversal(node.right, callback);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
private preorderTraversal(node: TreeNode<T> | null, callback: (element: T) => void): void {
|
|
180
|
-
if (node === null) return;
|
|
181
|
-
callback(node.value);
|
|
182
|
-
this.preorderTraversal(node.left, callback);
|
|
183
|
-
this.preorderTraversal(node.right, callback);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private postorderTraversal(node: TreeNode<T> | null, callback: (element: T) => void): void {
|
|
187
|
-
if (node === null) return;
|
|
188
|
-
this.postorderTraversal(node.left, callback);
|
|
189
|
-
this.postorderTraversal(node.right, callback);
|
|
190
|
-
callback(node.value);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Returns true if the BST is empty, false otherwise
|
|
195
|
-
*/
|
|
196
|
-
isEmpty(): boolean {
|
|
197
|
-
return this.root === null;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Removes all nodes from the BST
|
|
202
|
-
*/
|
|
203
|
-
clear(): void {
|
|
204
|
-
this.root = null;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Returns the number of nodes in the BST
|
|
209
|
-
*/
|
|
210
|
-
count(): number {
|
|
211
|
-
return this.countNodes(this.root);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private countNodes(node: TreeNode<T> | null): number {
|
|
215
|
-
if (node === null) return 0;
|
|
216
|
-
return 1 + this.countNodes(node.left) + this.countNodes(node.right);
|
|
217
|
-
}
|
|
218
|
-
}
|
package/lib/deque.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { LinkedList } from './linked-list';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export interface Deque<T> {
|
|
5
|
-
pushFront(element: T): void;
|
|
6
|
-
pushBack(element: T): void;
|
|
7
|
-
popFront(): T | undefined;
|
|
8
|
-
popBack(): T | undefined;
|
|
9
|
-
front(): T | undefined;
|
|
10
|
-
back(): T | undefined;
|
|
11
|
-
isEmpty(): boolean;
|
|
12
|
-
size(): number;
|
|
13
|
-
clear(): void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export class Deque<T> implements Deque<T> {
|
|
18
|
-
private items: LinkedList<T>;
|
|
19
|
-
|
|
20
|
-
constructor() {
|
|
21
|
-
this.items = new LinkedList<T>();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Adds an element to the front of the deque
|
|
26
|
-
*/
|
|
27
|
-
pushFront(element: T): void {
|
|
28
|
-
this.items.pushFront(element);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Adds an element to the back of the deque
|
|
33
|
-
*/
|
|
34
|
-
pushBack(element: T): void {
|
|
35
|
-
this.items.pushBack(element);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Removes and returns the front element from the deque, or undefined if deque is empty
|
|
40
|
-
*/
|
|
41
|
-
popFront(): T | undefined {
|
|
42
|
-
return this.items.popFront();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Removes and returns the back element from the deque, or undefined if deque is empty
|
|
47
|
-
*/
|
|
48
|
-
popBack(): T | undefined {
|
|
49
|
-
return this.items.popBack();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Returns the front element without removing it, or undefined if deque is empty
|
|
54
|
-
*/
|
|
55
|
-
front(): T | undefined {
|
|
56
|
-
return this.items.get(0);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Returns the back element without removing it, or undefined if deque is empty
|
|
61
|
-
*/
|
|
62
|
-
back(): T | undefined {
|
|
63
|
-
return this.items.get(this.items.size() - 1);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Returns true if the deque is empty, false otherwise
|
|
68
|
-
*/
|
|
69
|
-
isEmpty(): boolean {
|
|
70
|
-
return this.items.isEmpty();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Returns the number of elements in the deque
|
|
75
|
-
*/
|
|
76
|
-
size(): number {
|
|
77
|
-
return this.items.size();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Removes all elements from the deque
|
|
82
|
-
*/
|
|
83
|
-
clear(): void {
|
|
84
|
-
this.items.clear();
|
|
85
|
-
}
|
|
86
|
-
}
|
package/lib/hash-table.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
export interface HashTable<K, V> {
|
|
2
|
-
insert(key: K, value: V): void;
|
|
3
|
-
get(key: K): V | undefined;
|
|
4
|
-
remove(key: K): boolean;
|
|
5
|
-
size(): number;
|
|
6
|
-
isEmpty(): boolean;
|
|
7
|
-
clear(): void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class HashNode<K, V> {
|
|
12
|
-
key: K;
|
|
13
|
-
value: V;
|
|
14
|
-
next: HashNode<K, V> | null;
|
|
15
|
-
|
|
16
|
-
constructor(key: K, value: V) {
|
|
17
|
-
this.key = key;
|
|
18
|
-
this.value = value;
|
|
19
|
-
this.next = null;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class HashTable<K, V> implements HashTable<K, V> {
|
|
24
|
-
private table: Array<HashNode<K, V> | null>;
|
|
25
|
-
private count: number;
|
|
26
|
-
private readonly capacity: number;
|
|
27
|
-
|
|
28
|
-
constructor(capacity: number = 32) {
|
|
29
|
-
this.table = new Array(capacity).fill(null);
|
|
30
|
-
this.count = 0;
|
|
31
|
-
this.capacity = capacity;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
private hash(key: K): number {
|
|
35
|
-
if (typeof (key as any).hashCode === 'function') {
|
|
36
|
-
return (key as any).hashCode();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let stringKey: string;
|
|
40
|
-
switch (typeof key) {
|
|
41
|
-
case 'number':
|
|
42
|
-
// If the number is a safe integer, use Knuth's multiplicative method.
|
|
43
|
-
if (Number.isSafeInteger(key)) {
|
|
44
|
-
const knuthConstant = 2654435761;
|
|
45
|
-
return (Math.abs(key * knuthConstant) >>> 0) % this.capacity;
|
|
46
|
-
}
|
|
47
|
-
stringKey = key.toString();
|
|
48
|
-
break;
|
|
49
|
-
case 'object':
|
|
50
|
-
if (key === null) {
|
|
51
|
-
stringKey = 'null';
|
|
52
|
-
} else if (typeof (key as any).toString === 'function') {
|
|
53
|
-
stringKey = (key as any).toString();
|
|
54
|
-
} else {
|
|
55
|
-
stringKey = JSON.stringify(key);
|
|
56
|
-
}
|
|
57
|
-
break;
|
|
58
|
-
case 'string':
|
|
59
|
-
stringKey = key;
|
|
60
|
-
break;
|
|
61
|
-
case 'function':
|
|
62
|
-
stringKey = key.toString();
|
|
63
|
-
break;
|
|
64
|
-
case 'symbol':
|
|
65
|
-
stringKey = key.toString();
|
|
66
|
-
break;
|
|
67
|
-
case 'undefined':
|
|
68
|
-
stringKey = 'null';
|
|
69
|
-
break;
|
|
70
|
-
default:
|
|
71
|
-
stringKey = String(key);
|
|
72
|
-
}
|
|
73
|
-
let hash = 0;
|
|
74
|
-
// DJB2 hash algorithm
|
|
75
|
-
for (let i = 0; i < stringKey.length; i++) {
|
|
76
|
-
hash = ((hash << 5) + hash) + stringKey.charCodeAt(i);
|
|
77
|
-
hash = hash >>> 0; // Convert to 32-bit unsigned integer
|
|
78
|
-
}
|
|
79
|
-
return hash % this.capacity;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
insert(key: K, value: V): void {
|
|
83
|
-
const index = this.hash(key);
|
|
84
|
-
const newNode = new HashNode(key, value);
|
|
85
|
-
if (!this.table[index]) {
|
|
86
|
-
this.table[index] = newNode;
|
|
87
|
-
this.count++;
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
let current = this.table[index];
|
|
91
|
-
while (current) {
|
|
92
|
-
if (this.keysEqual(current.key, key)) {
|
|
93
|
-
current.value = value;
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (!current.next) {
|
|
97
|
-
current.next = newNode;
|
|
98
|
-
this.count++;
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
current = current.next;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
get(key: K): V | undefined {
|
|
106
|
-
const index = this.hash(key);
|
|
107
|
-
let current = this.table[index];
|
|
108
|
-
while (current) {
|
|
109
|
-
if (this.keysEqual(current.key, key)) {
|
|
110
|
-
return current.value;
|
|
111
|
-
}
|
|
112
|
-
current = current.next;
|
|
113
|
-
}
|
|
114
|
-
return undefined;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
remove(key: K): boolean {
|
|
118
|
-
const index = this.hash(key);
|
|
119
|
-
let current = this.table[index];
|
|
120
|
-
let prev: HashNode<K, V> | null = null;
|
|
121
|
-
while (current) {
|
|
122
|
-
if (this.keysEqual(current.key, key)) {
|
|
123
|
-
if (prev) {
|
|
124
|
-
prev.next = current.next;
|
|
125
|
-
} else {
|
|
126
|
-
this.table[index] = current.next;
|
|
127
|
-
}
|
|
128
|
-
this.count--;
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
prev = current;
|
|
132
|
-
current = current.next;
|
|
133
|
-
}
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private keysEqual(key1: K, key2: K): boolean {
|
|
138
|
-
// Check if keys have equals method and use it for comparison
|
|
139
|
-
if (typeof (key1 as any).equals === 'function') {
|
|
140
|
-
return (key1 as any).equals(key2);
|
|
141
|
-
}
|
|
142
|
-
if (key1 === key2) return true;
|
|
143
|
-
if (key1 == null || key2 == null) return false;
|
|
144
|
-
|
|
145
|
-
if (typeof key1 !== 'object' && typeof key2 !== 'object') {
|
|
146
|
-
return key1 === key2;
|
|
147
|
-
}
|
|
148
|
-
if (key1 instanceof Date && key2 instanceof Date) {
|
|
149
|
-
return key1.getTime() === key2.getTime();
|
|
150
|
-
}
|
|
151
|
-
if (key1 instanceof RegExp && key2 instanceof RegExp) {
|
|
152
|
-
return key1.toString() === key2.toString();
|
|
153
|
-
}
|
|
154
|
-
if (Array.isArray(key1) && Array.isArray(key2)) {
|
|
155
|
-
return key1.length === key2.length &&
|
|
156
|
-
key1.every((val, idx) => this.keysEqual(val, key2[idx]));
|
|
157
|
-
}
|
|
158
|
-
if (typeof key1 === 'object' && typeof key2 === 'object') {
|
|
159
|
-
const keys1 = Object.keys(key1);
|
|
160
|
-
const keys2 = Object.keys(key2);
|
|
161
|
-
return keys1.length === keys2.length &&
|
|
162
|
-
keys1.every(k => k in key2 && this.keysEqual((key1 as any)[k], (key2 as any)[k]));
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
size(): number {
|
|
168
|
-
return this.count;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
isEmpty(): boolean {
|
|
172
|
-
return this.count === 0;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
clear(): void {
|
|
176
|
-
this.table = new Array(this.capacity).fill(null);
|
|
177
|
-
this.count = 0;
|
|
178
|
-
}
|
|
179
|
-
}
|