data-structure-typed 2.2.2 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +311 -1687
- package/README_CN.md +509 -0
- package/SECURITY.md +962 -11
- package/SECURITY.zh-CN.md +966 -0
- package/SPECIFICATION.md +689 -30
- package/SPECIFICATION.zh-CN.md +715 -0
- package/SPONSOR.zh-CN.md +62 -0
- package/SPONSOR_POLISHED.md +62 -0
- package/benchmark/report.html +1 -1
- package/benchmark/report.json +215 -172
- package/dist/cjs/index.cjs +163 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +164 -0
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +163 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +164 -0
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +96 -2
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
- package/dist/types/data-structures/binary-tree/bst.d.ts +156 -13
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +84 -35
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -2
- package/dist/types/data-structures/graph/directed-graph.d.ts +126 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +160 -1
- package/dist/types/data-structures/hash/hash-map.d.ts +110 -27
- package/dist/types/data-structures/heap/heap.d.ts +107 -58
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +72 -404
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +121 -5
- package/dist/types/data-structures/queue/deque.d.ts +95 -67
- package/dist/types/data-structures/queue/queue.d.ts +90 -34
- package/dist/types/data-structures/stack/stack.d.ts +58 -40
- package/dist/types/data-structures/trie/trie.d.ts +109 -47
- package/dist/types/interfaces/binary-tree.d.ts +1 -0
- package/dist/umd/data-structure-typed.js +164 -0
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +3 -2
- package/src/data-structures/binary-tree/avl-tree.ts +96 -2
- package/src/data-structures/binary-tree/binary-tree.ts +117 -7
- package/src/data-structures/binary-tree/bst.ts +322 -13
- package/src/data-structures/binary-tree/red-black-tree.ts +84 -35
- package/src/data-structures/binary-tree/tree-multi-map.ts +2 -2
- package/src/data-structures/graph/directed-graph.ts +126 -1
- package/src/data-structures/graph/undirected-graph.ts +160 -1
- package/src/data-structures/hash/hash-map.ts +110 -27
- package/src/data-structures/heap/heap.ts +107 -58
- package/src/data-structures/linked-list/doubly-linked-list.ts +72 -404
- package/src/data-structures/linked-list/singly-linked-list.ts +121 -5
- package/src/data-structures/queue/deque.ts +95 -67
- package/src/data-structures/queue/queue.ts +90 -34
- package/src/data-structures/stack/stack.ts +58 -40
- package/src/data-structures/trie/trie.ts +109 -47
- package/src/interfaces/binary-tree.ts +2 -0
- package/test/performance/benchmark-runner.ts +14 -11
- package/test/performance/data-structures/binary-tree/avl-tree.test.ts +8 -8
- package/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts +8 -8
- package/test/performance/data-structures/binary-tree/binary-tree.test.ts +6 -6
- package/test/performance/data-structures/binary-tree/bst.test.ts +5 -5
- package/test/performance/data-structures/binary-tree/red-black-tree.test.ts +10 -10
- package/test/performance/reportor.ts +2 -1
- package/test/performance/single-suite-runner.ts +7 -4
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +117 -0
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +166 -0
- package/test/unit/data-structures/binary-tree/bst.test.ts +766 -8
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +89 -37
- package/test/unit/data-structures/graph/directed-graph.test.ts +133 -0
- package/test/unit/data-structures/graph/undirected-graph.test.ts +167 -0
- package/test/unit/data-structures/hash/hash-map.test.ts +149 -3
- package/test/unit/data-structures/heap/heap.test.ts +182 -47
- package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +118 -14
- package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +121 -0
- package/test/unit/data-structures/queue/deque.test.ts +98 -67
- package/test/unit/data-structures/queue/queue.test.ts +85 -51
- package/test/unit/data-structures/stack/stack.test.ts +142 -33
- package/test/unit/data-structures/trie/trie.test.ts +135 -39
- package/tsup.leetcode.config.js +99 -0
- package/typedoc.json +2 -1
- package/POSTS_zh-CN.md +0 -54
- package/README_zh-CN.md +0 -1208
- package/SPECIFICATION_zh-CN.md +0 -81
|
@@ -838,47 +838,99 @@ describe('real world data', () => {
|
|
|
838
838
|
});
|
|
839
839
|
|
|
840
840
|
describe('classic use', () => {
|
|
841
|
-
it('@example
|
|
842
|
-
//
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
841
|
+
it('@example basic Red-Black Tree with simple number keys', () => {
|
|
842
|
+
// Create a simple Red-Black Tree with numeric keys
|
|
843
|
+
const tree = new RedBlackTree([5, 2, 8, 1, 9]);
|
|
844
|
+
|
|
845
|
+
tree.print();
|
|
846
|
+
// _2___
|
|
847
|
+
// / \
|
|
848
|
+
// 1 _8_
|
|
849
|
+
// / \
|
|
850
|
+
// 5 9
|
|
851
|
+
|
|
852
|
+
// Verify the tree maintains sorted order
|
|
853
|
+
expect([...tree.keys()]).toEqual([1, 2, 5, 8, 9]);
|
|
854
|
+
|
|
855
|
+
// Check size
|
|
856
|
+
expect(tree.size).toBe(5);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it('@example Red-Black Tree with key-value pairs for lookups', () => {
|
|
860
|
+
interface Employee {
|
|
861
|
+
id: number;
|
|
862
|
+
name: string;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Create tree with employee data
|
|
866
|
+
const employees = new RedBlackTree<number, Employee>([
|
|
867
|
+
[1, { id: 1, name: 'Alice' }],
|
|
868
|
+
[3, { id: 3, name: 'Charlie' }],
|
|
869
|
+
[2, { id: 2, name: 'Bob' }]
|
|
870
|
+
]);
|
|
871
|
+
|
|
872
|
+
// Retrieve employee by ID
|
|
873
|
+
const alice = employees.get(1);
|
|
874
|
+
expect(alice?.name).toBe('Alice');
|
|
875
|
+
|
|
876
|
+
// Verify sorted order by ID
|
|
877
|
+
expect([...employees.keys()]).toEqual([1, 2, 3]);
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
it('@example Red-Black Tree range search for filtering', () => {
|
|
881
|
+
interface Product {
|
|
882
|
+
name: string;
|
|
883
|
+
price: number;
|
|
847
884
|
}
|
|
848
885
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
{
|
|
852
|
-
{
|
|
853
|
-
{
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
]
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
type StockTableRecord = StockRecord & { lastUpdated: Date };
|
|
860
|
-
|
|
861
|
-
// Create a Red-Black Tree to index stock records by price
|
|
862
|
-
// Simulates a database index with stock price as the key for quick lookups
|
|
863
|
-
const priceIndex = new RedBlackTree<number, StockTableRecord, StockRecord>(marketStockData, {
|
|
864
|
-
toEntryFn: stockRecord => [
|
|
865
|
-
stockRecord.price, // Use stock price as the key
|
|
866
|
-
{
|
|
867
|
-
...stockRecord,
|
|
868
|
-
lastUpdated: new Date() // Add a timestamp for when the record was indexed
|
|
869
|
-
}
|
|
870
|
-
]
|
|
886
|
+
const products = new RedBlackTree<number, Product>([
|
|
887
|
+
[10, { name: 'Item A', price: 10 }],
|
|
888
|
+
[25, { name: 'Item B', price: 25 }],
|
|
889
|
+
[40, { name: 'Item C', price: 40 }],
|
|
890
|
+
[50, { name: 'Item D', price: 50 }]
|
|
891
|
+
]);
|
|
892
|
+
|
|
893
|
+
// Find products in price range [20, 45]
|
|
894
|
+
const pricesInRange = products.rangeSearch([20, 45], node => {
|
|
895
|
+
return products.get(node)?.name;
|
|
871
896
|
});
|
|
872
897
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
898
|
+
expect(pricesInRange).toEqual(['Item B', 'Item C']);
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
it('@example Red-Black Tree as database index for stock market data', () => {
|
|
902
|
+
interface StockPrice {
|
|
903
|
+
symbol: string;
|
|
904
|
+
volume: number;
|
|
905
|
+
timestamp: Date;
|
|
906
|
+
}
|
|
876
907
|
|
|
877
|
-
//
|
|
878
|
-
const
|
|
879
|
-
[
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
908
|
+
// Simulate real-time stock price index
|
|
909
|
+
const priceIndex = new RedBlackTree<number, StockPrice>([
|
|
910
|
+
[142.5, { symbol: 'AAPL', volume: 1000000, timestamp: new Date() }],
|
|
911
|
+
[335.2, { symbol: 'MSFT', volume: 800000, timestamp: new Date() }],
|
|
912
|
+
[3285.04, { symbol: 'AMZN', volume: 500000, timestamp: new Date() }],
|
|
913
|
+
[267.98, { symbol: 'META', volume: 750000, timestamp: new Date() }],
|
|
914
|
+
[234.57, { symbol: 'GOOGL', volume: 900000, timestamp: new Date() }]
|
|
915
|
+
]);
|
|
916
|
+
|
|
917
|
+
// Find highest-priced stock
|
|
918
|
+
const maxPrice = priceIndex.getRightMost();
|
|
919
|
+
expect(priceIndex.get(maxPrice)?.symbol).toBe('AMZN');
|
|
920
|
+
|
|
921
|
+
// Find stocks in price range [200, 400] for portfolio balancing
|
|
922
|
+
const stocksInRange = priceIndex.rangeSearch([200, 400], node => {
|
|
923
|
+
const stock = priceIndex.get(node);
|
|
924
|
+
return {
|
|
925
|
+
symbol: stock?.symbol,
|
|
926
|
+
price: node,
|
|
927
|
+
volume: stock?.volume
|
|
928
|
+
};
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
expect(stocksInRange.length).toBe(3);
|
|
932
|
+
expect(stocksInRange.some((s: any) => s.symbol === 'GOOGL')).toBe(true);
|
|
933
|
+
expect(stocksInRange.some((s: any) => s.symbol === 'META')).toBe(true);
|
|
934
|
+
expect(stocksInRange.some((s: any) => s.symbol === 'MSFT')).toBe(true);
|
|
883
935
|
});
|
|
884
936
|
});
|
|
@@ -1050,3 +1050,136 @@ describe('delete', () => {
|
|
|
1050
1050
|
//
|
|
1051
1051
|
// })
|
|
1052
1052
|
});
|
|
1053
|
+
|
|
1054
|
+
describe('classic use', () => {
|
|
1055
|
+
it('@example basic DirectedGraph vertex and edge creation', () => {
|
|
1056
|
+
// Create a simple directed graph
|
|
1057
|
+
const graph = new DirectedGraph<string>();
|
|
1058
|
+
|
|
1059
|
+
// Add vertices
|
|
1060
|
+
graph.addVertex('A');
|
|
1061
|
+
graph.addVertex('B');
|
|
1062
|
+
graph.addVertex('C');
|
|
1063
|
+
|
|
1064
|
+
// Verify vertices exist
|
|
1065
|
+
expect(graph.hasVertex('A')).toBe(true);
|
|
1066
|
+
expect(graph.hasVertex('B')).toBe(true);
|
|
1067
|
+
expect(graph.hasVertex('C')).toBe(true);
|
|
1068
|
+
expect(graph.hasVertex('D')).toBe(false);
|
|
1069
|
+
|
|
1070
|
+
// Check vertex count
|
|
1071
|
+
expect(graph.size).toBe(3);
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
it('@example DirectedGraph edge operations', () => {
|
|
1075
|
+
const graph = new DirectedGraph<string>();
|
|
1076
|
+
|
|
1077
|
+
// Add vertices
|
|
1078
|
+
graph.addVertex('A');
|
|
1079
|
+
graph.addVertex('B');
|
|
1080
|
+
graph.addVertex('C');
|
|
1081
|
+
|
|
1082
|
+
// Add directed edges
|
|
1083
|
+
graph.addEdge('A', 'B', 1);
|
|
1084
|
+
graph.addEdge('B', 'C', 2);
|
|
1085
|
+
graph.addEdge('A', 'C', 3);
|
|
1086
|
+
|
|
1087
|
+
// Verify edges exist
|
|
1088
|
+
expect(graph.hasEdge('A', 'B')).toBe(true);
|
|
1089
|
+
expect(graph.hasEdge('B', 'C')).toBe(true);
|
|
1090
|
+
expect(graph.hasEdge('C', 'B')).toBe(false); // Graph is directed
|
|
1091
|
+
|
|
1092
|
+
// Get neighbors of A
|
|
1093
|
+
const neighborsA = graph.getNeighbors('A');
|
|
1094
|
+
expect(neighborsA[0].key).toBe('B');
|
|
1095
|
+
expect(neighborsA[1].key).toBe('C');
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
it('@example DirectedGraph deleteEdge and vertex operations', () => {
|
|
1099
|
+
const graph = new DirectedGraph<string>();
|
|
1100
|
+
|
|
1101
|
+
// Build a small graph
|
|
1102
|
+
graph.addVertex('X');
|
|
1103
|
+
graph.addVertex('Y');
|
|
1104
|
+
graph.addVertex('Z');
|
|
1105
|
+
graph.addEdge('X', 'Y', 1);
|
|
1106
|
+
graph.addEdge('Y', 'Z', 2);
|
|
1107
|
+
|
|
1108
|
+
// Delete an edge
|
|
1109
|
+
graph.deleteEdgeSrcToDest('X', 'Y');
|
|
1110
|
+
expect(graph.hasEdge('X', 'Y')).toBe(false);
|
|
1111
|
+
|
|
1112
|
+
// Edge in other direction should not exist
|
|
1113
|
+
expect(graph.hasEdge('Y', 'X')).toBe(false);
|
|
1114
|
+
|
|
1115
|
+
// Other edges should remain
|
|
1116
|
+
expect(graph.hasEdge('Y', 'Z')).toBe(true);
|
|
1117
|
+
|
|
1118
|
+
// Delete a vertex
|
|
1119
|
+
graph.deleteVertex('Y');
|
|
1120
|
+
expect(graph.hasVertex('Y')).toBe(false);
|
|
1121
|
+
expect(graph.size).toBe(2);
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
it('@example DirectedGraph topologicalSort for task scheduling', () => {
|
|
1125
|
+
const graph = new DirectedGraph<string>();
|
|
1126
|
+
|
|
1127
|
+
// Build a DAG (Directed Acyclic Graph) for task dependencies
|
|
1128
|
+
graph.addVertex('Design');
|
|
1129
|
+
graph.addVertex('Implement');
|
|
1130
|
+
graph.addVertex('Test');
|
|
1131
|
+
graph.addVertex('Deploy');
|
|
1132
|
+
|
|
1133
|
+
// Add dependency edges
|
|
1134
|
+
graph.addEdge('Design', 'Implement', 1); // Design must come before Implement
|
|
1135
|
+
graph.addEdge('Implement', 'Test', 1); // Implement must come before Test
|
|
1136
|
+
graph.addEdge('Test', 'Deploy', 1); // Test must come before Deploy
|
|
1137
|
+
|
|
1138
|
+
// Topological sort gives valid execution order
|
|
1139
|
+
const executionOrder = graph.topologicalSort();
|
|
1140
|
+
expect(executionOrder).toBeDefined();
|
|
1141
|
+
expect(executionOrder).toEqual(['Design', 'Implement', 'Test', 'Deploy']);
|
|
1142
|
+
|
|
1143
|
+
// All vertices should be included
|
|
1144
|
+
expect(executionOrder?.length).toBe(4);
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
it('@example DirectedGraph dijkstra shortest path for network routing', () => {
|
|
1148
|
+
// Build a weighted directed graph representing network nodes and costs
|
|
1149
|
+
const network = new DirectedGraph<string>();
|
|
1150
|
+
|
|
1151
|
+
// Add network nodes
|
|
1152
|
+
network.addVertex('Router-A');
|
|
1153
|
+
network.addVertex('Router-B');
|
|
1154
|
+
network.addVertex('Router-C');
|
|
1155
|
+
network.addVertex('Router-D');
|
|
1156
|
+
network.addVertex('Router-E');
|
|
1157
|
+
|
|
1158
|
+
// Add weighted edges (network latency costs)
|
|
1159
|
+
network.addEdge('Router-A', 'Router-B', 5);
|
|
1160
|
+
network.addEdge('Router-A', 'Router-C', 10);
|
|
1161
|
+
network.addEdge('Router-B', 'Router-D', 3);
|
|
1162
|
+
network.addEdge('Router-C', 'Router-D', 2);
|
|
1163
|
+
network.addEdge('Router-D', 'Router-E', 4);
|
|
1164
|
+
network.addEdge('Router-B', 'Router-E', 12);
|
|
1165
|
+
|
|
1166
|
+
// Find shortest path from Router-A to Router-E
|
|
1167
|
+
const { minDist, minPath } = network.dijkstra('Router-A', 'Router-E', true, true) || {
|
|
1168
|
+
minDist: undefined,
|
|
1169
|
+
minPath: undefined
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
// Verify shortest path is found
|
|
1173
|
+
expect(minDist).toBeDefined();
|
|
1174
|
+
expect(minPath).toBeDefined();
|
|
1175
|
+
|
|
1176
|
+
// Shortest path should be A -> B -> D -> E with cost 5+3+4=12
|
|
1177
|
+
// Or A -> C -> D -> E with cost 10+2+4=16
|
|
1178
|
+
// So the minimum is 12
|
|
1179
|
+
expect(minDist).toBeLessThanOrEqual(16);
|
|
1180
|
+
|
|
1181
|
+
// Verify path is valid (includes start and end)
|
|
1182
|
+
expect(minPath?.[0].key).toBe('Router-A');
|
|
1183
|
+
expect(minPath?.[minPath.length - 1].key).toBe('Router-E');
|
|
1184
|
+
});
|
|
1185
|
+
});
|
|
@@ -613,3 +613,170 @@ describe('UndirectedGraph tarjan', () => {
|
|
|
613
613
|
// expect(getAsVerticesArrays(ccs)).toEqual([["K", "J", "I", "H", "D", "C", "B"], ["G", "F", "E"], ["A"]]);
|
|
614
614
|
// });
|
|
615
615
|
});
|
|
616
|
+
|
|
617
|
+
describe('classic use', () => {
|
|
618
|
+
it('@example basic UndirectedGraph vertex and edge creation', () => {
|
|
619
|
+
// Create a simple undirected graph
|
|
620
|
+
const graph = new UndirectedGraph<string>();
|
|
621
|
+
|
|
622
|
+
// Add vertices
|
|
623
|
+
graph.addVertex('A');
|
|
624
|
+
graph.addVertex('B');
|
|
625
|
+
graph.addVertex('C');
|
|
626
|
+
graph.addVertex('D');
|
|
627
|
+
|
|
628
|
+
// Verify vertices exist
|
|
629
|
+
expect(graph.hasVertex('A')).toBe(true);
|
|
630
|
+
expect(graph.hasVertex('B')).toBe(true);
|
|
631
|
+
expect(graph.hasVertex('E')).toBe(false);
|
|
632
|
+
|
|
633
|
+
// Check vertex count
|
|
634
|
+
expect(graph.size).toBe(4);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it('@example UndirectedGraph edge operations (bidirectional)', () => {
|
|
638
|
+
const graph = new UndirectedGraph<string>();
|
|
639
|
+
|
|
640
|
+
// Add vertices
|
|
641
|
+
graph.addVertex('A');
|
|
642
|
+
graph.addVertex('B');
|
|
643
|
+
graph.addVertex('C');
|
|
644
|
+
|
|
645
|
+
// Add undirected edges (both directions automatically)
|
|
646
|
+
graph.addEdge('A', 'B', 1);
|
|
647
|
+
graph.addEdge('B', 'C', 2);
|
|
648
|
+
graph.addEdge('A', 'C', 3);
|
|
649
|
+
|
|
650
|
+
// Verify edges exist in both directions
|
|
651
|
+
expect(graph.hasEdge('A', 'B')).toBe(true);
|
|
652
|
+
expect(graph.hasEdge('B', 'A')).toBe(true); // Bidirectional!
|
|
653
|
+
|
|
654
|
+
expect(graph.hasEdge('C', 'B')).toBe(true);
|
|
655
|
+
expect(graph.hasEdge('B', 'C')).toBe(true); // Bidirectional!
|
|
656
|
+
|
|
657
|
+
// Get neighbors of A
|
|
658
|
+
const neighborsA = graph.getNeighbors('A');
|
|
659
|
+
expect(neighborsA[0].key).toBe('B');
|
|
660
|
+
expect(neighborsA[1].key).toBe('C');
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
it('@example UndirectedGraph deleteEdge and vertex operations', () => {
|
|
664
|
+
const graph = new UndirectedGraph<string>();
|
|
665
|
+
|
|
666
|
+
// Build a simple undirected graph
|
|
667
|
+
graph.addVertex('X');
|
|
668
|
+
graph.addVertex('Y');
|
|
669
|
+
graph.addVertex('Z');
|
|
670
|
+
graph.addEdge('X', 'Y', 1);
|
|
671
|
+
graph.addEdge('Y', 'Z', 2);
|
|
672
|
+
graph.addEdge('X', 'Z', 3);
|
|
673
|
+
|
|
674
|
+
// Delete an edge
|
|
675
|
+
graph.deleteEdge('X', 'Y');
|
|
676
|
+
expect(graph.hasEdge('X', 'Y')).toBe(false);
|
|
677
|
+
|
|
678
|
+
// Bidirectional deletion confirmed
|
|
679
|
+
expect(graph.hasEdge('Y', 'X')).toBe(false);
|
|
680
|
+
|
|
681
|
+
// Other edges should remain
|
|
682
|
+
expect(graph.hasEdge('Y', 'Z')).toBe(true);
|
|
683
|
+
expect(graph.hasEdge('Z', 'Y')).toBe(true);
|
|
684
|
+
|
|
685
|
+
// Delete a vertex
|
|
686
|
+
graph.deleteVertex('Y');
|
|
687
|
+
expect(graph.hasVertex('Y')).toBe(false);
|
|
688
|
+
expect(graph.size).toBe(2);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
it('@example UndirectedGraph connectivity and neighbors', () => {
|
|
692
|
+
const graph = new UndirectedGraph<string>();
|
|
693
|
+
|
|
694
|
+
// Build a friendship network
|
|
695
|
+
const people = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve'];
|
|
696
|
+
for (const person of people) {
|
|
697
|
+
graph.addVertex(person);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Add friendships (undirected edges)
|
|
701
|
+
graph.addEdge('Alice', 'Bob', 1);
|
|
702
|
+
graph.addEdge('Alice', 'Charlie', 1);
|
|
703
|
+
graph.addEdge('Bob', 'Diana', 1);
|
|
704
|
+
graph.addEdge('Charlie', 'Eve', 1);
|
|
705
|
+
graph.addEdge('Diana', 'Eve', 1);
|
|
706
|
+
|
|
707
|
+
// Get friends of each person
|
|
708
|
+
const aliceFriends = graph.getNeighbors('Alice');
|
|
709
|
+
expect(aliceFriends[0].key).toBe('Bob');
|
|
710
|
+
expect(aliceFriends[1].key).toBe('Charlie');
|
|
711
|
+
expect(aliceFriends.length).toBe(2);
|
|
712
|
+
|
|
713
|
+
const dianaFriends = graph.getNeighbors('Diana');
|
|
714
|
+
expect(dianaFriends[0].key).toBe('Bob');
|
|
715
|
+
expect(dianaFriends[1].key).toBe('Eve');
|
|
716
|
+
expect(dianaFriends.length).toBe(2);
|
|
717
|
+
|
|
718
|
+
// Verify bidirectional friendship
|
|
719
|
+
const bobFriends = graph.getNeighbors('Bob');
|
|
720
|
+
expect(bobFriends[0].key).toBe('Alice'); // Alice -> Bob -> Alice ✓
|
|
721
|
+
expect(bobFriends[1].key).toBe('Diana');
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it('@example UndirectedGraph for social network connectivity analysis', () => {
|
|
725
|
+
interface Person {
|
|
726
|
+
id: number;
|
|
727
|
+
name: string;
|
|
728
|
+
location: string;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// UndirectedGraph is perfect for modeling symmetric relationships
|
|
732
|
+
// (friendships, collaborations, partnerships)
|
|
733
|
+
const socialNetwork = new UndirectedGraph<number, Person>();
|
|
734
|
+
|
|
735
|
+
// Add people as vertices
|
|
736
|
+
const people: [number, Person][] = [
|
|
737
|
+
[1, { id: 1, name: 'Alice', location: 'New York' }],
|
|
738
|
+
[2, { id: 2, name: 'Bob', location: 'San Francisco' }],
|
|
739
|
+
[3, { id: 3, name: 'Charlie', location: 'Boston' }],
|
|
740
|
+
[4, { id: 4, name: 'Diana', location: 'New York' }],
|
|
741
|
+
[5, { id: 5, name: 'Eve', location: 'Seattle' }]
|
|
742
|
+
];
|
|
743
|
+
|
|
744
|
+
for (const [id] of people) {
|
|
745
|
+
socialNetwork.addVertex(id);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Add friendships (automatically bidirectional)
|
|
749
|
+
socialNetwork.addEdge(1, 2, 1); // Alice <-> Bob
|
|
750
|
+
socialNetwork.addEdge(1, 3, 1); // Alice <-> Charlie
|
|
751
|
+
socialNetwork.addEdge(2, 4, 1); // Bob <-> Diana
|
|
752
|
+
socialNetwork.addEdge(3, 5, 1); // Charlie <-> Eve
|
|
753
|
+
socialNetwork.addEdge(4, 5, 1); // Diana <-> Eve
|
|
754
|
+
|
|
755
|
+
expect(socialNetwork.size).toBe(5);
|
|
756
|
+
|
|
757
|
+
// Find direct connections for Alice
|
|
758
|
+
const aliceConnections = socialNetwork.getNeighbors(1);
|
|
759
|
+
expect(aliceConnections[0].key).toBe(2);
|
|
760
|
+
expect(aliceConnections[1].key).toBe(3);
|
|
761
|
+
expect(aliceConnections.length).toBe(2);
|
|
762
|
+
|
|
763
|
+
// Verify bidirectional connections
|
|
764
|
+
expect(socialNetwork.hasEdge(1, 2)).toBe(true);
|
|
765
|
+
expect(socialNetwork.hasEdge(2, 1)).toBe(true); // Friendship works both ways!
|
|
766
|
+
|
|
767
|
+
// Remove a person from network
|
|
768
|
+
socialNetwork.deleteVertex(2); // Bob leaves
|
|
769
|
+
expect(socialNetwork.hasVertex(2)).toBe(false);
|
|
770
|
+
expect(socialNetwork.size).toBe(4);
|
|
771
|
+
|
|
772
|
+
// Alice loses Bob as a friend
|
|
773
|
+
const updatedAliceConnections = socialNetwork.getNeighbors(1);
|
|
774
|
+
expect(updatedAliceConnections[0].key).toBe(3);
|
|
775
|
+
expect(updatedAliceConnections[1]).toBe(undefined);
|
|
776
|
+
|
|
777
|
+
// Diana loses Bob as a friend
|
|
778
|
+
const dianaConnections = socialNetwork.getNeighbors(4);
|
|
779
|
+
expect(dianaConnections[0].key).toBe(5);
|
|
780
|
+
expect(dianaConnections[1]).toBe(undefined);
|
|
781
|
+
});
|
|
782
|
+
});
|
|
@@ -918,7 +918,127 @@ describe('classic uses', () => {
|
|
|
918
918
|
});
|
|
919
919
|
|
|
920
920
|
describe('classic uses', () => {
|
|
921
|
-
it('@example
|
|
921
|
+
it('@example basic HashMap creation and set operation', () => {
|
|
922
|
+
// Create a simple HashMap with key-value pairs
|
|
923
|
+
const map = new HashMap<number, string>([
|
|
924
|
+
[1, 'one'],
|
|
925
|
+
[2, 'two'],
|
|
926
|
+
[3, 'three']
|
|
927
|
+
]);
|
|
928
|
+
|
|
929
|
+
// Verify size
|
|
930
|
+
expect(map.size).toBe(3);
|
|
931
|
+
|
|
932
|
+
// Set a new key-value pair
|
|
933
|
+
map.set(4, 'four');
|
|
934
|
+
expect(map.size).toBe(4);
|
|
935
|
+
|
|
936
|
+
// Verify entries
|
|
937
|
+
expect([...map.entries()]).toHaveLength(4);
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
it('@example HashMap get and has operations', () => {
|
|
941
|
+
const map = new HashMap<string, number>([
|
|
942
|
+
['apple', 1],
|
|
943
|
+
['banana', 2],
|
|
944
|
+
['cherry', 3]
|
|
945
|
+
]);
|
|
946
|
+
|
|
947
|
+
// Check if key exists
|
|
948
|
+
expect(map.has('apple')).toBe(true);
|
|
949
|
+
expect(map.has('date')).toBe(false);
|
|
950
|
+
|
|
951
|
+
// Get value by key
|
|
952
|
+
expect(map.get('banana')).toBe(2);
|
|
953
|
+
expect(map.get('grape')).toBeUndefined();
|
|
954
|
+
|
|
955
|
+
// Get all keys and values
|
|
956
|
+
const keys = [...map.keys()];
|
|
957
|
+
const values = [...map.values()];
|
|
958
|
+
expect(keys).toContain('apple');
|
|
959
|
+
expect(values).toContain(3);
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
it('@example HashMap iteration and filter operations', () => {
|
|
963
|
+
const map = new HashMap<number, string>([
|
|
964
|
+
[1, 'Alice'],
|
|
965
|
+
[2, 'Bob'],
|
|
966
|
+
[3, 'Charlie'],
|
|
967
|
+
[4, 'Diana'],
|
|
968
|
+
[5, 'Eve']
|
|
969
|
+
]);
|
|
970
|
+
|
|
971
|
+
// Iterate through entries
|
|
972
|
+
const entries: [number, string][] = [];
|
|
973
|
+
for (const [key, value] of map) {
|
|
974
|
+
entries.push([key, value]);
|
|
975
|
+
}
|
|
976
|
+
expect(entries).toHaveLength(5);
|
|
977
|
+
|
|
978
|
+
// Filter operation (for iteration with collection methods)
|
|
979
|
+
const filtered = [...map].filter(([key]) => key > 2);
|
|
980
|
+
expect(filtered.length).toBe(3);
|
|
981
|
+
|
|
982
|
+
// Map operation
|
|
983
|
+
const values = [...map.values()].map(v => v.length);
|
|
984
|
+
expect(values).toContain(3); // 'Bob', 'Eve'
|
|
985
|
+
expect(values).toContain(7); // 'Charlie'
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
it('@example HashMap for user session caching O(1) performance', () => {
|
|
989
|
+
interface UserSession {
|
|
990
|
+
userId: number;
|
|
991
|
+
username: string;
|
|
992
|
+
loginTime: number;
|
|
993
|
+
lastActivity: number;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// HashMap provides O(1) average-case performance for set/get/delete
|
|
997
|
+
// Perfect for session management with fast lookups
|
|
998
|
+
const sessionCache = new HashMap<string, UserSession>();
|
|
999
|
+
|
|
1000
|
+
// Simulate user sessions
|
|
1001
|
+
const sessions: [string, UserSession][] = [
|
|
1002
|
+
['session_001', { userId: 1, username: 'alice', loginTime: 1000, lastActivity: 1050 }],
|
|
1003
|
+
['session_002', { userId: 2, username: 'bob', loginTime: 1100, lastActivity: 1150 }],
|
|
1004
|
+
['session_003', { userId: 3, username: 'charlie', loginTime: 1200, lastActivity: 1250 }]
|
|
1005
|
+
];
|
|
1006
|
+
|
|
1007
|
+
// Store sessions with O(1) insertion
|
|
1008
|
+
for (const [token, session] of sessions) {
|
|
1009
|
+
sessionCache.set(token, session);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
expect(sessionCache.size).toBe(3);
|
|
1013
|
+
|
|
1014
|
+
// Retrieve session with O(1) lookup
|
|
1015
|
+
const userSession = sessionCache.get('session_001');
|
|
1016
|
+
expect(userSession?.username).toBe('alice');
|
|
1017
|
+
expect(userSession?.userId).toBe(1);
|
|
1018
|
+
|
|
1019
|
+
// Update session with O(1) operation
|
|
1020
|
+
if (userSession) {
|
|
1021
|
+
userSession.lastActivity = 2000;
|
|
1022
|
+
sessionCache.set('session_001', userSession);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// Check updated value
|
|
1026
|
+
const updated = sessionCache.get('session_001');
|
|
1027
|
+
expect(updated?.lastActivity).toBe(2000);
|
|
1028
|
+
|
|
1029
|
+
// Cleanup: delete expired sessions
|
|
1030
|
+
sessionCache.delete('session_002');
|
|
1031
|
+
expect(sessionCache.has('session_002')).toBe(false);
|
|
1032
|
+
|
|
1033
|
+
// Verify remaining sessions
|
|
1034
|
+
expect(sessionCache.size).toBe(2);
|
|
1035
|
+
|
|
1036
|
+
// Get all active sessions
|
|
1037
|
+
const activeCount = [...sessionCache.values()].length;
|
|
1038
|
+
expect(activeCount).toBe(2);
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
it('Fast lookup of values by key', () => {
|
|
922
1042
|
const hashMap = new HashMap<number, string>();
|
|
923
1043
|
hashMap.set(1, 'A');
|
|
924
1044
|
hashMap.set(2, 'B');
|
|
@@ -930,7 +1050,7 @@ describe('classic uses', () => {
|
|
|
930
1050
|
expect(hashMap.get(99)).toBeUndefined(); // Key not present
|
|
931
1051
|
});
|
|
932
1052
|
|
|
933
|
-
it('
|
|
1053
|
+
it('Remove duplicates when adding multiple entries', () => {
|
|
934
1054
|
const hashMap = new HashMap<number, string>();
|
|
935
1055
|
hashMap.set(1, 'A');
|
|
936
1056
|
hashMap.set(2, 'B');
|
|
@@ -941,7 +1061,7 @@ describe('classic uses', () => {
|
|
|
941
1061
|
expect(hashMap.get(2)).toBe('B');
|
|
942
1062
|
});
|
|
943
1063
|
|
|
944
|
-
it('
|
|
1064
|
+
it('Count occurrences of keys', () => {
|
|
945
1065
|
const data = [1, 2, 1, 3, 2, 1];
|
|
946
1066
|
|
|
947
1067
|
const countMap = new HashMap<number, number>();
|
|
@@ -976,4 +1096,30 @@ describe('classic uses', () => {
|
|
|
976
1096
|
expect(groupedMap.get('A')).toEqual([1, 3]);
|
|
977
1097
|
expect(groupedMap.get('B')).toEqual([2, 4]);
|
|
978
1098
|
});
|
|
1099
|
+
|
|
1100
|
+
it('HashMap delete and clear operations', () => {
|
|
1101
|
+
const map = new HashMap<string, number>([
|
|
1102
|
+
['a', 10],
|
|
1103
|
+
['b', 20],
|
|
1104
|
+
['c', 30],
|
|
1105
|
+
['d', 40]
|
|
1106
|
+
]);
|
|
1107
|
+
|
|
1108
|
+
// Delete a key
|
|
1109
|
+
map.delete('b');
|
|
1110
|
+
expect(map.has('b')).toBe(false);
|
|
1111
|
+
expect(map.size).toBe(3);
|
|
1112
|
+
|
|
1113
|
+
// Value for deleted key is undefined
|
|
1114
|
+
expect(map.get('b')).toBeUndefined();
|
|
1115
|
+
|
|
1116
|
+
// Other entries remain
|
|
1117
|
+
expect(map.get('a')).toBe(10);
|
|
1118
|
+
expect(map.get('c')).toBe(30);
|
|
1119
|
+
|
|
1120
|
+
// Clear all entries
|
|
1121
|
+
map.clear();
|
|
1122
|
+
expect(map.size).toBe(0);
|
|
1123
|
+
expect(map.has('a')).toBe(false);
|
|
1124
|
+
});
|
|
979
1125
|
});
|