typescript-dsa-stl 2.1.0 → 2.3.0

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/README.md CHANGED
@@ -1,421 +1,592 @@
1
- # TypeScript_DSA
2
-
3
- **This is the GitHub repository** for the npm package **[typescript-dsa-stl](https://www.npmjs.com/package/typescript-dsa-stl)**.
4
-
5
- STL-style data structures and algorithms for TypeScript: **Vector**, **Stack**, **Queue**, **List**, **PriorityQueue**, **OrderedMap** (Map), **UnorderedMap**, **OrderedSet** (Set), **UnorderedSet**, **OrderedMultiMap**, **OrderedMultiSet**, and algorithms (`sort`, `binarySearch`, `lowerBound`, `min`, `max`, etc.). Install from npm to use in your project; this repo holds the source code.
6
-
7
- ---
8
-
9
- ## Install
10
-
11
- ```bash
12
- npm install typescript-dsa-stl
13
- ```
14
-
15
- ---
16
-
17
- ## Quick start
18
-
19
- ```ts
20
- import {
21
- Vector,
22
- Stack,
23
- Queue,
24
- List,
25
- PriorityQueue,
26
- OrderedMap,
27
- UnorderedMap,
28
- OrderedSet,
29
- UnorderedSet,
30
- OrderedMultiMap,
31
- OrderedMultiSet,
32
- sort,
33
- find,
34
- binarySearch,
35
- min,
36
- max,
37
- clamp,
38
- range,
39
- } from 'typescript-dsa-stl';
40
-
41
- // Collections
42
- const vec = new Vector<number>([1, 2, 3]);
43
- vec.push(4);
44
- console.log(vec.back()); // 4
45
-
46
- const stack = new Stack<string>();
47
- stack.push('a');
48
- stack.push('b');
49
- console.log(stack.top()); // 'b'
50
-
51
- const queue = new Queue<number>();
52
- queue.enqueue(1);
53
- queue.enqueue(2);
54
- console.log(queue.front()); // 1
55
-
56
- const list = new List<number>();
57
- list.pushBack(10);
58
- const node = list.pushBack(20);
59
- list.insertBefore(node, 15);
60
- console.log(list.toArray()); // [10, 15, 20]
61
-
62
- // PriorityQueue (max-heap by default)
63
- const pq = new PriorityQueue<number>();
64
- pq.push(3); pq.push(1); pq.push(4);
65
- console.log(pq.top()); // 4
66
- pq.pop();
67
-
68
- // OrderedMap (keys sorted), UnorderedMap (hash)
69
- const map = new UnorderedMap<string, number>();
70
- map.set('a', 1); map.set('b', 2);
71
- console.log(map.get('a')); // 1
72
-
73
- // OrderedSet (sorted unique), UnorderedSet (hash)
74
- const set = new UnorderedSet<number>([1, 2, 2, 3]);
75
- console.log(set.size); // 3
76
-
77
- // OrderedMultiSet (sorted, duplicates allowed)
78
- const multiSet = new OrderedMultiSet<number>();
79
- multiSet.add(3); multiSet.add(1); multiSet.add(2); multiSet.add(2);
80
- console.log(multiSet.toArray()); // [1, 2, 2, 3]
81
- console.log(multiSet.count(2)); // 2
82
-
83
- // OrderedMultiMap (one key → multiple values, keys sorted)
84
- const multiMap = new OrderedMultiMap<string, number>();
85
- multiMap.set('scores', 90); multiMap.set('scores', 85); multiMap.set('scores', 95);
86
- console.log(multiMap.getAll('scores')); // [90, 85, 95]
87
-
88
- // Algorithms (work on arrays and iterables)
89
- const arr = [3, 1, 4, 1, 5];
90
- sort(arr); // [1, 1, 3, 4, 5]
91
- find(arr, (n) => n > 3); // 4
92
- min(arr); // 1
93
- max(arr); // 5
94
-
95
- const sorted = [1, 2, 4, 4, 5];
96
- binarySearch(sorted, 4); // 2
97
- lowerBound(sorted, 4); // 2
98
- upperBound(sorted, 4); // 4
99
-
100
- // Utils
101
- clamp(42, 0, 10); // 10
102
- range(0, 5); // [0, 1, 2, 3, 4]
103
- ```
104
-
105
- ### 2D and 3D vectors (like C++ `vector<vector<int>>`)
106
-
107
- `Vector<T>` is generic, so you can nest it for 2D/3D grids:
108
-
109
- | C++ | TypeScript |
110
- |-----|------------|
111
- | `vector<int>` | `Vector<number>` |
112
- | `vector<vector<int>>` | `Vector<Vector<number>>` |
113
- | `vector<vector<vector<int>>>` | `Vector<Vector<Vector<number>>>` |
114
-
115
- **2D example:**
116
-
117
- ```ts
118
- import { Vector } from 'typescript-dsa-stl';
119
-
120
- const grid = new Vector<Vector<number>>();
121
-
122
- const row0 = new Vector<number>([1, 2, 3]);
123
- const row1 = new Vector<number>([4, 5, 6]);
124
- grid.push(row0);
125
- grid.push(row1);
126
-
127
- grid.at(0).at(1); // 2 (first row, second column)
128
- grid.at(1).at(0); // 4 (second row, first column)
129
-
130
- grid.at(0).set(1, 99);
131
- grid.at(0).push(10);
132
- ```
133
-
134
- **3D example:**
135
-
136
- ```ts
137
- const cube = new Vector<Vector<Vector<number>>>();
138
-
139
- const layer0 = new Vector<Vector<number>>();
140
- layer0.push(new Vector<number>([1, 2]));
141
- layer0.push(new Vector<number>([3, 4]));
142
- cube.push(layer0);
143
-
144
- cube.at(0).at(1).at(0); // 3 (layer 0, row 1, col 0)
145
- ```
146
-
147
- ### Graph adjacency list (like C++ `vector<vector<type>> graph(n)`)
148
-
149
- You can model C++-style adjacency lists using the graph types and helpers exported from `typescript-dsa-stl/collections` (or the main package).
150
-
151
- #### Unweighted adjacency list
152
-
153
- C++:
154
-
155
- ```cpp
156
- int n = 5;
157
- vector<vector<int>> graph(n);
158
- graph[u].push_back(v); // or graph[u].pb(v);
159
- ```
160
-
161
- TypeScript (easy declaration with `createAdjacencyList`):
162
-
163
- ```ts
164
- import { createAdjacencyList } from 'typescript-dsa-stl/collections';
165
-
166
- const n = 5;
167
- const graph = createAdjacencyList(n); // empty graph with n vertices
168
-
169
- // C++: graph[u].push_back(v);
170
- graph[u].push(v);
171
-
172
- // Iteration is the same idea as in C++
173
- for (const v of graph[u]) {
174
- // neighbor v
175
- }
176
- ```
177
-
178
- Or with helpers `addEdge` / `deleteEdge`:
179
-
180
- ```ts
181
- import { createAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
182
-
183
- const graph = createAdjacencyList(5);
184
-
185
- addEdge(graph, u, v); // add u -> v
186
- deleteEdge(graph, u, v); // remove all edges u -> v
187
- ```
188
-
189
- #### Weighted adjacency list
190
-
191
- In C++ you might write:
192
-
193
- ```cpp
194
- int n = 5;
195
- vector<vector<pair<int,int>>> graph(n);
196
- graph[u].push_back({v, w}); // edge u -> v with weight w
197
- ```
198
-
199
- In TypeScript, use `createWeightedAdjacencyList` for easy declaration:
200
-
201
- ```ts
202
- import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
203
-
204
- const n = 5;
205
- const graph = createWeightedAdjacencyList(n); // empty weighted graph with n vertices
206
-
207
- // C++: graph[u].push_back({v, w});
208
- graph[u].push({ to: v, weight: w });
209
-
210
- // When iterating, you get both neighbor and weight
211
- for (const { to, weight } of graph[u]) {
212
- // edge u -> to with cost = weight
213
- }
214
- ```
215
-
216
- Or with the helper functions `addEdge` / `deleteEdge`:
217
-
218
- ```ts
219
- import { createWeightedAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
220
-
221
- const graph = createWeightedAdjacencyList(5);
222
-
223
- addEdge(graph, u, v, w); // add u -> v with weight w
224
- deleteEdge(graph, u, v, w); // delete all edges u -> v with weight w
225
- ```
226
-
227
- #### Graph adjacency list — use cases
228
-
229
- Use an **unweighted** graph (adjacency list) when you only care about connectivity; use a **weighted** graph when edges have costs (distance, time, capacity).
230
-
231
- | Use case | When to use |
232
- |----------|-------------|
233
- | **BFS / DFS, connectivity** | Unweighted: shortest path in terms of hop count, connected components, cycle detection. |
234
- | **Shortest path (Dijkstra), MST** | Weighted: edge weights as distances or costs; run Dijkstra, Prim, or Kruskal on the list. |
235
- | **Social / dependency graphs** | Unweighted or weighted: followers, dependencies (e.g. build order), recommendation graphs. |
236
- | **Grid / game graphs** | Unweighted: 4- or 8-neighbor grids; weighted if movement costs differ per cell. |
237
- | **Network / flow** | Weighted: capacities or latencies on edges for max-flow or routing. |
238
-
239
- #### Disjoint Set Union (Union-Find)
240
-
241
- Use Union-Find (DSU) to compute connected components efficiently. It merges endpoints of every edge in the adjacency list, so for directed graphs it returns weak connectivity components.
242
-
243
- ```ts
244
- import { createAdjacencyList, connectedComponents } from 'typescript-dsa-stl';
245
-
246
- const n = 5;
247
- const graph = createAdjacencyList(n);
248
- graph[0].push(1);
249
- graph[1].push(0);
250
- graph[3].push(4);
251
- graph[4].push(3);
252
-
253
- const comps = connectedComponents(n, graph);
254
- // e.g. [[0, 1], [2], [3, 4]]
255
- ```
256
-
257
- ##### Traverse the result
258
-
259
- `connectedComponents(n, adj)` returns `number[][]` where each inner array is a component (list of vertices).
260
-
261
- ```ts
262
- // 1) Iterate each component
263
- for (const comp of comps) {
264
- // comp is like [v0, v1, ...]
265
- for (const v of comp) {
266
- // do something with vertex v
267
- }
268
- }
269
-
270
- // 2) Component sizes
271
- const sizes = comps.map(comp => comp.length);
272
- ```
273
-
274
- #### Kruskal MST (uses DSU)
275
-
276
- For a weighted graph, `kruskalMST` builds a Minimum Spanning Tree (MST) using DSU.
277
-
278
- ```ts
279
- import {
280
- createWeightedAdjacencyList,
281
- addEdge,
282
- kruskalMST,
283
- } from 'typescript-dsa-stl';
284
-
285
- const n = 4;
286
- const wGraph = createWeightedAdjacencyList(n);
287
-
288
- // Add undirected edges by adding both directions.
289
- addEdge(wGraph, 0, 1, 1); addEdge(wGraph, 1, 0, 1);
290
- addEdge(wGraph, 0, 2, 4); addEdge(wGraph, 2, 0, 4);
291
- addEdge(wGraph, 1, 2, 2); addEdge(wGraph, 2, 1, 2);
292
- addEdge(wGraph, 1, 3, 3); addEdge(wGraph, 3, 1, 3);
293
-
294
- const { edges, totalWeight } = kruskalMST(n, wGraph, { undirected: true });
295
- // edges: MST edges (chosen by weight), totalWeight: sum of weights
296
- ```
297
-
298
- ##### Traverse the MST
299
-
300
- `kruskalMST(...)` returns `{ edges, totalWeight }`. To traverse the MST like a graph, convert `edges` into an adjacency list:
301
-
302
- ```ts
303
- import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
304
-
305
- const mstAdj = createWeightedAdjacencyList(n);
306
-
307
- for (const { u, v, weight } of edges) {
308
- // MST is undirected (we used { undirected: true })
309
- mstAdj[u].push({ to: v, weight });
310
- mstAdj[v].push({ to: u, weight });
311
- }
312
-
313
- // Example: iterate neighbors of vertex 0 in the MST
314
- for (const { to, weight } of mstAdj[0]) {
315
- // visit edge 0 -> to (weight)
316
- }
317
- ```
318
-
319
- ---
320
-
321
- ## API overview
322
-
323
- | Module | Exports |
324
- |--------|--------|
325
- | **Collections** | `Vector`, `Stack`, `Queue`, `List`, `ListNode`, `PriorityQueue`, `OrderedMap`, `UnorderedMap`, `OrderedSet`, `UnorderedSet`, `OrderedMultiMap`, `OrderedMultiSet`, `WeightedEdge`, `AdjacencyList`, `WeightedAdjacencyList`, `createAdjacencyList`, `createWeightedAdjacencyList`, `addEdge`, `deleteEdge` |
326
- | **Algorithms** | `sort`, `find`, `findIndex`, `transform`, `filter`, `reduce`, `reverse`, `unique`, `binarySearch`, `lowerBound`, `upperBound`, `min`, `max`, `partition`, `DisjointSetUnion`, `connectedComponents`, `kruskalMST` |
327
- | **Utils** | `clamp`, `range`, `noop`, `identity`, `swap` |
328
- | **Types** | `Comparator`, `Predicate`, `UnaryFn`, `Reducer`, `IterableLike`, `toArray` |
329
-
330
- ### Subpath imports (tree-shaking)
331
-
332
- ```ts
333
- import { Vector, Stack } from 'typescript-dsa-stl/collections';
334
- import { sort, binarySearch } from 'typescript-dsa-stl/algorithms';
335
- import { clamp, range } from 'typescript-dsa-stl/utils';
336
- import type { Comparator } from 'typescript-dsa-stl/types';
337
- ```
338
-
339
- ---
340
-
341
- ## Data structures
342
-
343
- | Structure | Access | Insert end | Insert middle | Remove end | Remove middle |
344
- |-----------|--------|------------|---------------|------------|---------------|
345
- | **Vector** | O(1) | O(1)* | O(n) | O(1) | O(n) |
346
- | **Stack** | — | O(1) | — | O(1) | — |
347
- | **Queue** | — | O(1)* | — | O(1)* | — |
348
- | **List** | O(n) | O(1) | O(1)** | O(1) | O(1)** |
349
- | **PriorityQueue** | — | O(log n) | — | O(log n) | — |
350
- | **OrderedMap** (Map) | O(log n) get | O(log n) set | — | O(log n) delete | — |
351
- | **UnorderedMap** | O(1)* get/set | O(1)* | — | O(1)* delete | — |
352
- | **OrderedSet** (Set) | O(log n) has | O(log n) add | — | O(log n) delete | — |
353
- | **UnorderedSet** | O(1)* has/add | O(1)* | — | O(1)* delete | — |
354
- | **OrderedMultiMap** | O(log n) get | O(log n) set | — | O(log n) delete | — |
355
- | **OrderedMultiSet** | O(log n) has/count | O(log n) add | — | O(log n) delete | — |
356
-
357
- \* Amortized (hash).
358
- \** At a known node.
359
-
360
- ---
361
-
362
- ## OrderedMultiMap and OrderedMultiSet — use cases
363
-
364
- **OrderedMultiSet** is a sorted collection that allows duplicate elements (like C++ `std::multiset`). Use it when you need ordering and multiple copies of the same value.
365
-
366
- | Use case | Example |
367
- |----------|---------|
368
- | **Sorted runs / leaderboard with ties** | Store scores; multiple users can have the same score. Iterate in sorted order, use `count(score)` for ties. |
369
- | **Event timeline with repeated timestamps** | Add events by time; several events can share the same time. `add(timestamp)`, iterate in order. |
370
- | **K-th smallest in a multiset** | Keep elements sorted; k-th element is at index `k - 1` in iteration. |
371
- | **Range counts** | Combined with binary search ideas: count elements in `[low, high]` using `count` and iteration. |
372
-
373
- **OrderedMultiMap** maps one key to multiple values while keeping keys sorted (like C++ `std::multimap`). Use it when a key can have several associated values and you need key order.
374
-
375
- | Use case | Example |
376
- |----------|---------|
377
- | **Inverted index** | Key = term, values = document IDs containing that term. `set(term, docId)` for each occurrence; `getAll(term)` returns all doc IDs. |
378
- | **Grouping by key** | Key = category, values = items. `set(category, item)`; iterate keys in order, use `getAll(key)` per group. |
379
- | **One-to-many relations** | Key = user ID, values = session IDs. `set(userId, sessionId)`; `getAll(userId)` lists all sessions. |
380
- | **Time-series by bucket** | Key = time bucket, values = events. Sorted keys give chronological buckets; `getAll(bucket)` gets events in that bucket. |
381
-
382
- ### OrderedMultiSet example
383
-
384
- ```ts
385
- import { OrderedMultiSet } from 'typescript-dsa-stl';
386
-
387
- const scores = new OrderedMultiSet<number>();
388
- scores.add(85); scores.add(92); scores.add(85); scores.add(78);
389
- console.log(scores.toArray()); // [78, 85, 85, 92]
390
- console.log(scores.count(85)); // 2
391
- scores.delete(85); // remove one 85
392
- console.log(scores.count(85)); // 1
393
- scores.deleteAll(85); // remove all 85s
394
- ```
395
-
396
- ### OrderedMultiMap example
397
-
398
- ```ts
399
- import { OrderedMultiMap } from 'typescript-dsa-stl';
400
-
401
- const index = new OrderedMultiMap<string, number>(); // term -> doc IDs
402
- index.set('typescript', 1); index.set('typescript', 3); index.set('stl', 2);
403
- console.log(index.getAll('typescript')); // [1, 3]
404
- console.log(index.get('stl')); // 2
405
- for (const [key, value] of index) {
406
- console.log(key, value); // keys in sorted order
407
- }
408
- ```
409
-
410
- ---
411
-
412
- ## For maintainers
413
-
414
- - **Build:** `npm run build` (also runs before `npm publish` via `prepublishOnly`)
415
- - **Publish:** `npm publish` (use `npm publish --access public` for a scoped package name)
416
-
417
- ---
418
-
419
- ## License
420
-
421
- MIT
1
+ # TypeScript_DSA
2
+
3
+ **This is the GitHub repository** for the npm package **[typescript-dsa-stl](https://www.npmjs.com/package/typescript-dsa-stl)**.
4
+
5
+ STL-style data structures and algorithms for TypeScript: **Vector**, **Stack**, **Queue**, **List**, **PriorityQueue**, **OrderedMap** (Map), **UnorderedMap**, **OrderedSet** (Set), **UnorderedSet**, **OrderedMultiMap**, **OrderedMultiSet**, and algorithms (`sort`, `binarySearch`, `lowerBound`, `min`, `max`, **KnuthMorrisPratt**, **RabinKarp**, **StringRollingHash**, etc.). Install from npm to use in your project; this repo holds the source code.
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install typescript-dsa-stl
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Quick start
18
+
19
+ ```ts
20
+ import {
21
+ Vector,
22
+ Stack,
23
+ Queue,
24
+ List,
25
+ PriorityQueue,
26
+ OrderedMap,
27
+ UnorderedMap,
28
+ OrderedSet,
29
+ UnorderedSet,
30
+ OrderedMultiMap,
31
+ OrderedMultiSet,
32
+ sort,
33
+ find,
34
+ binarySearch,
35
+ min,
36
+ max,
37
+ clamp,
38
+ range,
39
+ } from 'typescript-dsa-stl';
40
+
41
+ // Collections
42
+ const vec = new Vector<number>([1, 2, 3]);
43
+ vec.push(4);
44
+ console.log(vec.back()); // 4
45
+
46
+ const stack = new Stack<string>();
47
+ stack.push('a');
48
+ stack.push('b');
49
+ console.log(stack.top()); // 'b'
50
+
51
+ const queue = new Queue<number>();
52
+ queue.enqueue(1);
53
+ queue.enqueue(2);
54
+ console.log(queue.front()); // 1
55
+
56
+ const list = new List<number>();
57
+ list.pushBack(10);
58
+ const node = list.pushBack(20);
59
+ list.insertBefore(node, 15);
60
+ console.log(list.toArray()); // [10, 15, 20]
61
+
62
+ // PriorityQueue (max-heap by default)
63
+ const pq = new PriorityQueue<number>();
64
+ pq.push(3); pq.push(1); pq.push(4);
65
+ console.log(pq.top()); // 4
66
+ pq.pop();
67
+
68
+ // OrderedMap (keys sorted), UnorderedMap (hash)
69
+ const map = new UnorderedMap<string, number>();
70
+ map.set('a', 1); map.set('b', 2);
71
+ console.log(map.get('a')); // 1
72
+
73
+ // OrderedSet (sorted unique), UnorderedSet (hash)
74
+ const set = new UnorderedSet<number>([1, 2, 2, 3]);
75
+ console.log(set.size); // 3
76
+
77
+ // OrderedMultiSet (sorted, duplicates allowed)
78
+ const multiSet = new OrderedMultiSet<number>();
79
+ multiSet.add(3); multiSet.add(1); multiSet.add(2); multiSet.add(2);
80
+ console.log(multiSet.toArray()); // [1, 2, 2, 3]
81
+ console.log(multiSet.count(2)); // 2
82
+
83
+ // OrderedMultiMap (one key → multiple values, keys sorted)
84
+ const multiMap = new OrderedMultiMap<string, number>();
85
+ multiMap.set('scores', 90); multiMap.set('scores', 85); multiMap.set('scores', 95);
86
+ console.log(multiMap.getAll('scores')); // [90, 85, 95]
87
+
88
+ // Algorithms (work on arrays and iterables)
89
+ const arr = [3, 1, 4, 1, 5];
90
+ sort(arr); // [1, 1, 3, 4, 5]
91
+ find(arr, (n) => n > 3); // 4
92
+ min(arr); // 1
93
+ max(arr); // 5
94
+
95
+ const sorted = [1, 2, 4, 4, 5];
96
+ binarySearch(sorted, 4); // 2
97
+ lowerBound(sorted, 4); // 2
98
+ upperBound(sorted, 4); // 4
99
+
100
+ // Utils
101
+ clamp(42, 0, 10); // 10
102
+ range(0, 5); // [0, 1, 2, 3, 4]
103
+ ```
104
+
105
+ ### 2D and 3D vectors (like C++ `vector<vector<int>>`)
106
+
107
+ `Vector<T>` is generic, so you can nest it for 2D/3D grids:
108
+
109
+ | C++ | TypeScript |
110
+ |-----|------------|
111
+ | `vector<int>` | `Vector<number>` |
112
+ | `vector<vector<int>>` | `Vector<Vector<number>>` |
113
+ | `vector<vector<vector<int>>>` | `Vector<Vector<Vector<number>>>` |
114
+
115
+ **2D example:**
116
+
117
+ ```ts
118
+ import { Vector } from 'typescript-dsa-stl';
119
+
120
+ const grid = new Vector<Vector<number>>();
121
+
122
+ const row0 = new Vector<number>([1, 2, 3]);
123
+ const row1 = new Vector<number>([4, 5, 6]);
124
+ grid.push(row0);
125
+ grid.push(row1);
126
+
127
+ grid.at(0).at(1); // 2 (first row, second column)
128
+ grid.at(1).at(0); // 4 (second row, first column)
129
+
130
+ grid.at(0).set(1, 99);
131
+ grid.at(0).push(10);
132
+ ```
133
+
134
+ **3D example:**
135
+
136
+ ```ts
137
+ const cube = new Vector<Vector<Vector<number>>>();
138
+
139
+ const layer0 = new Vector<Vector<number>>();
140
+ layer0.push(new Vector<number>([1, 2]));
141
+ layer0.push(new Vector<number>([3, 4]));
142
+ cube.push(layer0);
143
+
144
+ cube.at(0).at(1).at(0); // 3 (layer 0, row 1, col 0)
145
+ ```
146
+
147
+ ### Graph adjacency list (like C++ `vector<vector<type>> graph(n)`)
148
+
149
+ You can model C++-style adjacency lists using the graph types and helpers exported from `typescript-dsa-stl/collections` (or the main package).
150
+
151
+ #### Unweighted adjacency list
152
+
153
+ C++:
154
+
155
+ ```cpp
156
+ int n = 5;
157
+ vector<vector<int>> graph(n);
158
+ graph[u].push_back(v); // or graph[u].pb(v);
159
+ ```
160
+
161
+ TypeScript (easy declaration with `createAdjacencyList`):
162
+
163
+ ```ts
164
+ import { createAdjacencyList } from 'typescript-dsa-stl/collections';
165
+
166
+ const n = 5;
167
+ const graph = createAdjacencyList(n); // empty graph with n vertices
168
+
169
+ // C++: graph[u].push_back(v);
170
+ graph[u].push(v);
171
+
172
+ // Iteration is the same idea as in C++
173
+ for (const v of graph[u]) {
174
+ // neighbor v
175
+ }
176
+ ```
177
+
178
+ Or with helpers `addEdge` / `deleteEdge`:
179
+
180
+ ```ts
181
+ import { createAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
182
+
183
+ const graph = createAdjacencyList(5);
184
+
185
+ addEdge(graph, u, v); // add u -> v
186
+ deleteEdge(graph, u, v); // remove all edges u -> v
187
+ ```
188
+
189
+ #### Weighted adjacency list
190
+
191
+ In C++ you might write:
192
+
193
+ ```cpp
194
+ int n = 5;
195
+ vector<vector<pair<int,int>>> graph(n);
196
+ graph[u].push_back({v, w}); // edge u -> v with weight w
197
+ ```
198
+
199
+ In TypeScript, use `createWeightedAdjacencyList` for easy declaration:
200
+
201
+ ```ts
202
+ import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
203
+
204
+ const n = 5;
205
+ const graph = createWeightedAdjacencyList(n); // empty weighted graph with n vertices
206
+
207
+ // C++: graph[u].push_back({v, w});
208
+ graph[u].push({ to: v, weight: w });
209
+
210
+ // When iterating, you get both neighbor and weight
211
+ for (const { to, weight } of graph[u]) {
212
+ // edge u -> to with cost = weight
213
+ }
214
+ ```
215
+
216
+ Or with the helper functions `addEdge` / `deleteEdge`:
217
+
218
+ ```ts
219
+ import { createWeightedAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
220
+
221
+ const graph = createWeightedAdjacencyList(5);
222
+
223
+ addEdge(graph, u, v, w); // add u -> v with weight w
224
+ deleteEdge(graph, u, v, w); // delete all edges u -> v with weight w
225
+ ```
226
+
227
+ #### Graph adjacency list — use cases
228
+
229
+ Use an **unweighted** graph (adjacency list) when you only care about connectivity; use a **weighted** graph when edges have costs (distance, time, capacity).
230
+
231
+ | Use case | When to use |
232
+ |----------|-------------|
233
+ | **BFS / DFS, connectivity** | Unweighted: shortest path in terms of hop count, connected components, cycle detection. |
234
+ | **Shortest path (Dijkstra), MST** | Weighted: edge weights as distances or costs; run Dijkstra, Prim, or Kruskal on the list. |
235
+ | **Social / dependency graphs** | Unweighted or weighted: followers, dependencies (e.g. build order), recommendation graphs. |
236
+ | **Grid / game graphs** | Unweighted: 4- or 8-neighbor grids; weighted if movement costs differ per cell. |
237
+ | **Network / flow** | Weighted: capacities or latencies on edges for max-flow or routing. |
238
+
239
+ #### Breadth-first search (BFS) and depth-first search (DFS)
240
+
241
+ `breadthFirstSearch` and `depthFirstSearch` take the number of vertices `n`, an unweighted `AdjacencyList`, and a `start` vertex. They return the **visit order** for all vertices **reachable** from `start` (vertices outside that component are not included). For an undirected graph, add each edge in **both** directions (see `addEdge` below).
242
+
243
+ **Example graph (diamond):** edges `0—1`, `0—2`, `1—3`, `2—3`.
244
+
245
+ ```text
246
+ 0
247
+ / \
248
+ 1 2
249
+ \ /
250
+ 3
251
+ ```
252
+
253
+ With neighbors listed in ascending vertex id (`0: [1,2]`, `1: [0,3]`, …), **BFS** from `0` visits by increasing distance from `0`: first `0`, then `1` and `2`, then `3` → order `[0, 1, 2, 3]`. **DFS** (preorder, first neighbor in each list first) goes `0 → 1 → 3` then `2` → order `[0, 1, 3, 2]`. The exact DFS order depends on how you order each adjacency list.
254
+
255
+ ```ts
256
+ import {
257
+ createAdjacencyList,
258
+ addEdge,
259
+ breadthFirstSearch,
260
+ depthFirstSearch,
261
+ } from 'typescript-dsa-stl';
262
+
263
+ const n = 4;
264
+ const graph = createAdjacencyList(n);
265
+
266
+ // Undirected diamond: add both directions for each edge
267
+ addEdge(graph, 0, 1);
268
+ addEdge(graph, 1, 0);
269
+ addEdge(graph, 0, 2);
270
+ addEdge(graph, 2, 0);
271
+ addEdge(graph, 1, 3);
272
+ addEdge(graph, 3, 1);
273
+ addEdge(graph, 2, 3);
274
+ addEdge(graph, 3, 2);
275
+
276
+ const start = 0;
277
+
278
+ // BFS: level-by-level from start (hop count); output: [0, 1, 2, 3]
279
+ console.log(breadthFirstSearch(n, graph, start));
280
+ // Expected console output: [ 0, 1, 2, 3 ]
281
+
282
+ // DFS: preorder with explicit stack; output: [0, 1, 3, 2] for this adjacency layout
283
+ console.log(depthFirstSearch(n, graph, start));
284
+ // Expected console output: [ 0, 1, 3, 2 ]
285
+
286
+ // Invalid start → empty traversal
287
+ console.log(breadthFirstSearch(n, graph, -1)); // []
288
+ console.log(depthFirstSearch(n, graph, n)); // []
289
+
290
+ // Vertex 4 isolated: BFS/DFS from 0 never visits 4
291
+ const withIsolated = createAdjacencyList(5);
292
+ addEdge(withIsolated, 0, 1);
293
+ addEdge(withIsolated, 1, 0);
294
+ console.log(breadthFirstSearch(5, withIsolated, 0)); // [0, 1] not [0,1,2,3,4]
295
+ ```
296
+
297
+ **Notes**
298
+
299
+ - **Directed graphs:** only list outgoing edges in `adj[u]`; traversal follows arcs from `start`.
300
+ - **Disconnected graphs:** run again from another unvisited `start`, or use `connectedComponents` to enumerate components first.
301
+ - **Weighted graphs:** for traversal ignoring weights, use the same vertex lists as the unweighted graph (weights are ignored by these two functions).
302
+
303
+ #### Disjoint Set Union (Union-Find)
304
+
305
+ Use Union-Find (DSU) to compute connected components efficiently. It merges endpoints of every edge in the adjacency list, so for directed graphs it returns weak connectivity components.
306
+
307
+ ```ts
308
+ import { createAdjacencyList, connectedComponents } from 'typescript-dsa-stl';
309
+
310
+ const n = 5;
311
+ const graph = createAdjacencyList(n);
312
+ graph[0].push(1);
313
+ graph[1].push(0);
314
+ graph[3].push(4);
315
+ graph[4].push(3);
316
+
317
+ const comps = connectedComponents(n, graph);
318
+ // e.g. [[0, 1], [2], [3, 4]]
319
+ ```
320
+
321
+ ##### Traverse the result
322
+
323
+ `connectedComponents(n, adj)` returns `number[][]` where each inner array is a component (list of vertices).
324
+
325
+ ```ts
326
+ // 1) Iterate each component
327
+ for (const comp of comps) {
328
+ // comp is like [v0, v1, ...]
329
+ for (const v of comp) {
330
+ // do something with vertex v
331
+ }
332
+ }
333
+
334
+ // 2) Component sizes
335
+ const sizes = comps.map(comp => comp.length);
336
+ ```
337
+
338
+ #### Kruskal MST (uses DSU)
339
+
340
+ For a weighted graph, `kruskalMST` builds a Minimum Spanning Tree (MST) using DSU.
341
+
342
+ ```ts
343
+ import {
344
+ createWeightedAdjacencyList,
345
+ addEdge,
346
+ kruskalMST,
347
+ } from 'typescript-dsa-stl';
348
+
349
+ const n = 4;
350
+ const wGraph = createWeightedAdjacencyList(n);
351
+
352
+ // Add undirected edges by adding both directions.
353
+ addEdge(wGraph, 0, 1, 1); addEdge(wGraph, 1, 0, 1);
354
+ addEdge(wGraph, 0, 2, 4); addEdge(wGraph, 2, 0, 4);
355
+ addEdge(wGraph, 1, 2, 2); addEdge(wGraph, 2, 1, 2);
356
+ addEdge(wGraph, 1, 3, 3); addEdge(wGraph, 3, 1, 3);
357
+
358
+ const { edges, totalWeight } = kruskalMST(n, wGraph, { undirected: true });
359
+ // edges: MST edges (chosen by weight), totalWeight: sum of weights
360
+ ```
361
+
362
+ ##### Traverse the MST
363
+
364
+ `kruskalMST(...)` returns `{ edges, totalWeight }`. To traverse the MST like a graph, convert `edges` into an adjacency list:
365
+
366
+ ```ts
367
+ import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
368
+
369
+ const mstAdj = createWeightedAdjacencyList(n);
370
+
371
+ for (const { u, v, weight } of edges) {
372
+ // MST is undirected (we used { undirected: true })
373
+ mstAdj[u].push({ to: v, weight });
374
+ mstAdj[v].push({ to: u, weight });
375
+ }
376
+
377
+ // Example: iterate neighbors of vertex 0 in the MST
378
+ for (const { to, weight } of mstAdj[0]) {
379
+ // visit edge 0 -> to (weight)
380
+ }
381
+ ```
382
+
383
+ #### Knuth–Morris–Pratt (KMP), Rabin–Karp, and string rolling hash
384
+
385
+ All three work on **UTF-16 code units** (same as `String` indexing). They solve **different jobs**: KMP and Rabin–Karp are **pattern matchers** (list all start indices of a pattern in a text). `StringRollingHash` is a **substring-hash tool** on a **fixed** string—you combine it with your own logic (equality checks, binary search, etc.).
386
+
387
+ ##### When to use which
388
+
389
+ | Goal | Prefer | Why |
390
+ |------|--------|-----|
391
+ | **Find every occurrence** of **one pattern** in **one text**, with **worst-case** O(n + m), **no hashing**, predictable behaviour | **KnuthMorrisPratt** | LPS table; only character comparisons; no modular arithmetic. |
392
+ | **Find every occurrence** of a pattern using a **sliding window** and **hashes** (triple moduli + final verify) | **RabinKarp** | Same asymptotic average case; good when you think in rolling hashes or batch **same-length** patterns. |
393
+ | **Many O(1) hash queries** on **substrings of one string** you already hold (compare two ranges, palindrome / LCP style checks, rolling checks without slicing) | **StringRollingHash** | O(n) preprocess, O(1) per `substringHash`; **not** a drop-in “find all matches” API—use KMP or Rabin–Karp for that. |
394
+
395
+ **Concrete situations**
396
+
397
+ - **Use KMP** when you need a **guaranteed** linear scan (interviews, strict time bounds, large alphabets), or the pattern is **reused** across many searches (`new KnuthMorrisPratt(pattern)` once, `.search(text)` many times).
398
+ - **Use Rabin–Karp** when a **rolling hash** model fits (e.g. one long stream, one pattern), or you later generalize to **several patterns of the same length** (compare each pattern’s triple hash to each window hash). Triple hashing keeps false hash positives negligible; **verification** still guarantees correct indices.
399
+ - **Use `StringRollingHash`** when the problem is **“hash of s[l..r)”** many times on **one** `s`—e.g. check `s[i..i+k) === s[j..j+k)` via hash equality (then confirm if needed), or algorithms that **binary search** on length using substring hashes. For **only** “list all starts of P in T”, pick **KMP** or **Rabin–Karp** instead of building substring hashes by hand.
400
+
401
+ ```ts
402
+ import { KnuthMorrisPratt, RabinKarp, StringRollingHash } from 'typescript-dsa-stl';
403
+
404
+ // A) Pattern fixed, searched in many buffers — build KMP once, call .search() repeatedly (LPS reused).
405
+ const multiDoc = new KnuthMorrisPratt('ERROR');
406
+ multiDoc.search(serverLog1);
407
+ multiDoc.search(serverLog2);
408
+
409
+ // B) One haystack, one needle — both matchers return the same indices; KMP = no mods, Rabin–Karp = triple hash + verify.
410
+ const hay = '...long text...';
411
+ KnuthMorrisPratt.findOccurrences(hay, 'needle');
412
+ new RabinKarp('needle').search(hay);
413
+
414
+ // C) Same static string, many range-equality checks rolling hash (not for “find all pattern starts” by itself).
415
+ const s = 'banana';
416
+ const rh = new StringRollingHash(s);
417
+ // Does s[1..4) equal s[3..6)? (both length 3)
418
+ const maybe = rh.substringHash(1, 3) === rh.substringHash(3, 3); // then compare slices if you need certainty
419
+ ```
420
+
421
+ **How KMP works:** Build an LPS (longest proper prefix that is also a suffix) table for the pattern, then scan the text once. On a mismatch, the LPS tells you how far to shift the pattern without moving the text pointer backward—**O(n + m)** time, **O(m)** extra space for the pattern’s LPS.
422
+
423
+ **How Rabin–Karp works:** Use **triple hashing**—three independent polynomial hashes (same base, three distinct prime moduli, see `RABIN_KARP_DEFAULT_MODS`). A position is a candidate only when **all three** window hashes match the pattern’s triple; then a **character-by-character** check runs so reported matches are always correct. Spurious triple collisions are negligible; average **O(n + m)**.
424
+
425
+ **How rolling hash works:** Precompute prefix hashes and powers of a base modulo a prime. The hash of any substring `s[start .. start + length)` is derived from two prefix values—**O(n)** build, **O(1)** per substring query. Hashes can collide; for critical equality checks, compare actual strings after a hash match.
426
+
427
+ ```ts
428
+ import {
429
+ KnuthMorrisPratt,
430
+ StringRollingHash,
431
+ RabinKarp,
432
+ RABIN_KARP_DEFAULT_MODS,
433
+ } from 'typescript-dsa-stl';
434
+
435
+ // --- KMP: construct with the pattern; LPS is built inside the constructor ---
436
+ const kmp = new KnuthMorrisPratt('aba');
437
+ // LPS for "aba" is [0, 0, 1] — used to skip redundant comparisons after a partial match
438
+
439
+ const positions = kmp.search('ababa');
440
+ // "aba" appears starting at index 0 ("ababa") and index 2 ("aba" at the end)
441
+ console.log(positions); // [0, 2]
442
+
443
+ // One-shot search without storing an instance (same result as above for this pattern/text)
444
+ console.log(KnuthMorrisPratt.findOccurrences('ababa', 'aba')); // [0, 2]
445
+
446
+ // Overlapping matches are all reported (each valid start index)
447
+ const overlaps = KnuthMorrisPratt.findOccurrences('aaaa', 'aa');
448
+ // Starts at 0, 1, 2 — three overlapping occurrences of "aa"
449
+ console.log(overlaps); // [0, 1, 2]
450
+
451
+ // Empty pattern: no matches returned (empty array)
452
+ console.log(new KnuthMorrisPratt('').search('anything')); // []
453
+
454
+ // --- Rabin–Karp: triple moduli (defaults) + verification; same results as KMP for real matches ---
455
+ const rk = new RabinKarp('aba');
456
+ // RABIN_KARP_DEFAULT_MODS = [1e9+7, 1e9+9, 998244353] — three hashes must agree, then verify
457
+ console.log(RABIN_KARP_DEFAULT_MODS.length); // 3
458
+ console.log(rk.search('ababa')); // [0, 2]
459
+ console.log(RabinKarp.findOccurrences('ababa', 'aba')); // [0, 2]
460
+ // new RabinKarp(pattern, base?, [mod0, mod1, mod2]) — override the three primes if needed
461
+
462
+ // Overlapping matches (same as KMP)
463
+ console.log(RabinKarp.findOccurrences('aaaa', 'aa')); // [0, 1, 2]
464
+
465
+ // Empty pattern: no matches
466
+ console.log(new RabinKarp('').search('text')); // []
467
+
468
+ // --- String rolling hash: default base 131, mod 1_000_000_007 (both configurable) ---
469
+ const rh = new StringRollingHash('hello');
470
+ // Internally: prefix[] and pow[] so substring hashes are O(1)
471
+
472
+ // Hash of the entire string (same as substring from 0 with full length)
473
+ console.log(rh.fullHash()); // bigint — concrete value depends on base/mod
474
+ console.log(rh.substringHash(0, rh.length())); // same bigint as fullHash()
475
+
476
+ // Hash of substring "ell" — starts at index 1, length 3
477
+ console.log(rh.substringHash(1, 3)); // bigint for "ell"
478
+
479
+ // Two equal strings with the same base/mod yield equal full hashes
480
+ const same = new StringRollingHash('hello');
481
+ console.log(rh.fullHash() === same.fullHash()); // true
482
+
483
+ // Compare a substring of one string to another range (useful for pattern checks)
484
+ const a = new StringRollingHash('banana');
485
+ const b = new StringRollingHash('na');
486
+ // Hash of "na" in "banana" at index 2 should match b’s full hash if substrings equal
487
+ console.log(a.substringHash(2, 2) === b.fullHash()); // true — both are "na"
488
+ ```
489
+
490
+ ---
491
+
492
+ ## API overview
493
+
494
+ | Module | Exports |
495
+ |--------|--------|
496
+ | **Collections** | `Vector`, `Stack`, `Queue`, `List`, `ListNode`, `PriorityQueue`, `OrderedMap`, `UnorderedMap`, `OrderedSet`, `UnorderedSet`, `OrderedMultiMap`, `OrderedMultiSet`, `WeightedEdge`, `AdjacencyList`, `WeightedAdjacencyList`, `createAdjacencyList`, `createWeightedAdjacencyList`, `addEdge`, `deleteEdge` |
497
+ | **Algorithms** | `sort`, `find`, `findIndex`, `transform`, `filter`, `reduce`, `reverse`, `unique`, `binarySearch`, `lowerBound`, `upperBound`, `min`, `max`, `partition`, `DisjointSetUnion`, `KnuthMorrisPratt`, `RabinKarp`, `RABIN_KARP_DEFAULT_MODS`, `StringRollingHash`, `breadthFirstSearch`, `depthFirstSearch`, `connectedComponents`, `kruskalMST` |
498
+ | **Utils** | `clamp`, `range`, `noop`, `identity`, `swap` |
499
+ | **Types** | `Comparator`, `Predicate`, `UnaryFn`, `Reducer`, `IterableLike`, `toArray`, `RabinKarpTripleMods` |
500
+
501
+ ### Subpath imports (tree-shaking)
502
+
503
+ ```ts
504
+ import { Vector, Stack } from 'typescript-dsa-stl/collections';
505
+ import { sort, binarySearch, breadthFirstSearch, depthFirstSearch, KnuthMorrisPratt, RabinKarp, StringRollingHash } from 'typescript-dsa-stl/algorithms';
506
+ import { clamp, range } from 'typescript-dsa-stl/utils';
507
+ import type { Comparator } from 'typescript-dsa-stl/types';
508
+ ```
509
+
510
+ ---
511
+
512
+ ## Data structures
513
+
514
+ | Structure | Access | Insert end | Insert middle | Remove end | Remove middle |
515
+ |-----------|--------|------------|---------------|------------|---------------|
516
+ | **Vector** | O(1) | O(1)* | O(n) | O(1) | O(n) |
517
+ | **Stack** | — | O(1) | — | O(1) | — |
518
+ | **Queue** | — | O(1)* | — | O(1)* | — |
519
+ | **List** | O(n) | O(1) | O(1)** | O(1) | O(1)** |
520
+ | **PriorityQueue** | — | O(log n) | — | O(log n) | — |
521
+ | **OrderedMap** (Map) | O(log n) get | O(log n) set | — | O(log n) delete | — |
522
+ | **UnorderedMap** | O(1)* get/set | O(1)* | — | O(1)* delete | — |
523
+ | **OrderedSet** (Set) | O(log n) has | O(log n) add | — | O(log n) delete | — |
524
+ | **UnorderedSet** | O(1)* has/add | O(1)* | — | O(1)* delete | — |
525
+ | **OrderedMultiMap** | O(log n) get | O(log n) set | — | O(log n) delete | — |
526
+ | **OrderedMultiSet** | O(log n) has/count | O(log n) add | — | O(log n) delete | — |
527
+
528
+ \* Amortized (hash).
529
+ \** At a known node.
530
+
531
+ ---
532
+
533
+ ## OrderedMultiMap and OrderedMultiSet — use cases
534
+
535
+ **OrderedMultiSet** is a sorted collection that allows duplicate elements (like C++ `std::multiset`). Use it when you need ordering and multiple copies of the same value.
536
+
537
+ | Use case | Example |
538
+ |----------|---------|
539
+ | **Sorted runs / leaderboard with ties** | Store scores; multiple users can have the same score. Iterate in sorted order, use `count(score)` for ties. |
540
+ | **Event timeline with repeated timestamps** | Add events by time; several events can share the same time. `add(timestamp)`, iterate in order. |
541
+ | **K-th smallest in a multiset** | Keep elements sorted; k-th element is at index `k - 1` in iteration. |
542
+ | **Range counts** | Combined with binary search ideas: count elements in `[low, high]` using `count` and iteration. |
543
+
544
+ **OrderedMultiMap** maps one key to multiple values while keeping keys sorted (like C++ `std::multimap`). Use it when a key can have several associated values and you need key order.
545
+
546
+ | Use case | Example |
547
+ |----------|---------|
548
+ | **Inverted index** | Key = term, values = document IDs containing that term. `set(term, docId)` for each occurrence; `getAll(term)` returns all doc IDs. |
549
+ | **Grouping by key** | Key = category, values = items. `set(category, item)`; iterate keys in order, use `getAll(key)` per group. |
550
+ | **One-to-many relations** | Key = user ID, values = session IDs. `set(userId, sessionId)`; `getAll(userId)` lists all sessions. |
551
+ | **Time-series by bucket** | Key = time bucket, values = events. Sorted keys give chronological buckets; `getAll(bucket)` gets events in that bucket. |
552
+
553
+ ### OrderedMultiSet example
554
+
555
+ ```ts
556
+ import { OrderedMultiSet } from 'typescript-dsa-stl';
557
+
558
+ const scores = new OrderedMultiSet<number>();
559
+ scores.add(85); scores.add(92); scores.add(85); scores.add(78);
560
+ console.log(scores.toArray()); // [78, 85, 85, 92]
561
+ console.log(scores.count(85)); // 2
562
+ scores.delete(85); // remove one 85
563
+ console.log(scores.count(85)); // 1
564
+ scores.deleteAll(85); // remove all 85s
565
+ ```
566
+
567
+ ### OrderedMultiMap example
568
+
569
+ ```ts
570
+ import { OrderedMultiMap } from 'typescript-dsa-stl';
571
+
572
+ const index = new OrderedMultiMap<string, number>(); // term -> doc IDs
573
+ index.set('typescript', 1); index.set('typescript', 3); index.set('stl', 2);
574
+ console.log(index.getAll('typescript')); // [1, 3]
575
+ console.log(index.get('stl')); // 2
576
+ for (const [key, value] of index) {
577
+ console.log(key, value); // keys in sorted order
578
+ }
579
+ ```
580
+
581
+ ---
582
+
583
+ ## For maintainers
584
+
585
+ - **Build:** `npm run build` (also runs before `npm publish` via `prepublishOnly`)
586
+ - **Publish:** `npm publish` (use `npm publish --access public` for a scoped package name)
587
+
588
+ ---
589
+
590
+ ## License
591
+
592
+ MIT