data-structure-typed 1.41.8 → 1.41.9

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "data-structure-typed",
3
- "version": "1.41.8",
3
+ "version": "1.41.9",
4
4
  "description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.",
5
5
  "main": "dist/cjs/src/index.js",
6
6
  "module": "dist/mjs/src/index.js",
@@ -64,10 +64,10 @@
64
64
  "@typescript-eslint/eslint-plugin": "^6.7.4",
65
65
  "@typescript-eslint/parser": "^6.7.4",
66
66
  "auto-changelog": "^2.4.0",
67
- "avl-tree-typed": "^1.41.6",
67
+ "avl-tree-typed": "^1.41.8",
68
68
  "benchmark": "^2.1.4",
69
- "binary-tree-typed": "^1.41.6",
70
- "bst-typed": "^1.41.6",
69
+ "binary-tree-typed": "^1.41.8",
70
+ "bst-typed": "^1.41.8",
71
71
  "dependency-cruiser": "^14.1.0",
72
72
  "eslint": "^8.50.0",
73
73
  "eslint-config-prettier": "^9.0.0",
@@ -75,7 +75,7 @@
75
75
  "eslint-import-resolver-typescript": "^3.6.1",
76
76
  "eslint-plugin-import": "^2.28.1",
77
77
  "fast-glob": "^3.3.1",
78
- "heap-typed": "^1.41.6",
78
+ "heap-typed": "^1.41.8",
79
79
  "istanbul-badges-readme": "^1.8.5",
80
80
  "jest": "^29.7.0",
81
81
  "prettier": "^3.0.3",
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * data-structure-typed
3
3
  *
4
- * @author Kirk Qi
5
- * @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
4
+ * @author Tyler Zeng
5
+ * @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
6
6
  * @license MIT License
7
7
  */
8
- import {arrayRemove, uuidV4} from '../../utils';
8
+ import {uuidV4} from '../../utils';
9
9
  import {PriorityQueue} from '../priority-queue';
10
10
  import type {DijkstraResult, VertexKey} from '../../types';
11
11
  import {IGraph} from '../../interfaces';
@@ -223,39 +223,43 @@ export abstract class AbstractGraph<
223
223
  * @param {VO | VertexKey} v1 - The parameter `v1` represents either a vertex object (`VO`) or a vertex ID (`VertexKey`).
224
224
  * It is the starting vertex for finding paths.
225
225
  * @param {VO | VertexKey} v2 - The parameter `v2` represents either a vertex object (`VO`) or a vertex ID (`VertexKey`).
226
+ * @param limit - The count of limitation of result array.
226
227
  * @returns The function `getAllPathsBetween` returns an array of arrays of vertices (`VO[][]`).
227
228
  */
228
- getAllPathsBetween(v1: VO | VertexKey, v2: VO | VertexKey): VO[][] {
229
+ getAllPathsBetween(v1: VO | VertexKey, v2: VO | VertexKey, limit = 1000): VO[][] {
229
230
  const paths: VO[][] = [];
230
231
  const vertex1 = this._getVertex(v1);
231
232
  const vertex2 = this._getVertex(v2);
233
+
232
234
  if (!(vertex1 && vertex2)) {
233
235
  return [];
234
236
  }
235
237
 
236
- const dfs = (cur: VO, dest: VO, visiting: Set<VO>, path: VO[]) => {
237
- visiting.add(cur);
238
+ const stack: { vertex: VO, path: VO[] }[] = [];
239
+ stack.push({ vertex: vertex1, path: [vertex1] });
240
+
241
+ while (stack.length > 0) {
242
+ const { vertex, path } = stack.pop()!;
238
243
 
239
- if (cur === dest) {
240
- paths.push([vertex1, ...path]);
244
+ if (vertex === vertex2) {
245
+ paths.push(path);
246
+ if (paths.length >= limit) return paths;
241
247
  }
242
248
 
243
- const neighbors = this.getNeighbors(cur);
249
+ const neighbors = this.getNeighbors(vertex);
244
250
  for (const neighbor of neighbors) {
245
- if (!visiting.has(neighbor)) {
246
- path.push(neighbor);
247
- dfs(neighbor, dest, visiting, path);
248
- path.pop();
251
+ if (!path.includes(neighbor)) {
252
+ const newPath = [...path, neighbor];
253
+ stack.push({ vertex: neighbor, path: newPath });
249
254
  }
250
255
  }
251
-
252
- visiting.delete(cur);
253
- };
254
-
255
- dfs(vertex1, vertex2, new Set<VO>(), []);
256
+ }
256
257
  return paths;
257
258
  }
258
259
 
260
+
261
+
262
+
259
263
  /**
260
264
  * The function calculates the sum of weights along a given path.
261
265
  * @param {VO[]} path - An array of vertices (VO) representing a path in a graph.
@@ -338,38 +342,43 @@ export abstract class AbstractGraph<
338
342
  * @param {boolean} [isWeight] - A boolean flag indicating whether to consider the weight of edges in finding the
339
343
  * minimum path. If set to true, the function will use Dijkstra's algorithm to find the minimum weighted path. If set
340
344
  * to false, the function will use breadth-first search (BFS) to find the minimum path.
345
+ * @param isDFS - If set to true, it enforces the use of getAllPathsBetween to first obtain all possible paths,
346
+ * followed by iterative computation of the shortest path. This approach may result in exponential time complexity,
347
+ * so the default method is to use the Dijkstra algorithm to obtain the shortest weighted path.
341
348
  * @returns The function `getMinPathBetween` returns an array of vertices (`VO[]`) representing the minimum path between
342
349
  * two vertices (`v1` and `v2`). If there is no path between the vertices, it returns `null`.
343
350
  */
344
- getMinPathBetween(v1: VO | VertexKey, v2: VO | VertexKey, isWeight?: boolean): VO[] | null {
351
+ getMinPathBetween(v1: VO | VertexKey, v2: VO | VertexKey, isWeight?: boolean, isDFS = false): VO[] | null {
345
352
  if (isWeight === undefined) isWeight = false;
346
353
 
347
354
  if (isWeight) {
348
- const allPaths = this.getAllPathsBetween(v1, v2);
349
- let min = Infinity;
350
- let minIndex = -1;
351
- let index = 0;
352
- for (const path of allPaths) {
353
- const pathSumWeight = this.getPathSumWeight(path);
354
- if (pathSumWeight < min) {
355
- min = pathSumWeight;
356
- minIndex = index;
355
+ if (isDFS) {
356
+ const allPaths = this.getAllPathsBetween(v1, v2, 10000);
357
+ let min = Infinity;
358
+ let minIndex = -1;
359
+ let index = 0;
360
+ for (const path of allPaths) {
361
+ const pathSumWeight = this.getPathSumWeight(path);
362
+ if (pathSumWeight < min) {
363
+ min = pathSumWeight;
364
+ minIndex = index;
365
+ }
366
+ index++;
357
367
  }
358
- index++;
368
+ return allPaths[minIndex] || null;
369
+ } else {
370
+ return this.dijkstra(v1, v2, true, true)?.minPath ?? [];
359
371
  }
360
- return allPaths[minIndex] || null;
372
+
361
373
  } else {
362
- // BFS
374
+ // DFS
363
375
  let minPath: VO[] = [];
364
376
  const vertex1 = this._getVertex(v1);
365
377
  const vertex2 = this._getVertex(v2);
366
- if (!(vertex1 && vertex2)) {
367
- return [];
368
- }
369
-
370
- const dfs = (cur: VO, dest: VO, visiting: Map<VO, boolean>, path: VO[]) => {
371
- visiting.set(cur, true);
378
+ if (!(vertex1 && vertex2)) return [];
372
379
 
380
+ const dfs = (cur: VO, dest: VO, visiting: Set<VO>, path: VO[]) => {
381
+ visiting.add(cur);
373
382
  if (cur === dest) {
374
383
  minPath = [vertex1, ...path];
375
384
  return;
@@ -377,17 +386,17 @@ export abstract class AbstractGraph<
377
386
 
378
387
  const neighbors = this.getNeighbors(cur);
379
388
  for (const neighbor of neighbors) {
380
- if (!visiting.get(neighbor)) {
389
+ if (!visiting.has(neighbor)) {
381
390
  path.push(neighbor);
382
391
  dfs(neighbor, dest, visiting, path);
383
- arrayRemove(path, (vertex: VO) => vertex === neighbor);
392
+ path.pop();
384
393
  }
385
394
  }
386
395
 
387
- visiting.set(cur, false);
396
+ visiting.delete(cur);
388
397
  };
389
398
 
390
- dfs(vertex1, vertex2, new Map<VO, boolean>(), []);
399
+ dfs(vertex1, vertex2, new Set<VO>(), []);
391
400
  return minPath;
392
401
  }
393
402
  }
@@ -149,8 +149,7 @@ describe('UndirectedGraph', () => {
149
149
  expect(degreeOfC).toBe(1);
150
150
  });
151
151
 
152
- it('xxx', () => {
153
- // const start = performance.now();
152
+ it('should getAllPathsBetween work well in 66 vertexes 97 edges graph', () => {
154
153
  const graph = new UndirectedGraph<{name: string}, number>();
155
154
  for (const v of saltyVertexes) {
156
155
  graph.addVertex(v.name, v);
@@ -159,9 +158,19 @@ describe('UndirectedGraph', () => {
159
158
  const [s, d] = e;
160
159
  graph.addEdge(s.name, d.name, d.weight);
161
160
  }
162
- // const result = graph.getAllPathsBetween('Intersection_1','Intersection_5');
163
- // console.log('---xxx', performance.now() - start, result)
164
- // const result = graph.dijkstra('Intersection_1','Intersection_5', true, true);
165
- // console.log('---xxx', performance.now() - start, result)
161
+ const allPaths = graph.getAllPathsBetween('Intersection_1','Intersection_5');
162
+ expect(allPaths.length).toBe(1000);
163
+ const minWeightedPathDFS = graph.getMinPathBetween('Intersection_1','Intersection_5', true, true);
164
+ expect(minWeightedPathDFS?.[0]?.key).toBe('Intersection_1');
165
+ expect(minWeightedPathDFS?.[5]?.key).toBe('Intersection_42');
166
+ expect(minWeightedPathDFS?.[8]?.key).toBe('Intersection_18');
167
+ expect(minWeightedPathDFS?.[27]?.key).toBe('Intersection_6');
168
+ const minWeightedPath = graph.dijkstra('Intersection_1','Intersection_5', true, true);
169
+
170
+ expect(minWeightedPath?.minPath?.[0]?.key).toBe('Intersection_1')
171
+ expect(minWeightedPath?.minPath?.[1]?.key).toBe('Intersection_2')
172
+ expect(minWeightedPath?.minPath?.[2]?.key).toBe('Intersection_3')
173
+ expect(minWeightedPath?.minPath?.[3]?.key).toBe('Intersection_4')
174
+ expect(minWeightedPath?.minPath?.[4]?.key).toBe('Intersection_5')
166
175
  });
167
176
  });