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.

This commit is contained in:
Revone 2023-08-27 22:26:30 +08:00
parent 8ac4e0e7c4
commit 0b330aeadd
6 changed files with 96 additions and 77 deletions

View file

@ -351,8 +351,8 @@ let graph: DirectedGraph<DirectedVertex, DirectedEdge>;
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<DirectedVertex, DirectedEdge>;
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<DirectedVertex, DirectedEdge>;
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<DirectedVertex, DirectedEdge>;
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();

View file

@ -139,12 +139,19 @@ export abstract class AbstractGraph<V extends AbstractVertex<any>, 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<V extends AbstractVertex<any>, 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.

View file

@ -141,13 +141,13 @@ export class DirectedGraph<V extends DirectedVertex<any> = 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;
}

View file

@ -120,7 +120,7 @@ export class UndirectedGraph<V extends UndirectedVertex<any> = 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;

View file

@ -8,9 +8,9 @@ export interface IAbstractGraph<V, E> {
// _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<V, E> {
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;

View file

@ -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();