data-structure-typed 2.6.0 → 2.6.1

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.
Files changed (80) hide show
  1. package/.github/workflows/ci.yml +7 -2
  2. package/.github/workflows/release-package.yml +9 -2
  3. package/docs-site-docusaurus/docs/api/classes/AVLTree.md +108 -108
  4. package/docs-site-docusaurus/docs/api/classes/BST.md +101 -101
  5. package/docs-site-docusaurus/docs/api/classes/BinaryIndexedTree.md +13 -13
  6. package/docs-site-docusaurus/docs/api/classes/BinaryTree.md +66 -66
  7. package/docs-site-docusaurus/docs/api/classes/Deque.md +235 -51
  8. package/docs-site-docusaurus/docs/api/classes/DirectedGraph.md +21 -21
  9. package/docs-site-docusaurus/docs/api/classes/DoublyLinkedList.md +231 -67
  10. package/docs-site-docusaurus/docs/api/classes/FibonacciHeap.md +9 -9
  11. package/docs-site-docusaurus/docs/api/classes/FibonacciHeapNode.md +1 -1
  12. package/docs-site-docusaurus/docs/api/classes/HashMap.md +14 -14
  13. package/docs-site-docusaurus/docs/api/classes/Heap.md +117 -34
  14. package/docs-site-docusaurus/docs/api/classes/IterableElementBase.md +83 -13
  15. package/docs-site-docusaurus/docs/api/classes/LinearBase.md +124 -20
  16. package/docs-site-docusaurus/docs/api/classes/LinearLinkedBase.md +140 -32
  17. package/docs-site-docusaurus/docs/api/classes/LinkedHashMap.md +23 -23
  18. package/docs-site-docusaurus/docs/api/classes/LinkedListQueue.md +159 -51
  19. package/docs-site-docusaurus/docs/api/classes/MapGraph.md +20 -20
  20. package/docs-site-docusaurus/docs/api/classes/Matrix.md +23 -23
  21. package/docs-site-docusaurus/docs/api/classes/MaxHeap.md +117 -34
  22. package/docs-site-docusaurus/docs/api/classes/MaxPriorityQueue.md +117 -34
  23. package/docs-site-docusaurus/docs/api/classes/MinHeap.md +117 -34
  24. package/docs-site-docusaurus/docs/api/classes/MinPriorityQueue.md +117 -34
  25. package/docs-site-docusaurus/docs/api/classes/PriorityQueue.md +117 -34
  26. package/docs-site-docusaurus/docs/api/classes/Queue.md +142 -34
  27. package/docs-site-docusaurus/docs/api/classes/RedBlackTree.md +117 -117
  28. package/docs-site-docusaurus/docs/api/classes/SegmentTree.md +8 -8
  29. package/docs-site-docusaurus/docs/api/classes/SinglyLinkedList.md +158 -50
  30. package/docs-site-docusaurus/docs/api/classes/SkipList.md +21 -21
  31. package/docs-site-docusaurus/docs/api/classes/Stack.md +108 -26
  32. package/docs-site-docusaurus/docs/api/classes/TreeMap.md +33 -33
  33. package/docs-site-docusaurus/docs/api/classes/TreeMultiMap.md +75 -39
  34. package/docs-site-docusaurus/docs/api/classes/TreeSet.md +301 -39
  35. package/docs-site-docusaurus/docs/api/classes/Trie.md +110 -28
  36. package/docs-site-docusaurus/docs/api/classes/UndirectedGraph.md +20 -20
  37. package/package.json +45 -46
  38. package/src/common/error.ts +15 -32
  39. package/src/common/index.ts +0 -3
  40. package/src/data-structures/base/iterable-element-base.ts +0 -3
  41. package/src/data-structures/base/linear-base.ts +2 -36
  42. package/src/data-structures/binary-tree/avl-tree.ts +31 -529
  43. package/src/data-structures/binary-tree/binary-indexed-tree.ts +47 -572
  44. package/src/data-structures/binary-tree/binary-tree.ts +326 -1311
  45. package/src/data-structures/binary-tree/bst.ts +158 -1082
  46. package/src/data-structures/binary-tree/red-black-tree.ts +451 -1290
  47. package/src/data-structures/binary-tree/segment-tree.ts +73 -351
  48. package/src/data-structures/binary-tree/tree-map.ts +462 -5124
  49. package/src/data-structures/binary-tree/tree-multi-map.ts +302 -4914
  50. package/src/data-structures/binary-tree/tree-multi-set.ts +284 -3972
  51. package/src/data-structures/binary-tree/tree-set.ts +338 -4836
  52. package/src/data-structures/graph/abstract-graph.ts +98 -167
  53. package/src/data-structures/graph/directed-graph.ts +137 -562
  54. package/src/data-structures/graph/map-graph.ts +0 -3
  55. package/src/data-structures/graph/undirected-graph.ts +132 -511
  56. package/src/data-structures/hash/hash-map.ts +154 -582
  57. package/src/data-structures/heap/heap.ts +200 -795
  58. package/src/data-structures/linked-list/doubly-linked-list.ts +121 -865
  59. package/src/data-structures/linked-list/singly-linked-list.ts +122 -794
  60. package/src/data-structures/linked-list/skip-linked-list.ts +211 -918
  61. package/src/data-structures/matrix/matrix.ts +179 -518
  62. package/src/data-structures/matrix/navigator.ts +0 -1
  63. package/src/data-structures/priority-queue/max-priority-queue.ts +1 -6
  64. package/src/data-structures/priority-queue/min-priority-queue.ts +6 -11
  65. package/src/data-structures/priority-queue/priority-queue.ts +1 -2
  66. package/src/data-structures/queue/deque.ts +214 -882
  67. package/src/data-structures/queue/queue.ts +102 -625
  68. package/src/data-structures/stack/stack.ts +76 -505
  69. package/src/data-structures/trie/trie.ts +98 -628
  70. package/src/types/common.ts +0 -10
  71. package/src/types/data-structures/binary-tree/bst.ts +0 -7
  72. package/src/types/data-structures/binary-tree/red-black-tree.ts +0 -1
  73. package/src/types/data-structures/graph/abstract-graph.ts +0 -2
  74. package/src/types/data-structures/hash/hash-map.ts +0 -3
  75. package/src/types/data-structures/hash/index.ts +0 -1
  76. package/src/types/data-structures/matrix/navigator.ts +0 -2
  77. package/src/types/utils/utils.ts +0 -7
  78. package/src/types/utils/validate-type.ts +0 -7
  79. package/src/utils/number.ts +0 -2
  80. package/src/utils/utils.ts +0 -5
@@ -5,7 +5,6 @@
5
5
  * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
6
  * @license MIT License
7
7
  */
8
-
9
8
  import type { DijkstraResult, EntryCallback, GraphOptions, VertexKey } from '../../types';
10
9
  import { uuidV4 } from '../../utils';
11
10
  import { ERR, raise } from '../../common';
@@ -35,7 +34,6 @@ export abstract class AbstractEdge<E = any> {
35
34
  }
36
35
 
37
36
  protected _hashCode: string;
38
-
39
37
  get hashCode(): string {
40
38
  return this._hashCode;
41
39
  }
@@ -71,13 +69,11 @@ export abstract class AbstractGraph<
71
69
  }
72
70
 
73
71
  protected _options: GraphOptions<V> = { defaultEdgeWeight: 1 };
74
-
75
72
  get options(): Readonly<GraphOptions<V>> {
76
73
  return this._options;
77
74
  }
78
75
 
79
76
  protected _vertexMap: Map<VertexKey, VO> = new Map<VertexKey, VO>();
80
-
81
77
  get vertexMap(): Map<VertexKey, VO> {
82
78
  return this._vertexMap;
83
79
  }
@@ -90,6 +86,14 @@ export abstract class AbstractGraph<
90
86
  return this._vertexMap.size;
91
87
  }
92
88
 
89
+ /**
90
+ * The edge connector string used in visual output.
91
+ * Override in subclasses (e.g., '--' for undirected, '->' for directed).
92
+ */
93
+ protected get _edgeConnector(): string {
94
+ return '--';
95
+ }
96
+
93
97
  /**
94
98
  * Create a new vertex instance (implementation specific).
95
99
  * @param key - Vertex identifier.
@@ -275,7 +279,10 @@ export abstract class AbstractGraph<
275
279
  const newEdge = this.createEdge(srcOrEdge, dest, weight, value);
276
280
  return this._addEdge(newEdge);
277
281
  } else {
278
- raise(TypeError, ERR.invalidArgument('dest must be a Vertex or vertex key when srcOrEdge is an Edge.', 'Graph'));
282
+ raise(
283
+ TypeError,
284
+ ERR.invalidArgument('dest must be a Vertex or vertex key when srcOrEdge is an Edge.', 'Graph')
285
+ );
279
286
  }
280
287
  }
281
288
  }
@@ -310,22 +317,17 @@ export abstract class AbstractGraph<
310
317
  const paths: VO[][] = [];
311
318
  const vertex1 = this._getVertex(v1);
312
319
  const vertex2 = this._getVertex(v2);
313
-
314
320
  if (!(vertex1 && vertex2)) {
315
321
  return [];
316
322
  }
317
-
318
323
  const stack: { vertex: VO; path: VO[] }[] = [];
319
324
  stack.push({ vertex: vertex1, path: [vertex1] });
320
-
321
325
  while (stack.length > 0) {
322
326
  const { vertex, path } = stack.pop()!;
323
-
324
327
  if (vertex === vertex2) {
325
328
  paths.push(path);
326
329
  if (paths.length >= limit) return paths;
327
330
  }
328
-
329
331
  const neighbors = this.getNeighbors(vertex);
330
332
  for (const neighbor of neighbors) {
331
333
  if (!path.includes(neighbor)) {
@@ -361,7 +363,6 @@ export abstract class AbstractGraph<
361
363
  */
362
364
  getMinCostBetween(v1: VO | VertexKey, v2: VO | VertexKey, isWeight?: boolean): number | undefined {
363
365
  if (isWeight === undefined) isWeight = false;
364
-
365
366
  if (isWeight) {
366
367
  const allPaths = this.getAllPathsBetween(v1, v2);
367
368
  let min = Number.MAX_SAFE_INTEGER;
@@ -375,7 +376,6 @@ export abstract class AbstractGraph<
375
376
  if (!(vertex1 && vertex2)) {
376
377
  return undefined;
377
378
  }
378
-
379
379
  const visited: Map<VO, boolean> = new Map();
380
380
  const queue = new Queue<VO>([vertex1]);
381
381
  visited.set(vertex1, true);
@@ -386,7 +386,6 @@ export abstract class AbstractGraph<
386
386
  if (cur === vertex2) {
387
387
  return cost;
388
388
  }
389
-
390
389
  if (cur !== undefined) {
391
390
  const neighbors = this.getNeighbors(cur);
392
391
  for (const neighbor of neighbors) {
@@ -414,7 +413,6 @@ export abstract class AbstractGraph<
414
413
  */
415
414
  getMinPathBetween(v1: VO | VertexKey, v2: VO | VertexKey, isWeight?: boolean, isDFS = false): VO[] | undefined {
416
415
  if (isWeight === undefined) isWeight = false;
417
-
418
416
  if (isWeight) {
419
417
  if (isDFS) {
420
418
  const allPaths = this.getAllPathsBetween(v1, v2, 10000);
@@ -447,14 +445,12 @@ export abstract class AbstractGraph<
447
445
  const vertex1 = this._getVertex(v1);
448
446
  const vertex2 = this._getVertex(v2);
449
447
  if (!(vertex1 && vertex2)) return [];
450
-
451
448
  const dfs = (cur: VO, dest: VO, visiting: Set<VO>, path: VO[]) => {
452
449
  visiting.add(cur);
453
450
  if (cur === dest) {
454
451
  minPath = [vertex1, ...path];
455
452
  return;
456
453
  }
457
-
458
454
  const neighbors = this.getNeighbors(cur);
459
455
  for (const neighbor of neighbors) {
460
456
  if (!visiting.has(neighbor)) {
@@ -463,10 +459,8 @@ export abstract class AbstractGraph<
463
459
  path.pop();
464
460
  }
465
461
  }
466
-
467
462
  visiting.delete(cur);
468
463
  };
469
-
470
464
  dfs(vertex1, vertex2, new Set<VO>(), []);
471
465
  return minPath;
472
466
  }
@@ -491,26 +485,21 @@ export abstract class AbstractGraph<
491
485
  let minDest: VO | undefined = undefined;
492
486
  let minPath: VO[] = [];
493
487
  const paths: VO[][] = [];
494
-
495
488
  const vertexMap = this._vertexMap;
496
489
  const distMap: Map<VO, number> = new Map();
497
490
  const seen: Set<VO> = new Set();
498
491
  const preMap: Map<VO, VO | undefined> = new Map();
499
492
  const srcVertex = this._getVertex(src);
500
-
501
493
  const destVertex = dest ? this._getVertex(dest) : undefined;
502
-
503
494
  if (!srcVertex) {
504
495
  return undefined;
505
496
  }
506
-
507
497
  for (const vertex of vertexMap) {
508
498
  const vertexOrKey = vertex[1];
509
499
  if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Number.MAX_SAFE_INTEGER);
510
500
  }
511
501
  distMap.set(srcVertex, 0);
512
502
  preMap.set(srcVertex, undefined);
513
-
514
503
  const getMinOfNoSeen = () => {
515
504
  let min = Number.MAX_SAFE_INTEGER;
516
505
  let minV: VO | undefined = undefined;
@@ -524,11 +513,9 @@ export abstract class AbstractGraph<
524
513
  }
525
514
  return minV;
526
515
  };
527
-
528
516
  const getPaths = (minV: VO | undefined) => {
529
517
  for (const vertex of vertexMap) {
530
518
  const vertexOrKey = vertex[1];
531
-
532
519
  if (vertexOrKey instanceof AbstractVertex) {
533
520
  const path: VO[] = [vertexOrKey];
534
521
  let parent = preMap.get(vertexOrKey);
@@ -542,7 +529,6 @@ export abstract class AbstractGraph<
542
529
  }
543
530
  }
544
531
  };
545
-
546
532
  for (let i = 1; i < vertexMap.size; i++) {
547
533
  const cur = getMinOfNoSeen();
548
534
  if (cur) {
@@ -563,7 +549,6 @@ export abstract class AbstractGraph<
563
549
  if (edge) {
564
550
  const curFromMap = distMap.get(cur);
565
551
  const neighborFromMap = distMap.get(neighbor);
566
-
567
552
  if (curFromMap !== undefined && neighborFromMap !== undefined) {
568
553
  if (edge.weight + curFromMap < neighborFromMap) {
569
554
  distMap.set(neighbor, edge.weight + curFromMap);
@@ -575,7 +560,6 @@ export abstract class AbstractGraph<
575
560
  }
576
561
  }
577
562
  }
578
-
579
563
  if (getMinDist)
580
564
  distMap.forEach((d, v) => {
581
565
  if (v !== srcVertex) {
@@ -585,9 +569,7 @@ export abstract class AbstractGraph<
585
569
  }
586
570
  }
587
571
  });
588
-
589
572
  if (genPaths) getPaths(minDest);
590
-
591
573
  return { distMap, preMap, seen, paths, minDist, minPath };
592
574
  }
593
575
 
@@ -605,23 +587,17 @@ export abstract class AbstractGraph<
605
587
  const distMap: Map<VO, number> = new Map();
606
588
  const seen: Set<VO> = new Set();
607
589
  const preMap: Map<VO, VO | undefined> = new Map();
608
-
609
590
  const srcVertex = this._getVertex(src);
610
591
  const destVertex = dest ? this._getVertex(dest) : undefined;
611
-
612
592
  if (!srcVertex) return undefined;
613
-
614
593
  for (const vertex of vertexMap) {
615
594
  const vertexOrKey = vertex[1];
616
595
  if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Number.MAX_SAFE_INTEGER);
617
596
  }
618
-
619
597
  const heap = new Heap<{ key: number; value: VO }>([], { comparator: (a, b) => a.key - b.key });
620
598
  heap.add({ key: 0, value: srcVertex });
621
-
622
599
  distMap.set(srcVertex, 0);
623
600
  preMap.set(srcVertex, undefined);
624
-
625
601
  const getPaths = (minV: VO | undefined) => {
626
602
  for (const vertex of vertexMap) {
627
603
  const vertexOrKey = vertex[1];
@@ -638,7 +614,6 @@ export abstract class AbstractGraph<
638
614
  }
639
615
  }
640
616
  };
641
-
642
617
  while (heap.size > 0) {
643
618
  const curHeapNode = heap.poll();
644
619
  const dist = curHeapNode?.key;
@@ -674,7 +649,6 @@ export abstract class AbstractGraph<
674
649
  }
675
650
  }
676
651
  }
677
-
678
652
  if (getMinDist) {
679
653
  distMap.forEach((d, v) => {
680
654
  if (v !== srcVertex) {
@@ -685,11 +659,9 @@ export abstract class AbstractGraph<
685
659
  }
686
660
  });
687
661
  }
688
-
689
662
  if (genPaths) {
690
663
  getPaths(minDest);
691
664
  }
692
-
693
665
  return { distMap, preMap, seen, paths, minDist, minPath };
694
666
  }
695
667
 
@@ -705,29 +677,23 @@ export abstract class AbstractGraph<
705
677
  bellmanFord(src: VO | VertexKey, scanNegativeCycle?: boolean, getMin?: boolean, genPath?: boolean) {
706
678
  if (getMin === undefined) getMin = false;
707
679
  if (genPath === undefined) genPath = false;
708
-
709
680
  const srcVertex = this._getVertex(src);
710
681
  const paths: VO[][] = [];
711
682
  const distMap: Map<VO, number> = new Map();
712
683
  const preMap: Map<VO, VO> = new Map();
713
684
  let min = Number.MAX_SAFE_INTEGER;
714
685
  let minPath: VO[] = [];
715
-
716
686
  let hasNegativeCycle: boolean | undefined;
717
687
  if (scanNegativeCycle) hasNegativeCycle = false;
718
688
  if (!srcVertex) return { hasNegativeCycle, distMap, preMap, paths, min, minPath };
719
-
720
689
  const vertexMap = this._vertexMap;
721
690
  const numOfVertices = vertexMap.size;
722
691
  const edgeMap = this.edgeSet();
723
692
  const numOfEdges = edgeMap.length;
724
-
725
693
  this._vertexMap.forEach(vertex => {
726
694
  distMap.set(vertex, Number.MAX_SAFE_INTEGER);
727
695
  });
728
-
729
696
  distMap.set(srcVertex, 0);
730
-
731
697
  for (let i = 1; i < numOfVertices; ++i) {
732
698
  for (let j = 0; j < numOfEdges; ++j) {
733
699
  const ends = this.getEndsOfEdge(edgeMap[j]);
@@ -745,7 +711,6 @@ export abstract class AbstractGraph<
745
711
  }
746
712
  }
747
713
  }
748
-
749
714
  let minDest: VO | undefined = undefined;
750
715
  if (getMin) {
751
716
  distMap.forEach((d, v) => {
@@ -757,7 +722,6 @@ export abstract class AbstractGraph<
757
722
  }
758
723
  });
759
724
  }
760
-
761
725
  if (genPath) {
762
726
  for (const vertex of vertexMap) {
763
727
  const vertexOrKey = vertex[1];
@@ -774,7 +738,6 @@ export abstract class AbstractGraph<
774
738
  }
775
739
  }
776
740
  }
777
-
778
741
  for (let j = 0; j < numOfEdges; ++j) {
779
742
  const ends = this.getEndsOfEdge(edgeMap[j]);
780
743
  if (ends) {
@@ -786,7 +749,6 @@ export abstract class AbstractGraph<
786
749
  }
787
750
  }
788
751
  }
789
-
790
752
  return { hasNegativeCycle, distMap, preMap, paths, min, minPath };
791
753
  }
792
754
 
@@ -798,10 +760,8 @@ export abstract class AbstractGraph<
798
760
  floydWarshall(): { costs: number[][]; predecessor: (VO | undefined)[][] } {
799
761
  const idAndVertices = [...this._vertexMap];
800
762
  const n = idAndVertices.length;
801
-
802
763
  const costs: number[][] = [];
803
764
  const predecessor: (VO | undefined)[][] = [];
804
-
805
765
  for (let i = 0; i < n; i++) {
806
766
  costs[i] = [];
807
767
  predecessor[i] = [];
@@ -809,13 +769,11 @@ export abstract class AbstractGraph<
809
769
  predecessor[i][j] = undefined;
810
770
  }
811
771
  }
812
-
813
772
  for (let i = 0; i < n; i++) {
814
773
  for (let j = 0; j < n; j++) {
815
774
  costs[i][j] = this.getEdge(idAndVertices[i][1], idAndVertices[j][1])?.weight || Number.MAX_SAFE_INTEGER;
816
775
  }
817
776
  }
818
-
819
777
  for (let k = 0; k < n; k++) {
820
778
  for (let i = 0; i < n; i++) {
821
779
  for (let j = 0; j < n; j++) {
@@ -838,7 +796,6 @@ export abstract class AbstractGraph<
838
796
  getCycles(isInclude2Cycle: boolean = false): VertexKey[][] {
839
797
  const cycles: VertexKey[][] = [];
840
798
  const visited: Set<VO> = new Set();
841
-
842
799
  const dfs = (vertex: VO, currentPath: VertexKey[], visited: Set<VO>) => {
843
800
  if (visited.has(vertex)) {
844
801
  if (
@@ -849,27 +806,20 @@ export abstract class AbstractGraph<
849
806
  }
850
807
  return;
851
808
  }
852
-
853
809
  visited.add(vertex);
854
810
  currentPath.push(vertex.key);
855
-
856
811
  for (const neighbor of this.getNeighbors(vertex)) {
857
812
  if (neighbor) dfs(neighbor, currentPath, visited);
858
813
  }
859
-
860
814
  visited.delete(vertex);
861
815
  currentPath.pop();
862
816
  };
863
-
864
817
  for (const vertex of this.vertexMap.values()) {
865
818
  dfs(vertex, [], visited);
866
819
  }
867
-
868
820
  const uniqueCycles = new Map<string, VertexKey[]>();
869
-
870
821
  for (const cycle of cycles) {
871
822
  const sorted = [...cycle].sort().toString();
872
-
873
823
  if (uniqueCycles.has(sorted)) continue;
874
824
  else {
875
825
  uniqueCycles.set(sorted, cycle);
@@ -935,6 +885,8 @@ export abstract class AbstractGraph<
935
885
  return mapped;
936
886
  }
937
887
 
888
+ // ===== Same-species factory & cloning helpers =====
889
+
938
890
  /**
939
891
  * Create a deep clone of the graph with the same species.
940
892
  * @remarks Time O(V + E), Space O(V + E)
@@ -948,7 +900,90 @@ export abstract class AbstractGraph<
948
900
  return this._createLike(undefined, this._snapshotOptions());
949
901
  }
950
902
 
951
- // ===== Same-species factory & cloning helpers =====
903
+ /**
904
+ * Generate a text-based visual representation of the graph.
905
+ *
906
+ * **Adjacency list format:**
907
+ * ```
908
+ * Graph (5 vertices, 6 edges):
909
+ * A -> B (1), C (2)
910
+ * B -> D (3)
911
+ * C -> (no outgoing edges)
912
+ * D -> A (1)
913
+ * E (isolated)
914
+ * ```
915
+ *
916
+ * @param options - Optional display settings.
917
+ * @param options.showWeight - Whether to show edge weights (default: true).
918
+ * @returns The visual string.
919
+ */
920
+ override toVisual(options?: { showWeight?: boolean }): string {
921
+ const showWeight = options?.showWeight ?? true;
922
+ const vertices = [...this._vertexMap.values()];
923
+ const vertexCount = vertices.length;
924
+ const edgeCount = this.edgeSet().length;
925
+ const lines: string[] = [`Graph (${vertexCount} vertices, ${edgeCount} edges):`];
926
+ for (const vertex of vertices) {
927
+ const neighbors = this.getNeighbors(vertex);
928
+ if (neighbors.length === 0) {
929
+ lines.push(` ${vertex.key} (isolated)`);
930
+ } else {
931
+ const edgeStrs = neighbors.map(neighbor => {
932
+ const edge = this.getEdge(vertex, neighbor);
933
+ if (edge && showWeight && edge.weight !== undefined && edge.weight !== 1) {
934
+ return `${neighbor.key} (${edge.weight})`;
935
+ }
936
+ return `${neighbor.key}`;
937
+ });
938
+ lines.push(` ${vertex.key} ${this._edgeConnector} ${edgeStrs.join(', ')}`);
939
+ }
940
+ }
941
+ return lines.join('\n');
942
+ }
943
+
944
+ /**
945
+ * Generate DOT language representation for Graphviz.
946
+ *
947
+ * @param options - Optional display settings.
948
+ * @param options.name - Graph name (default: 'G').
949
+ * @param options.showWeight - Whether to label edges with weight (default: true).
950
+ * @returns DOT format string.
951
+ */
952
+ toDot(options?: { name?: string; showWeight?: boolean }): string {
953
+ const name = options?.name ?? 'G';
954
+ const showWeight = options?.showWeight ?? true;
955
+ const isDirected = this._edgeConnector === '->';
956
+ const graphType = isDirected ? 'digraph' : 'graph';
957
+ const edgeOp = isDirected ? '->' : '--';
958
+ const lines: string[] = [`${graphType} ${name} {`];
959
+ // Add all vertices (ensures isolated vertices appear)
960
+ for (const vertex of this._vertexMap.values()) {
961
+ lines.push(` "${vertex.key}";`);
962
+ }
963
+ // Add edges
964
+ const visited = new Set<string>();
965
+ for (const vertex of this._vertexMap.values()) {
966
+ for (const neighbor of this.getNeighbors(vertex)) {
967
+ const edgeId = isDirected ? `${vertex.key}->${neighbor.key}` : [vertex.key, neighbor.key].sort().join('--');
968
+ if (visited.has(edgeId)) continue;
969
+ visited.add(edgeId);
970
+ const edge = this.getEdge(vertex, neighbor);
971
+ const label =
972
+ edge && showWeight && edge.weight !== undefined && edge.weight !== 1 ? ` [label="${edge.weight}"]` : '';
973
+ lines.push(` "${vertex.key}" ${edgeOp} "${neighbor.key}"${label};`);
974
+ }
975
+ }
976
+ lines.push('}');
977
+ return lines.join('\n');
978
+ }
979
+
980
+ /**
981
+ * Print the graph to console.
982
+ * @param options - Display settings passed to `toVisual`.
983
+ */
984
+ override print(options?: { showWeight?: boolean }): void {
985
+ console.log(this.toVisual(options));
986
+ }
952
987
 
953
988
  /**
954
989
  * Internal iterator over `[key, value]` entries in insertion order.
@@ -1079,108 +1114,4 @@ export abstract class AbstractGraph<
1079
1114
  protected _getVertexKey(vertexOrKey: VO | VertexKey): VertexKey {
1080
1115
  return vertexOrKey instanceof AbstractVertex ? vertexOrKey.key : vertexOrKey;
1081
1116
  }
1082
-
1083
- /**
1084
- * The edge connector string used in visual output.
1085
- * Override in subclasses (e.g., '--' for undirected, '->' for directed).
1086
- */
1087
- protected get _edgeConnector(): string {
1088
- return '--';
1089
- }
1090
-
1091
- /**
1092
- * Generate a text-based visual representation of the graph.
1093
- *
1094
- * **Adjacency list format:**
1095
- * ```
1096
- * Graph (5 vertices, 6 edges):
1097
- * A -> B (1), C (2)
1098
- * B -> D (3)
1099
- * C -> (no outgoing edges)
1100
- * D -> A (1)
1101
- * E (isolated)
1102
- * ```
1103
- *
1104
- * @param options - Optional display settings.
1105
- * @param options.showWeight - Whether to show edge weights (default: true).
1106
- * @returns The visual string.
1107
- */
1108
- override toVisual(options?: { showWeight?: boolean }): string {
1109
- const showWeight = options?.showWeight ?? true;
1110
- const vertices = [...this._vertexMap.values()];
1111
- const vertexCount = vertices.length;
1112
- const edgeCount = this.edgeSet().length;
1113
-
1114
- const lines: string[] = [`Graph (${vertexCount} vertices, ${edgeCount} edges):`];
1115
-
1116
- for (const vertex of vertices) {
1117
- const neighbors = this.getNeighbors(vertex);
1118
- if (neighbors.length === 0) {
1119
- lines.push(` ${vertex.key} (isolated)`);
1120
- } else {
1121
- const edgeStrs = neighbors.map(neighbor => {
1122
- const edge = this.getEdge(vertex, neighbor);
1123
- if (edge && showWeight && edge.weight !== undefined && edge.weight !== 1) {
1124
- return `${neighbor.key} (${edge.weight})`;
1125
- }
1126
- return `${neighbor.key}`;
1127
- });
1128
- lines.push(` ${vertex.key} ${this._edgeConnector} ${edgeStrs.join(', ')}`);
1129
- }
1130
- }
1131
-
1132
- return lines.join('\n');
1133
- }
1134
-
1135
- /**
1136
- * Generate DOT language representation for Graphviz.
1137
- *
1138
- * @param options - Optional display settings.
1139
- * @param options.name - Graph name (default: 'G').
1140
- * @param options.showWeight - Whether to label edges with weight (default: true).
1141
- * @returns DOT format string.
1142
- */
1143
- toDot(options?: { name?: string; showWeight?: boolean }): string {
1144
- const name = options?.name ?? 'G';
1145
- const showWeight = options?.showWeight ?? true;
1146
- const isDirected = this._edgeConnector === '->';
1147
- const graphType = isDirected ? 'digraph' : 'graph';
1148
- const edgeOp = isDirected ? '->' : '--';
1149
-
1150
- const lines: string[] = [`${graphType} ${name} {`];
1151
-
1152
- // Add all vertices (ensures isolated vertices appear)
1153
- for (const vertex of this._vertexMap.values()) {
1154
- lines.push(` "${vertex.key}";`);
1155
- }
1156
-
1157
- // Add edges
1158
- const visited = new Set<string>();
1159
- for (const vertex of this._vertexMap.values()) {
1160
- for (const neighbor of this.getNeighbors(vertex)) {
1161
- const edgeId = isDirected
1162
- ? `${vertex.key}->${neighbor.key}`
1163
- : [vertex.key, neighbor.key].sort().join('--');
1164
- if (visited.has(edgeId)) continue;
1165
- visited.add(edgeId);
1166
-
1167
- const edge = this.getEdge(vertex, neighbor);
1168
- const label = edge && showWeight && edge.weight !== undefined && edge.weight !== 1
1169
- ? ` [label="${edge.weight}"]`
1170
- : '';
1171
- lines.push(` "${vertex.key}" ${edgeOp} "${neighbor.key}"${label};`);
1172
- }
1173
- }
1174
-
1175
- lines.push('}');
1176
- return lines.join('\n');
1177
- }
1178
-
1179
- /**
1180
- * Print the graph to console.
1181
- * @param options - Display settings passed to `toVisual`.
1182
- */
1183
- override print(options?: { showWeight?: boolean }): void {
1184
- console.log(this.toVisual(options));
1185
- }
1186
1117
  }