From de5a001b6a719b74f231a2cfeb5a8e04419b6e5a Mon Sep 17 00:00:00 2001 From: Revone Date: Mon, 28 Aug 2023 11:15:04 +0800 Subject: [PATCH] The Graph's API has been optimized. --- README.md | 526 ++++++------------ package.json | 2 +- src/data-structures/graph/abstract-graph.ts | 2 +- src/data-structures/graph/directed-graph.ts | 17 +- .../data-structures/binary-tree/bst.test.ts | 24 +- .../binary-tree/overall.test.ts | 54 ++ .../data-structures/constants/magnitude.ts | 2 +- .../graph/directed-graph.test.ts | 2 +- .../data-structures/graph/overall.test.ts | 50 ++ .../graph/undirected-graph.test.ts | 20 +- 10 files changed, 296 insertions(+), 403 deletions(-) create mode 100644 tests/unit/data-structures/binary-tree/overall.test.ts create mode 100644 tests/unit/data-structures/graph/overall.test.ts diff --git a/README.md b/README.md index 83cb720..3eedb83 100644 --- a/README.md +++ b/README.md @@ -204,22 +204,6 @@ wide range of data structures DFS, DFSIterative, BFS, morris, Bellman-Ford Algorithm, Dijkstra's Algorithm, Floyd-Warshall Algorithm, Tarjan's Algorithm # How - -[API Docs](https://data-structure-typed-docs.vercel.app) - -[Live Examples](https://data-structure-typed-examples.vercel.app) - - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/dfs-pre-order.webp) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/test-graphs.webp) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/cut-off-trees-for-golf.webp) - -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/parenthesis-check.webp) - -Live Examples - ## install ### yarn @@ -236,367 +220,160 @@ npm install data-structure-typed ### Binary Search Tree (BST) snippet +#### TS ```typescript import {BST, BSTNode} from 'data-structure-typed'; - const tree = new BST(); - expect(tree).toBeInstanceOf(BST); - - const ids = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - tree.addMany(ids); - expect(tree.root).toBeInstanceOf(BSTNode); - if (tree.root) expect(tree.root.id).toBe(11); - expect(tree.count).toBe(16); - expect(tree.has(6)).toBe(true); - - const node6 = tree.get(6); - expect(node6 && tree.getHeight(node6)).toBe(2); - expect(node6 && tree.getDepth(node6)).toBe(3); - - const nodeId10 = tree.get(10, 'id'); - expect(nodeId10?.id).toBe(10); - - const nodeVal9 = tree.get(9, 'val'); - expect(nodeVal9?.id).toBe(9); - - const nodesByCount1 = tree.getNodes(1, 'count'); - expect(nodesByCount1.length).toBe(16); - - const leftMost = tree.getLeftMost(); + const bst = new BST(); + bst.add(11); + bst.add(3); + bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]); + bst.size === 16; // true + bst.has(6); // true + const node6 = bst.get(6); + bst.getHeight(6) === 2; // true + bst.getHeight() === 5; // true + bst.getDepth(6) === 3; // true + const leftMost = bst.getLeftMost(); + leftMost?.id === 1; // true expect(leftMost?.id).toBe(1); + bst.remove(6); + bst.get(6); // null + bst.isAVLBalanced(); // true or false + const bfsIDs = bst.BFS(); + bfsIDs[0] === 11; // true + expect(bfsIDs[0]).toBe(11); + + const objBST = new BST>(); + objBST.add(11, {id: 11, keyA: 11}); + objBST.add(3, {id: 3, keyA: 3}); + + objBST.addMany([{id: 15, keyA: 15}, {id: 1, keyA: 1}, {id: 8, keyA: 8}, + {id: 13, keyA: 13}, {id: 16, keyA: 16}, {id: 2, keyA: 2}, + {id: 6, keyA: 6}, {id: 9, keyA: 9}, {id: 12, keyA: 12}, + {id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7}, + {id: 10, keyA: 10}, {id: 5, keyA: 5}]); + + objBST.remove(11); + + + const avlTree = new AVLTree(); + avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]) + avlTree.isAVLBalanced(); // true + avlTree.remove(10); + avlTree.isAVLBalanced(); // true - const node15 = tree.get(15); - const minNodeBySpecificNode = node15 && tree.getLeftMost(node15); - expect(minNodeBySpecificNode?.id).toBe(12); +``` +#### JS +```javascript + const {BST, BSTNode} = require('data-structure-typed'); - const subTreeSum = node15 && tree.subTreeSum(node15); - expect(subTreeSum).toBe(70); + const bst = new BST(); + bst.add(11); + bst.add(3); + bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]); + bst.size === 16; // true + bst.has(6); // true + const node6 = bst.get(6); + bst.getHeight(6) === 2; // true + bst.getHeight() === 5; // true + bst.getDepth(6) === 3; // true + const leftMost = bst.getLeftMost(); + leftMost?.id === 1; // true + expect(leftMost?.id).toBe(1); + bst.remove(6); + bst.get(6); // null + bst.isAVLBalanced(); // true or false + const bfsIDs = bst.BFS(); + bfsIDs[0] === 11; // true + expect(bfsIDs[0]).toBe(11); + + const objBST = new BST(); + objBST.add(11, {id: 11, keyA: 11}); + objBST.add(3, {id: 3, keyA: 3}); + + objBST.addMany([{id: 15, keyA: 15}, {id: 1, keyA: 1}, {id: 8, keyA: 8}, + {id: 13, keyA: 13}, {id: 16, keyA: 16}, {id: 2, keyA: 2}, + {id: 6, keyA: 6}, {id: 9, keyA: 9}, {id: 12, keyA: 12}, + {id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7}, + {id: 10, keyA: 10}, {id: 5, keyA: 5}]); + + objBST.remove(11); + + + const avlTree = new AVLTree(); + avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]) + avlTree.isAVLBalanced(); // true + avlTree.remove(10); + avlTree.isAVLBalanced(); // true - const lesserSum = tree.lesserSum(10); - expect(lesserSum).toBe(45); - - expect(node15).toBeInstanceOf(BSTNode); - if (node15 instanceof BSTNode) { - const subTreeAdd = tree.subTreeAdd(node15, 1, 'count'); - expect(subTreeAdd).toBeDefined(); - } - - const node11 = tree.get(11); - expect(node11).toBeInstanceOf(BSTNode); - if (node11 instanceof BSTNode) { - const allGreaterNodesAdded = tree.allGreaterNodesAdd(node11, 2, 'count'); - expect(allGreaterNodesAdded).toBeDefined(); - } - - const dfsInorderNodes = tree.DFS('in', 'node'); - expect(dfsInorderNodes[0].id).toBe(1); - expect(dfsInorderNodes[dfsInorderNodes.length - 1].id).toBe(16); - - tree.balance(); - expect(tree.isBalanced()).toBe(true); - - const bfsNodesAfterBalanced = tree.BFS('node'); - expect(bfsNodesAfterBalanced[0].id).toBe(8); - expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].id).toBe(16); - - const removed11 = tree.remove(11, true); - expect(removed11).toBeInstanceOf(Array); - expect(removed11[0]).toBeDefined(); - expect(removed11[0].deleted).toBeDefined(); - - if (removed11[0].deleted) expect(removed11[0].deleted.id).toBe(11); - - expect(tree.isAVLBalanced()).toBe(true); - - expect(node15 && tree.getHeight(node15)).toBe(2); - - const removed1 = tree.remove(1, true); - expect(removed1).toBeInstanceOf(Array); - expect(removed1[0]).toBeDefined(); - expect(removed1[0].deleted).toBeDefined(); - if (removed1[0].deleted) expect(removed1[0].deleted.id).toBe(1); - - expect(tree.isAVLBalanced()).toBe(true); - - expect(tree.getHeight()).toBe(4); - - // The code for removing these nodes (4, 10, 15, 5, 13, 3, 8, 6, 7, 9, 14) in sequence has been omitted. - - expect(tree.isAVLBalanced()).toBe(false); - - const bfsIDs = tree.BFS(); - expect(bfsIDs[0]).toBe(2); - expect(bfsIDs[1]).toBe(12); - expect(bfsIDs[2]).toBe(16); - - const bfsNodes = tree.BFS('node'); - expect(bfsNodes[0].id).toBe(2); - expect(bfsNodes[1].id).toBe(12); - expect(bfsNodes[2].id).toBe(16); ``` ### Directed Graph simple snippet +#### TS or JS ```typescript -import {DirectedGraph, DirectedVertex, DirectedEdge, VertexId} from 'data-structure-typed'; +import {DirectedGraph} from 'data-structure-typed'; -let graph: DirectedGraph; - - beforeEach(() => { - graph = new DirectedGraph(); - }); - - - it('should add vertices', () => { - const vertex1 = new DirectedVertex('A'); - const vertex2 = new DirectedVertex('B'); - - graph._addVertexOnly(vertex1); - graph._addVertexOnly(vertex2); - - expect(graph.hasVertex(vertex1)).toBe(true); - expect(graph.hasVertex(vertex2)).toBe(true); - }); - - it('should add edges', () => { - const vertex1 = new DirectedVertex('A'); - const vertex2 = new DirectedVertex('B'); - const edge = new DirectedEdge('A', 'B'); - - graph._addVertexOnly(vertex1); - graph._addVertexOnly(vertex2); - graph._addEdgeOnly(edge); - - expect(graph.hasEdge('A', 'B')).toBe(true); - expect(graph.hasEdge('B', 'A')).toBe(false); - }); - - it('should remove edges', () => { - const vertex1 = new DirectedVertex('A'); - const vertex2 = new DirectedVertex('B'); - const edge = new DirectedEdge('A', 'B'); - - graph._addVertexOnly(vertex1); - graph._addVertexOnly(vertex2); - graph._addEdgeOnly(edge); - - expect(graph.removeEdge(edge)).toBe(edge); - expect(graph.hasEdge('A', 'B')).toBe(false); - }); - - it('should perform topological sort', () => { - const vertexA = new DirectedVertex('A'); - const vertexB = new DirectedVertex('B'); - const vertexC = new DirectedVertex('C'); - const edgeAB = new DirectedEdge('A', 'B'); - const edgeBC = new DirectedEdge('B', 'C'); - - graph._addVertexOnly(vertexA); - graph._addVertexOnly(vertexB); - graph._addVertexOnly(vertexC); - graph._addEdgeOnly(edgeAB); - graph._addEdgeOnly(edgeBC); - - const topologicalOrder = graph.topologicalSort(); - if (topologicalOrder) expect(topologicalOrder.map(v => v.id)).toEqual(['A', 'B', 'C']); - }); + const graph = new DirectedGraph(); + + graph.addVertex('A'); + graph.addVertex('B'); + + graph.hasVertex('A'); // true + graph.hasVertex('B'); // true + graph.hasVertex('C'); // false + + graph.addEdge('A', 'B'); + graph.hasEdge('A', 'B'); // true + graph.hasEdge('B', 'A'); // false + + graph.removeEdgeSrcToDest('A', 'B'); + graph.hasEdge('A', 'B'); // false + + graph.addVertex('C'); + + graph.addEdge('A', 'B'); + graph.addEdge('B', 'C'); + + const topologicalOrderIds = graph.topologicalSort(); // ['A', 'B', 'C'] ``` -### Directed Graph complex snippet +### Undirected Graph snippet +#### TS or JS ```typescript -import {DirectedGraph, DirectedVertex, DirectedEdge, VertexId} from 'data-structure-typed'; +import {UndirectedGraph} from 'data-structure-typed'; -class MyVertex extends DirectedVertex { - private _data: string; - get data(): string { - return this._data; - } - set data(value: string) { - this._data = value; - } - - constructor(id: VertexId, data: string) { - super(id); - this._data = data; - } -} - -class MyEdge extends DirectedEdge { - private _data: string; - get data(): string { - return this._data; - } - set data(value: string) { - this._data = value; - } - - constructor(v1: VertexId, v2: VertexId, weight: number, data: string) { - super(v1, v2, weight); - this._data = data; - } -} - -describe('DirectedGraph Test3', () => { - const myGraph = new DirectedGraph(); - - it('should test graph operations', () => { - const vertex1 = new MyVertex(1, 'data1'); - const vertex2 = new MyVertex(2, 'data2'); - const vertex3 = new MyVertex(3, 'data3'); - const vertex4 = new MyVertex(4, 'data4'); - const vertex5 = new MyVertex(5, 'data5'); - const vertex6 = new MyVertex(6, 'data6'); - const vertex7 = new MyVertex(7, 'data7'); - const vertex8 = new MyVertex(8, 'data8'); - const vertex9 = new MyVertex(9, 'data9'); - myGraph._addVertexOnly(vertex1); - myGraph._addVertexOnly(vertex2); - myGraph._addVertexOnly(vertex3); - myGraph._addVertexOnly(vertex4); - myGraph._addVertexOnly(vertex5); - myGraph._addVertexOnly(vertex6); - myGraph._addVertexOnly(vertex7); - myGraph._addVertexOnly(vertex8); - myGraph._addVertexOnly(vertex9); - - myGraph._addEdgeOnly(new MyEdge(1, 2, 10, 'edge-data1-2')); - myGraph._addEdgeOnly(new MyEdge(2, 1, 20, 'edge-data2-1')); - - expect(myGraph.getEdge(1, 2)).toBeTruthy(); - expect(myGraph.getEdge(2, 1)).toBeTruthy(); - expect(myGraph.getEdge(1, '100')).toBeFalsy(); - - myGraph.removeEdgeBetween(1, 2); - expect(myGraph.getEdge(1, 2)).toBeFalsy(); - - myGraph._addEdgeOnly(new MyEdge(3, 1, 3, 'edge-data-3-1')); - myGraph._addEdgeOnly(new MyEdge(1, 9, 19, 'edge-data1-9')); - myGraph._addEdgeOnly(new MyEdge(9, 7, 97, 'edge-data9-7')); - myGraph._addEdgeOnly(new MyEdge(7, 9, 79, 'edge-data7-9')); - myGraph._addEdgeOnly(new MyEdge(1, 4, 14, 'edge-data1-4')); - myGraph._addEdgeOnly(new MyEdge(4, 7, 47, 'edge-data4-7')); - myGraph._addEdgeOnly(new MyEdge(1, 2, 12, 'edge-data1-2')); - myGraph._addEdgeOnly(new MyEdge(2, 3, 23, 'edge-data2-3')); - myGraph._addEdgeOnly(new MyEdge(3, 5, 35, 'edge-data3-5')); - myGraph._addEdgeOnly(new MyEdge(5, 7, 57, 'edge-data5-7')); - myGraph._addEdgeOnly(new MyEdge(7, 3, 73, 'edge-data7-3')); - - const topologicalSorted = myGraph.topologicalSort(); - expect(topologicalSorted).toBeNull(); - - const minPath1to7 = myGraph.getMinPathBetween(1, 7); - expect(minPath1to7).toBeInstanceOf(Array); - if (minPath1to7 && minPath1to7.length > 0) { - expect(minPath1to7).toHaveLength(3); - expect(minPath1to7[0]).toBeInstanceOf(MyVertex); - expect(minPath1to7[0].id).toBe(1); - expect(minPath1to7[1].id).toBe(9); - expect(minPath1to7[2].id).toBe(7); - } - - const fordResult1 = myGraph.bellmanFord(1); - expect(fordResult1).toBeTruthy(); - expect(fordResult1.hasNegativeCycle).toBeUndefined(); - const {distMap, preMap, paths, min, minPath} = fordResult1; - expect(distMap).toBeInstanceOf(Map); - expect(distMap.size).toBe(9); - expect(distMap.get(vertex1)).toBe(0); - expect(distMap.get(vertex2)).toBe(12); - expect(distMap.get(vertex3)).toBe(35); - expect(distMap.get(vertex4)).toBe(14); - expect(distMap.get(vertex5)).toBe(70); - expect(distMap.get(vertex6)).toBe(Infinity); - expect(distMap.get(vertex7)).toBe(61); - expect(distMap.get(vertex8)).toBe(Infinity); - expect(distMap.get(vertex9)).toBe(19); - - expect(preMap).toBeInstanceOf(Map); - expect(preMap.size).toBe(0); - - expect(paths).toBeInstanceOf(Array); - expect(paths.length).toBe(0); - expect(min).toBe(Infinity); - expect(minPath).toBeInstanceOf(Array); - - const floydResult = myGraph.floyd(); - expect(floydResult).toBeTruthy(); - if (floydResult) { - const {costs, predecessor} = floydResult; - expect(costs).toBeInstanceOf(Array); - expect(costs.length).toBe(9); - expect(costs[0]).toEqual([32, 12, 35, 14, 70, Infinity, 61, Infinity, 19]); - expect(costs[1]).toEqual([20, 32, 23, 34, 58, Infinity, 81, Infinity, 39]); - expect(costs[2]).toEqual([3, 15, 38, 17, 35, Infinity, 64, Infinity, 22]); - expect(costs[3]).toEqual([123, 135, 120, 137, 155, Infinity, 47, Infinity, 126]); - expect(costs[4]).toEqual([133, 145, 130, 147, 165, Infinity, 57, Infinity, 136]); - expect(costs[5]).toEqual([Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]); - expect(costs[6]).toEqual([76, 88, 73, 90, 108, Infinity, 137, Infinity, 79]); - expect(costs[7]).toEqual([Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]); - expect(costs[8]).toEqual([173, 185, 170, 187, 205, Infinity, 97, Infinity, 176]); - - expect(predecessor).toBeInstanceOf(Array); - expect(predecessor.length).toBe(9); - expect(predecessor[0]).toEqual([vertex2, null, vertex2, null, vertex3, null, vertex4, null, null]); - expect(predecessor[1]).toEqual([null, vertex1, null, vertex1, vertex3, null, vertex4, null, vertex1]); - expect(predecessor[5]).toEqual([null, null, null, null, null, null, null, null, null]); - expect(predecessor[7]).toEqual([null, null, null, null, null, null, null, null, null]); - expect(predecessor[8]).toEqual([vertex7, vertex7, vertex7, vertex7, vertex7, null, null, null, vertex7]); - } - - const dijkstraRes12tt = myGraph.dijkstra(1, 2, true, true); - expect(dijkstraRes12tt).toBeTruthy(); - if (dijkstraRes12tt) { - const {distMap, minDist, minPath, paths, preMap, seen} = dijkstraRes12tt; - expect(distMap).toBeInstanceOf(Map); - expect(distMap.size).toBe(9); - expect(distMap.get(vertex1)).toBe(0); - expect(distMap.get(vertex2)).toBe(12); - expect(distMap.get(vertex3)).toBe(Infinity); - expect(distMap.get(vertex4)).toBe(14); - expect(distMap.get(vertex5)).toBe(Infinity); - expect(distMap.get(vertex6)).toBe(Infinity); - expect(distMap.get(vertex7)).toBe(Infinity); - expect(distMap.get(vertex8)).toBe(Infinity); - expect(distMap.get(vertex9)).toBe(19); - - expect(minDist).toBe(12); - expect(minPath).toBeInstanceOf(Array); - expect(minPath.length).toBe(2); - expect(minPath[0]).toBe(vertex1); - expect(minPath[1]).toBe(vertex2); - - expect(paths).toBeInstanceOf(Array); - expect(paths.length).toBe(9); - expect(paths[0]).toBeInstanceOf(Array); - expect(paths[0][0]).toBe(vertex1); - - expect(paths[1]).toBeInstanceOf(Array); - expect(paths[1][0]).toBe(vertex1); - expect(paths[1][1]).toBe(vertex2); - - expect(paths[2]).toBeInstanceOf(Array); - expect(paths[2][0]).toBe(vertex3); - expect(paths[3]).toBeInstanceOf(Array); - expect(paths[3][0]).toBe(vertex1); - expect(paths[3][1]).toBe(vertex4); - expect(paths[4]).toBeInstanceOf(Array); - expect(paths[4][0]).toBe(vertex5); - - expect(paths[5]).toBeInstanceOf(Array); - expect(paths[5][0]).toBe(vertex6); - expect(paths[6]).toBeInstanceOf(Array); - expect(paths[6][0]).toBe(vertex7); - expect(paths[7]).toBeInstanceOf(Array); - expect(paths[7][0]).toBe(vertex8); - expect(paths[8]).toBeInstanceOf(Array); - expect(paths[8][0]).toBe(vertex1); - expect(paths[8][1]).toBe(vertex9); - } - }); -}); + const graph = new UndirectedGraph(); + graph.addVertex('A'); + graph.addVertex('B'); + graph.addVertex('C'); + graph.addVertex('D'); + graph.removeVertex('C'); + graph.addEdge('A', 'B'); + graph.addEdge('B', 'D'); + + const dijkstraResult = graph.dijkstra('A'); + Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.id) // ['A', 'B', 'D'] ``` +[API Docs](https://data-structure-typed-docs.vercel.app) + +[Live Examples](https://data-structure-typed-examples.vercel.app) + + +![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/dfs-pre-order.webp) + +![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/test-graphs.webp) + +![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/cut-off-trees-for-golf.webp) + +![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/examples/parenthesis-check.webp) + +Live Examples + + ## API docs @@ -875,29 +652,40 @@ describe('DirectedGraph Test3', () => { ![complexities of data structures](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/assets/data-structure-complexities.jpg) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/binary-tree/bst-rotation.gif) +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/binary-tree/bst-rotation.gif)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/binary-tree/avl-tree-inserting.gif) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/binary-tree/avl-tree-inserting.gif)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/tarjan.webp) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/tarjan.webp)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-list.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-list.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-list-pros-cons.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-list-pros-cons.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-matrix.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-matrix.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-matrix-pros-cons.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/adjacency-matrix-pros-cons.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/dfs-can-do.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/dfs-can-do.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/edge-list.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/edge-list.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/edge-list-pros-cons.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/edge-list-pros-cons.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/max-flow.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/max-flow.jpg)) -![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/mst.jpg) +[//]: # () +[//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/mst.jpg)) [//]: # (![](https://github.com/zrwusa/assets/blob/master/images/data-structure-typed/graph/tarjan-articulation-point-bridge.png)) diff --git a/package.json b/package.json index 92ecf8c..e0ad0e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.18.7", + "version": "1.18.8", "description": "Explore our comprehensive Javascript Data Structure / TypeScript Data Structure Library, meticulously crafted to empower developers with a versatile set of essential data structures. Our library includes a wide range of data structures, such as Binary Tree, AVL Tree, Binary Search Tree (BST), Tree Multiset, Segment Tree, Binary Indexed Tree, Graph, Directed Graph, Undirected Graph, Singly Linked List, Hash, CoordinateSet, CoordinateMap, Heap, Doubly Linked List, Priority Queue, Max Priority Queue, Min Priority Queue, Queue, ObjectDeque, ArrayDeque, Stack, and Trie. Each data structure is thoughtfully designed and implemented using TypeScript to provide efficient, reliable, and easy-to-use solutions for your programming needs. Whether you're optimizing algorithms, managing data, or enhancing performance, our TypeScript Data Structure Library is your go-to resource. Elevate your coding experience with these fundamental building blocks for software development.", "main": "dist/index.js", "scripts": { diff --git a/src/data-structures/graph/abstract-graph.ts b/src/data-structures/graph/abstract-graph.ts index 59a0370..fed9e8d 100644 --- a/src/data-structures/graph/abstract-graph.ts +++ b/src/data-structures/graph/abstract-graph.ts @@ -199,7 +199,7 @@ export abstract class AbstractGraph, E extends Abs addEdge(edge: E): boolean - addEdge(src: V | VertexId, dest: V | VertexId, weight: number, val?: E['val']): boolean + addEdge(src: V | VertexId, dest: V | VertexId, weight?: number, val?: E['val']): boolean addEdge(srcOrEdge: V | VertexId | E, dest?: V | VertexId, weight?: number, val?: E['val']): boolean { if (srcOrEdge instanceof AbstractEdge) { diff --git a/src/data-structures/graph/directed-graph.ts b/src/data-structures/graph/directed-graph.ts index e149748..2a7255f 100644 --- a/src/data-structures/graph/directed-graph.ts +++ b/src/data-structures/graph/directed-graph.ts @@ -330,13 +330,17 @@ export class DirectedGraph = DirectedVertex, E ext return destinations; } + /** - * The `topologicalSort` function performs a topological sort on a directed graph and returns the sorted vertices in - * reverse order, or null if the graph contains a cycle. - * @returns The function `topologicalSort()` returns an array of `V` or `VertexId` objects in - * topological order, or `null` if there is a cycle in the graph. + * The `topologicalSort` function performs a topological sort on a graph and returns an array of vertices or vertex IDs + * in the sorted order, or null if the graph contains a cycle. + * @param {'vertex' | 'id'} [propertyName] - The `propertyName` parameter is an optional parameter that specifies the + * property to use for sorting the vertices. It can have two possible values: 'vertex' or 'id'. If 'vertex' is + * specified, the vertices themselves will be used for sorting. If 'id' is specified, the ids of + * @returns an array of vertices or vertex IDs in topological order, or null if there is a cycle in the graph. */ - topologicalSort(): Array | null { + topologicalSort(propertyName?: 'vertex' | 'id'): Array | null { + propertyName = propertyName ?? 'id'; // When judging whether there is a cycle in the undirected graph, all nodes with degree of **<= 1** are enqueued // When judging whether there is a cycle in the directed graph, all nodes with **in degree = 0** are enqueued const statusMap: Map = new Map(); @@ -344,7 +348,7 @@ export class DirectedGraph = DirectedVertex, E ext statusMap.set(entry[1], 0); } - const sorted: (V | VertexId)[] = []; + let sorted: (V | VertexId)[] = []; let hasCycle = false; const dfs = (cur: V | VertexId) => { statusMap.set(cur, 1); @@ -369,6 +373,7 @@ export class DirectedGraph = DirectedVertex, E ext if (hasCycle) return null; + if (propertyName === 'id') sorted = sorted.map(vertex => vertex instanceof DirectedVertex ? vertex.id : vertex); return sorted.reverse(); } diff --git a/tests/unit/data-structures/binary-tree/bst.test.ts b/tests/unit/data-structures/binary-tree/bst.test.ts index 61cf46f..5f0a7f1 100644 --- a/tests/unit/data-structures/binary-tree/bst.test.ts +++ b/tests/unit/data-structures/binary-tree/bst.test.ts @@ -4,13 +4,14 @@ describe('BST operations test', () => { it('should perform various operations on a Binary Search Tree with numeric values', () => { const bst = new BST(); expect(bst).toBeInstanceOf(BST); - - const values = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - bst.addMany(values); + bst.add(11); + bst.add(3); + bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]); expect(bst.root).toBeInstanceOf(BSTNode); if (bst.root) expect(bst.root.id).toBe(11); + expect(bst.size).toBe(16); expect(bst.count).toBe(16); expect(bst.has(6)).toBe(true); @@ -194,16 +195,15 @@ describe('BST operations test', () => { }); it('should perform various operations on a Binary Search Tree with object values', () => { - const objBST = new BST>({autoIncrementId: false}); + const objBST = new BST>(); expect(objBST).toBeInstanceOf(BST); - - const values = [{id: 11, keyA: 11}, {id: 3, keyA: 3}, {id: 15, keyA: 15}, {id: 1, keyA: 1}, { - id: 8, - keyA: 8 - }, {id: 13, keyA: 13}, {id: 16, keyA: 16}, {id: 2, keyA: 2}, {id: 6, keyA: 6}, {id: 9, keyA: 9}, { - id: 12, - keyA: 12 - }, {id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7}, {id: 10, keyA: 10}, {id: 5, keyA: 5}]; + objBST.add(11, {id: 11, keyA: 11}); + objBST.add(3, {id: 3, keyA: 3}); + const values = [{id: 15, keyA: 15}, {id: 1, keyA: 1}, {id: 8, keyA: 8}, + {id: 13, keyA: 13}, {id: 16, keyA: 16}, {id: 2, keyA: 2}, + {id: 6, keyA: 6}, {id: 9, keyA: 9}, {id: 12, keyA: 12}, + {id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7}, + {id: 10, keyA: 10}, {id: 5, keyA: 5}]; objBST.addMany(values); diff --git a/tests/unit/data-structures/binary-tree/overall.test.ts b/tests/unit/data-structures/binary-tree/overall.test.ts new file mode 100644 index 0000000..a27fd06 --- /dev/null +++ b/tests/unit/data-structures/binary-tree/overall.test.ts @@ -0,0 +1,54 @@ +import {AVLTree, BST, BSTNode} from '../../../../src'; + +describe('Overall BinaryTree Test', () => { + it('should perform various operations on BinaryTree', () => { + const bst = new BST(); + bst.add(11); + bst.add(3); + bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]); + bst.size === 16; // true + expect(bst.size).toBe(16); // true + bst.has(6); // true + expect(bst.has(6)).toBe(true); // true + const node6 = bst.get(6); + bst.getHeight(6) === 2; // true + bst.getHeight() === 5; // true + bst.getDepth(6) === 3; // true + expect(bst.getHeight(6)).toBe(2); // true + expect(bst.getHeight()).toBe(5); // true + expect(bst.getDepth(6)).toBe(3); // true + const leftMost = bst.getLeftMost(); + leftMost?.id === 1; // true + expect(leftMost?.id).toBe(1); + bst.remove(6); + bst.get(6); // null + expect(bst.get(6)).toBeNull(); + bst.isAVLBalanced(); // true or false + expect(bst.isAVLBalanced()).toBe(true); + const bfsIDs = bst.BFS(); + bfsIDs[0] === 11; // true + expect(bfsIDs[0]).toBe(11); + + const objBST = new BST>(); + objBST.add(11, {id: 11, keyA: 11}); + objBST.add(3, {id: 3, keyA: 3}); + + objBST.addMany([{id: 15, keyA: 15}, {id: 1, keyA: 1}, {id: 8, keyA: 8}, + {id: 13, keyA: 13}, {id: 16, keyA: 16}, {id: 2, keyA: 2}, + {id: 6, keyA: 6}, {id: 9, keyA: 9}, {id: 12, keyA: 12}, + {id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7}, + {id: 10, keyA: 10}, {id: 5, keyA: 5}]); + + objBST.remove(11); + + + const avlTree = new AVLTree(); + avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]) + avlTree.isAVLBalanced(); // true + expect(avlTree.isAVLBalanced()).toBe(true); // true + avlTree.remove(10); + avlTree.isAVLBalanced(); // true + expect(avlTree.isAVLBalanced()).toBe(true); // true + + }); +}); diff --git a/tests/unit/data-structures/constants/magnitude.ts b/tests/unit/data-structures/constants/magnitude.ts index 9de4c5f..eaed3fe 100644 --- a/tests/unit/data-structures/constants/magnitude.ts +++ b/tests/unit/data-structures/constants/magnitude.ts @@ -1,4 +1,4 @@ -const orderReducedBy = 3; // reduction of magnitude's order compared to the baseline magnitude +const orderReducedBy = 2; // reduction of magnitude's order compared to the baseline magnitude export const magnitude = { CONSTANT: Math.floor(Number.MAX_SAFE_INTEGER / Math.pow(10, orderReducedBy)), diff --git a/tests/unit/data-structures/graph/directed-graph.test.ts b/tests/unit/data-structures/graph/directed-graph.test.ts index b7bf54f..fa1b669 100644 --- a/tests/unit/data-structures/graph/directed-graph.test.ts +++ b/tests/unit/data-structures/graph/directed-graph.test.ts @@ -59,7 +59,7 @@ describe('DirectedGraph Operation Test', () => { graph.addEdge(edgeBC); const topologicalOrder = graph.topologicalSort(); - if (topologicalOrder) expect(topologicalOrder.map(v => v instanceof DirectedVertex ? v.id : '')).toEqual(['A', 'B', 'C']); + if (topologicalOrder) expect(topologicalOrder).toEqual(['A', 'B', 'C']); }); }); diff --git a/tests/unit/data-structures/graph/overall.test.ts b/tests/unit/data-structures/graph/overall.test.ts new file mode 100644 index 0000000..130876a --- /dev/null +++ b/tests/unit/data-structures/graph/overall.test.ts @@ -0,0 +1,50 @@ +import {DirectedGraph, UndirectedGraph} from '../../../../src'; + +describe('Overall Graph Operation Test', () => { + + it('Overall DirectedGraph Operation Test', () => { + const graph = new DirectedGraph(); + + graph.addVertex('A'); + graph.addVertex('B'); + + graph.hasVertex('A'); // true + graph.hasVertex('B'); // true + graph.hasVertex('C'); // false + expect(graph.hasVertex('A')).toBe(true); // true + expect(graph.hasVertex('B')).toBe(true); // true + expect(graph.hasVertex('C')).toBe(false); // false + + graph.addEdge('A', 'B'); + graph.hasEdge('A', 'B'); // true + graph.hasEdge('B', 'A'); // false + expect(graph.hasEdge('A', 'B')).toBe(true); // true + expect(graph.hasEdge('B', 'A')).toBe(false); // false + + graph.removeEdgeSrcToDest('A', 'B'); + graph.hasEdge('A', 'B'); // false + expect(graph.hasEdge('A', 'B')).toBe(false); // false + + graph.addVertex('C'); + + graph.addEdge('A', 'B'); + graph.addEdge('B', 'C'); + + const topologicalOrderIds = graph.topologicalSort(); + expect(topologicalOrderIds).toEqual(['A', 'B', 'C']) + }); + it('Overall UndirectedGraph Operation Test', () => { + const graph = new UndirectedGraph(); + graph.addVertex('A'); + graph.addVertex('B'); + graph.addVertex('C'); + graph.addVertex('D'); + graph.removeVertex('C'); + graph.addEdge('A', 'B'); + graph.addEdge('B', 'D'); + + const dijkstraResult = graph.dijkstra('A'); + Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.id) // ['A', 'B', 'D'] + expect(Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.id)).toEqual(['A', 'B', 'D']); + }); +}); diff --git a/tests/unit/data-structures/graph/undirected-graph.test.ts b/tests/unit/data-structures/graph/undirected-graph.test.ts index 34a75cd..2efeaba 100644 --- a/tests/unit/data-structures/graph/undirected-graph.test.ts +++ b/tests/unit/data-structures/graph/undirected-graph.test.ts @@ -46,19 +46,15 @@ describe('UndirectedGraph Operation Test', () => { }); it('should perform topological sort', () => { - const vertexA = new UndirectedVertex('A'); - const vertexB = new UndirectedVertex('B'); - const vertexC = new UndirectedVertex('C'); - const edgeAB = new UndirectedEdge('A', 'B'); - const edgeBC = new UndirectedEdge('B', 'C'); - - graph.addVertex(vertexA); - graph.addVertex(vertexB); - graph.addVertex(vertexC); - graph.addEdge(edgeAB); - graph.addEdge(edgeBC); + graph.addVertex('A'); + graph.addVertex('B'); + graph.addVertex('C'); + graph.addVertex('D'); + graph.removeVertex('C'); + graph.addEdge('A', 'B'); + graph.addEdge('B', 'D'); const dijkstraResult = graph.dijkstra('A'); - // TODO to be tested + expect(Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.id)).toEqual(['A', 'B', 'D']); }); });