From 0b330aeadd88a30b4b9480d0b520c0906831d237 Mon Sep 17 00:00:00 2001 From: Revone Date: Sun, 27 Aug 2023 22:26:30 +0800 Subject: [PATCH] Enable adding nodes using both Vertex instances and the vertex parameter in the Graph. For adding edges, support both Edge instances and the src, dest parameter approach simultaneously. --- README.md | 70 +++++++++---------- src/data-structures/graph/abstract-graph.ts | 38 +++++++--- src/data-structures/graph/directed-graph.ts | 6 +- src/data-structures/graph/undirected-graph.ts | 2 +- .../interfaces/abstract-graph.ts | 8 +-- .../graph/directed-graph.test.ts | 49 ++++++------- 6 files changed, 96 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index fb26862..83cb720 100644 --- a/README.md +++ b/README.md @@ -351,8 +351,8 @@ let graph: DirectedGraph; const vertex1 = new DirectedVertex('A'); const vertex2 = new DirectedVertex('B'); - graph.addVertex(vertex1); - graph.addVertex(vertex2); + graph._addVertexOnly(vertex1); + graph._addVertexOnly(vertex2); expect(graph.hasVertex(vertex1)).toBe(true); expect(graph.hasVertex(vertex2)).toBe(true); @@ -363,9 +363,9 @@ let graph: DirectedGraph; const vertex2 = new DirectedVertex('B'); const edge = new DirectedEdge('A', 'B'); - graph.addVertex(vertex1); - graph.addVertex(vertex2); - graph.addEdge(edge); + graph._addVertexOnly(vertex1); + graph._addVertexOnly(vertex2); + graph._addEdgeOnly(edge); expect(graph.hasEdge('A', 'B')).toBe(true); expect(graph.hasEdge('B', 'A')).toBe(false); @@ -376,9 +376,9 @@ let graph: DirectedGraph; const vertex2 = new DirectedVertex('B'); const edge = new DirectedEdge('A', 'B'); - graph.addVertex(vertex1); - graph.addVertex(vertex2); - graph.addEdge(edge); + graph._addVertexOnly(vertex1); + graph._addVertexOnly(vertex2); + graph._addEdgeOnly(edge); expect(graph.removeEdge(edge)).toBe(edge); expect(graph.hasEdge('A', 'B')).toBe(false); @@ -391,11 +391,11 @@ let graph: DirectedGraph; const edgeAB = new DirectedEdge('A', 'B'); const edgeBC = new DirectedEdge('B', 'C'); - graph.addVertex(vertexA); - graph.addVertex(vertexB); - graph.addVertex(vertexC); - graph.addEdge(edgeAB); - graph.addEdge(edgeBC); + 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']); @@ -450,18 +450,18 @@ describe('DirectedGraph Test3', () => { const vertex7 = new MyVertex(7, 'data7'); const vertex8 = new MyVertex(8, 'data8'); const vertex9 = new MyVertex(9, 'data9'); - myGraph.addVertex(vertex1); - myGraph.addVertex(vertex2); - myGraph.addVertex(vertex3); - myGraph.addVertex(vertex4); - myGraph.addVertex(vertex5); - myGraph.addVertex(vertex6); - myGraph.addVertex(vertex7); - myGraph.addVertex(vertex8); - myGraph.addVertex(vertex9); + 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.addEdge(new MyEdge(1, 2, 10, 'edge-data1-2')); - myGraph.addEdge(new MyEdge(2, 1, 20, 'edge-data2-1')); + 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(); @@ -470,17 +470,17 @@ describe('DirectedGraph Test3', () => { myGraph.removeEdgeBetween(1, 2); expect(myGraph.getEdge(1, 2)).toBeFalsy(); - myGraph.addEdge(new MyEdge(3, 1, 3, 'edge-data-3-1')); - myGraph.addEdge(new MyEdge(1, 9, 19, 'edge-data1-9')); - myGraph.addEdge(new MyEdge(9, 7, 97, 'edge-data9-7')); - myGraph.addEdge(new MyEdge(7, 9, 79, 'edge-data7-9')); - myGraph.addEdge(new MyEdge(1, 4, 14, 'edge-data1-4')); - myGraph.addEdge(new MyEdge(4, 7, 47, 'edge-data4-7')); - myGraph.addEdge(new MyEdge(1, 2, 12, 'edge-data1-2')); - myGraph.addEdge(new MyEdge(2, 3, 23, 'edge-data2-3')); - myGraph.addEdge(new MyEdge(3, 5, 35, 'edge-data3-5')); - myGraph.addEdge(new MyEdge(5, 7, 57, 'edge-data5-7')); - myGraph.addEdge(new MyEdge(7, 3, 73, 'edge-data7-3')); + 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(); diff --git a/src/data-structures/graph/abstract-graph.ts b/src/data-structures/graph/abstract-graph.ts index 9f842ff..2420232 100644 --- a/src/data-structures/graph/abstract-graph.ts +++ b/src/data-structures/graph/abstract-graph.ts @@ -139,12 +139,19 @@ export abstract class AbstractGraph, E extends Abs abstract getEdge(srcOrId: V | VertexId, destOrId: V | VertexId): E | null; - createAddVertex(id: VertexId, val?: V['val']): boolean { - const newVertex = this.createVertex(id, val); - return this.addVertex(newVertex); + addVertex(vertex: V): boolean + addVertex(id: VertexId , val?: V['val']): boolean + addVertex(idOrVertex: VertexId | V, val?: V['val']): boolean { + if (idOrVertex instanceof AbstractVertex) { + return this._addVertexOnly(idOrVertex); + + } else { + const newVertex = this.createVertex(idOrVertex, val); + return this._addVertexOnly(newVertex); + } } - addVertex(newVertex: V): boolean { + protected _addVertexOnly(newVertex: V): boolean { if (this.hasVertex(newVertex)) { return false; // throw (new Error('Duplicated vertex id is not allowed')); @@ -199,14 +206,25 @@ export abstract class AbstractGraph, E extends Abs return !!edge; } - createAddEdge(src: V | VertexId, dest: V | VertexId, weight: number, val: E['val']): boolean { - if (src instanceof AbstractVertex) src = src.id; - if (dest instanceof AbstractVertex) dest = dest.id; - const newEdge = this.createEdge(src, dest, weight, val); - return this.addEdge(newEdge); + addEdge(edge: E): 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) { + return this._addEdgeOnly(srcOrEdge); + } else { + if (dest instanceof AbstractVertex || typeof dest === 'string' || typeof dest === 'number') { + if (!(this.hasVertex(srcOrEdge) && this.hasVertex(dest))) return false; + if (srcOrEdge instanceof AbstractVertex) srcOrEdge = srcOrEdge.id; + if (dest instanceof AbstractVertex) dest = dest.id; + const newEdge = this.createEdge(srcOrEdge, dest, weight, val); + return this._addEdgeOnly(newEdge); + } else { + throw new Error('dest must be a Vertex or vertex id while srcOrEdge is an Edge') + } + } } - abstract addEdge(edge: E): boolean; + protected abstract _addEdgeOnly(edge: E): boolean; /** * The function sets the weight of an edge between two vertices in a graph. diff --git a/src/data-structures/graph/directed-graph.ts b/src/data-structures/graph/directed-graph.ts index 58e5e7d..2eae676 100644 --- a/src/data-structures/graph/directed-graph.ts +++ b/src/data-structures/graph/directed-graph.ts @@ -141,13 +141,13 @@ export class DirectedGraph = DirectedVertex, E ext } /** - * The `addEdge` function adds a directed edge to a graph if the source and destination vertices exist. + * The `_addEdgeOnly` function adds a directed edge to a graph if the source and destination vertices exist. * @param edge - The parameter `edge` is of type `E`, which represents a directed edge in a graph. It * contains two properties: - * @returns The method `addEdge` returns a boolean value. It returns `true` if the edge was successfully added to the + * @returns The method `_addEdgeOnly` returns a boolean value. It returns `true` if the edge was successfully added to the * graph, and `false` if either the source or destination vertex of the edge is not present in the graph. */ - addEdge(edge: E): boolean { + protected _addEdgeOnly(edge: E): boolean { if (!(this.hasVertex(edge.src) && this.hasVertex(edge.dest))) { return false; } diff --git a/src/data-structures/graph/undirected-graph.ts b/src/data-structures/graph/undirected-graph.ts index 260f213..6257a58 100644 --- a/src/data-structures/graph/undirected-graph.ts +++ b/src/data-structures/graph/undirected-graph.ts @@ -120,7 +120,7 @@ export class UndirectedGraph = UndirectedVertex, * array of two vertices connected by the edge. * @returns a boolean value. */ - addEdge(edge: E): boolean { + protected _addEdgeOnly(edge: E): boolean { for (const end of edge.vertices) { const endVertex = this._getVertex(end); if (endVertex === null) return false; diff --git a/src/data-structures/interfaces/abstract-graph.ts b/src/data-structures/interfaces/abstract-graph.ts index 71eb9d3..fd895b5 100644 --- a/src/data-structures/interfaces/abstract-graph.ts +++ b/src/data-structures/interfaces/abstract-graph.ts @@ -8,9 +8,9 @@ export interface IAbstractGraph { // _getVertexId(vertexOrId: V | VertexId): VertexId; - createAddVertex(id: VertexId, val?: V): boolean; + addVertex(id: VertexId, val?: V): boolean; - addVertex(newVertex: V): boolean; + // _addVertexOnly(newVertex: V): boolean; removeVertex(vertexOrId: V | VertexId): boolean; @@ -26,9 +26,9 @@ export interface IAbstractGraph { edgeSet(): E[]; - createAddEdge(src: V | VertexId, dest: V | VertexId, weight: number, val: E): boolean; + addEdge(src: V | VertexId, dest: V | VertexId, weight: number, val: E): boolean; - addEdge(edge: E): boolean; + // _addEdgeOnly(edge: E): boolean; removeEdge(edge: E): E | null; diff --git a/tests/unit/data-structures/graph/directed-graph.test.ts b/tests/unit/data-structures/graph/directed-graph.test.ts index 7d7bd7a..b7bf54f 100644 --- a/tests/unit/data-structures/graph/directed-graph.test.ts +++ b/tests/unit/data-structures/graph/directed-graph.test.ts @@ -116,11 +116,11 @@ describe('Inherit from DirectedGraph and perform operations', () => { }); it('Add vertices', () => { - myGraph.addVertex(new MyVertex(1, 'data1')); - myGraph.addVertex(new MyVertex(2, 'data2')); - myGraph.addVertex(new MyVertex(3, 'data3')); - myGraph.addVertex(new MyVertex(4, 'data4')); - myGraph.addVertex(new MyVertex(5, 'data5')); + myGraph.addVertex(1, 'data1'); + myGraph.addVertex(2, 'data2'); + myGraph.addVertex(3, 'data3'); + myGraph.addVertex(4, 'data4'); + myGraph.addVertex(5, 'data5'); myGraph.addVertex(new MyVertex(6, 'data6')); myGraph.addVertex(new MyVertex(7, 'data7')); myGraph.addVertex(new MyVertex(8, 'data8')); @@ -129,9 +129,9 @@ describe('Inherit from DirectedGraph and perform operations', () => { }); it('Add edges', () => { - myGraph.addVertex(new MyVertex(1, 'data1')); - myGraph.addVertex(new MyVertex(2, 'data2')); - myGraph.addEdge(new MyEdge(1, 2, 10, 'edge-data1-2')); + myGraph.addVertex(1, 'data1'); + myGraph.addVertex(2, 'data2'); + myGraph.addEdge(1, 2, 10, 'edge-data1-2'); myGraph.addEdge(new MyEdge(2, 1, 20, 'edge-data2-1')); expect(myGraph.edgeSet().length).toBe(2); @@ -141,9 +141,9 @@ describe('Inherit from DirectedGraph and perform operations', () => { }); it('Get edge', () => { - myGraph.createAddVertex(1, 'val1'); - myGraph.createAddVertex(2, 'val1'); - myGraph.createAddEdge(1, 2, 1, 'val1'); + myGraph.addVertex(1, 'val1'); + myGraph.addVertex(2, 'val1'); + myGraph.addEdge(1, 2, 1, 'val1'); const edge1 = myGraph.getEdge(1, 2); const edge2 = myGraph.getEdge(myGraph.getVertex(1), myGraph.getVertex(2)); const edge3 = myGraph.getEdge(1, '100'); @@ -167,9 +167,9 @@ describe('Inherit from DirectedGraph and perform operations', () => { }); it('Remove edge between vertices', () => { - myGraph.addVertex(new MyVertex(1, 'data1')); - myGraph.addVertex(new MyVertex(2, 'data2')); - myGraph.addEdge(new MyEdge(1, 2, 10, 'edge-data1-2')); + myGraph.addVertex(1, 'data1'); + myGraph.addVertex(2, 'data2'); + myGraph.addEdge(1, 2, 10, 'edge-data1-2'); const removedEdge = myGraph.removeEdgeSrcToDest(1, 2); const edgeAfterRemoval = myGraph.getEdge(1, 2); @@ -249,24 +249,25 @@ describe('Inherit from DirectedGraph and perform operations test2.', () => { myGraph.removeEdgeSrcToDest(1, 2); expect(myGraph.getEdge(1, 2)).toBeFalsy(); - myGraph.addEdge(new MyEdge(3, 1, 3, 'edge-data-3-1')); + myGraph.addEdge(3, 1, 3, 'edge-data-3-1'); - myGraph.addEdge(new MyEdge(1, 9, 19, 'edge-data1-9')); - myGraph.addEdge(new MyEdge(9, 7, 97, 'edge-data9-7')); + myGraph.addEdge(1, 9, 19, 'edge-data1-9'); - myGraph.addEdge(new MyEdge(7, 9, 79, 'edge-data7-9')); + myGraph.addEdge(9, 7, 97, 'edge-data9-7'); - myGraph.addEdge(new MyEdge(1, 4, 14, 'edge-data1-4')); + myGraph.addEdge(7, 9, 79, 'edge-data7-9'); - myGraph.addEdge(new MyEdge(4, 7, 47, 'edge-data4-7')); + myGraph.addEdge(1, 4, 14, 'edge-data1-4'); - myGraph.addEdge(new MyEdge(1, 2, 12, 'edge-data1-2')); + myGraph.addEdge(4, 7, 47, 'edge-data4-7'); - myGraph.addEdge(new MyEdge(2, 3, 23, 'edge-data2-3')); + myGraph.addEdge(1, 2, 12, 'edge-data1-2'); - myGraph.addEdge(new MyEdge(3, 5, 35, 'edge-data3-5')); + myGraph.addEdge(2, 3, 23, 'edge-data2-3'); - myGraph.addEdge(new MyEdge(5, 7, 57, 'edge-data5-7')); + myGraph.addEdge(3, 5, 35, 'edge-data3-5'); + + myGraph.addEdge(5, 7, 57, 'edge-data5-7'); myGraph.addEdge(new MyEdge(7, 3, 73, 'edge-data7-3')); const topologicalSorted = myGraph.topologicalSort();