mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
edge1.data has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
This commit is contained in:
parent
1b441f1d47
commit
d3f5dd4a9e
|
@ -2,5 +2,6 @@
|
|||
<project version="4">
|
||||
<component name="TypeScriptCompiler">
|
||||
<option name="nodeInterpreterTextField" value="$USER_HOME$/.nvm/versions/node/v19.9.0/bin/node" />
|
||||
<option name="versionType" value="EMBEDDED" />
|
||||
</component>
|
||||
</project>
|
|
@ -10,12 +10,14 @@ import {PriorityQueue} from '../priority-queue';
|
|||
import type {DijkstraResult, VertexId} from '../types';
|
||||
import {IGraph} from '../interfaces';
|
||||
|
||||
export class AbstractVertex {
|
||||
constructor(id: VertexId) {
|
||||
export abstract class AbstractVertex<V = number> {
|
||||
|
||||
protected constructor(id: VertexId, val?: V) {
|
||||
this._id = id;
|
||||
this._val = val;
|
||||
}
|
||||
|
||||
protected _id: VertexId;
|
||||
private _id: VertexId;
|
||||
|
||||
get id(): VertexId {
|
||||
return this._id;
|
||||
|
@ -24,22 +26,45 @@ export class AbstractVertex {
|
|||
set id(v: VertexId) {
|
||||
this._id = v;
|
||||
}
|
||||
|
||||
private _val: V | undefined;
|
||||
|
||||
get val(): V | undefined {
|
||||
return this._val;
|
||||
}
|
||||
|
||||
set val(value: V | undefined) {
|
||||
this._val = value;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
// * This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
// * @param id
|
||||
// * @param val
|
||||
// */
|
||||
// abstract _createVertex(id: VertexId, val?: V): AbstractVertex<V>;
|
||||
}
|
||||
|
||||
export abstract class AbstractEdge {
|
||||
export abstract class AbstractEdge<E> {
|
||||
|
||||
|
||||
/**
|
||||
* The function is a protected constructor that initializes the weight and generates a unique hash code for an edge.
|
||||
* @param {number} [weight] - The `weight` parameter is an optional number that represents the weight of the edge. If
|
||||
* no weight is provided, it will default to the value of `AbstractEdge.DEFAULT_EDGE_WEIGHT`.
|
||||
*/
|
||||
protected constructor(weight?: number) {
|
||||
protected constructor(weight?: number, val?: E) {
|
||||
this._weight = weight !== undefined ? weight : 1;
|
||||
this._val = val;
|
||||
this._hashCode = uuidV4();
|
||||
}
|
||||
|
||||
protected _weight: number;
|
||||
private _val: E | undefined;
|
||||
|
||||
get val(): E | undefined {
|
||||
return this._val;
|
||||
}
|
||||
|
||||
set val(value: E | undefined) {
|
||||
this._val = value;
|
||||
}
|
||||
|
||||
private _weight: number;
|
||||
|
||||
get weight(): number {
|
||||
return this._weight;
|
||||
|
@ -55,40 +80,61 @@ export abstract class AbstractEdge {
|
|||
return this._hashCode;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
// * This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
// * @param srcOrV1
|
||||
// * @param destOrV2
|
||||
// * @param weight
|
||||
// * @param val
|
||||
// */
|
||||
// abstract _createEdge(srcOrV1: VertexId | string, destOrV2: VertexId | string, weight?: number, val?: E): AbstractEdge<E>;
|
||||
|
||||
protected _setHashCode(v: string) {
|
||||
this._hashCode = v;
|
||||
}
|
||||
}
|
||||
|
||||
// Connected Component === Largest Connected Sub-Graph
|
||||
export abstract class AbstractGraph<V extends AbstractVertex, E extends AbstractEdge> implements IGraph<V, E> {
|
||||
export abstract class AbstractGraph<V = number, E = number> implements IGraph<V, E> {
|
||||
private _vertices: Map<VertexId, AbstractVertex<V>> = new Map<VertexId, AbstractVertex<V>>();
|
||||
|
||||
protected _vertices: Map<VertexId, V> = new Map<VertexId, V>();
|
||||
|
||||
abstract removeEdgeBetween(srcOrId: V | VertexId, destOrId: V | VertexId): E | null;
|
||||
|
||||
abstract removeEdge(edge: E): E | null;
|
||||
|
||||
/**
|
||||
* The function `getVertex` returns the vertex object associated with a given vertex ID or vertex object, or null if it
|
||||
* does not exist.
|
||||
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a `V`.
|
||||
* @returns The function `getVertex` returns the vertex object (`V`) corresponding to the given `vertexOrId` parameter.
|
||||
* If the vertex is found in the `_vertices` map, it is returned. Otherwise, `null` is returned.
|
||||
*/
|
||||
getVertex(vertexOrId: VertexId | V): V | null {
|
||||
const vertexId = this.getVertexId(vertexOrId);
|
||||
return this._vertices.get(vertexId) || null;
|
||||
get vertices(): Map<VertexId, AbstractVertex<V>> {
|
||||
return this._vertices;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `getVertexId` returns the id of a vertex, whether it is passed as an instance of `AbstractVertex` or as
|
||||
* a `VertexId`.
|
||||
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`).
|
||||
* @returns the id of the vertex.
|
||||
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
* @param id
|
||||
* @param val
|
||||
*/
|
||||
getVertexId(vertexOrId: V | VertexId): VertexId {
|
||||
abstract _createVertex(id: VertexId, val?: V): AbstractVertex<V>;
|
||||
|
||||
/**
|
||||
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
* @param srcOrV1
|
||||
* @param destOrV2
|
||||
* @param weight
|
||||
* @param val
|
||||
*/
|
||||
abstract _createEdge(srcOrV1: VertexId | string, destOrV2: VertexId | string, weight?: number, val?: E): AbstractEdge<E>;
|
||||
|
||||
abstract removeEdgeBetween(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
|
||||
|
||||
abstract removeEdge(edge: AbstractEdge<E>): AbstractEdge<E> | null;
|
||||
|
||||
_getVertex(vertexOrId: VertexId | AbstractVertex<V>): AbstractVertex<V> | null {
|
||||
const vertexId = this._getVertexId(vertexOrId);
|
||||
return this._vertices.get(vertexId) || null;
|
||||
}
|
||||
|
||||
getVertex(vertexId: VertexId): AbstractVertex<V> | null {
|
||||
return this._vertices.get(vertexId) || null;
|
||||
}
|
||||
|
||||
_getVertexId(vertexOrId: AbstractVertex<V> | VertexId): VertexId {
|
||||
return vertexOrId instanceof AbstractVertex ? vertexOrId.id : vertexOrId;
|
||||
}
|
||||
|
||||
|
@ -98,29 +144,20 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* (`VertexId`).
|
||||
* @returns The method `hasVertex` returns a boolean value.
|
||||
*/
|
||||
hasVertex(vertexOrId: V | VertexId): boolean {
|
||||
return this._vertices.has(this.getVertexId(vertexOrId));
|
||||
hasVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean {
|
||||
return this._vertices.has(this._getVertexId(vertexOrId));
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `vertexSet()` returns a map of vertices.
|
||||
* @returns The method `vertexSet()` returns a map of vertex IDs to vertex objects.
|
||||
*/
|
||||
vertexSet(): Map<VertexId, V> {
|
||||
return this._vertices;
|
||||
abstract getEdge(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
|
||||
|
||||
createAddVertex(id: VertexId, val?: V): boolean {
|
||||
const newVertex = this._createVertex(id, val);
|
||||
return this.addVertex(newVertex);
|
||||
}
|
||||
|
||||
abstract getEdge(srcOrId: V | null | VertexId, destOrId: V | null | VertexId): E | null;
|
||||
|
||||
/**
|
||||
* The addVertex function adds a new vertex to a graph if it does not already exist.
|
||||
* @param {V} newVertex - The parameter "newVertex" is of type V, which represents a vertex in a graph.
|
||||
* @returns The method is returning a boolean value. If the newVertex is already contained in the graph, it will return
|
||||
* false. Otherwise, it will add the newVertex to the graph and return true.
|
||||
*/
|
||||
addVertex(newVertex: V): boolean {
|
||||
addVertex(newVertex: AbstractVertex<V>): boolean {
|
||||
if (this.hasVertex(newVertex)) {
|
||||
return false;
|
||||
throw (new Error('Duplicated vertex id is not allowed'));
|
||||
}
|
||||
this._vertices.set(newVertex.id, newVertex);
|
||||
return true;
|
||||
|
@ -132,8 +169,8 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* (`VertexId`).
|
||||
* @returns The method `removeVertex` returns a boolean value.
|
||||
*/
|
||||
removeVertex(vertexOrId: V | VertexId): boolean {
|
||||
const vertexId = this.getVertexId(vertexOrId);
|
||||
removeVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean {
|
||||
const vertexId = this._getVertexId(vertexOrId);
|
||||
return this._vertices.delete(vertexId);
|
||||
}
|
||||
|
||||
|
@ -144,7 +181,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* @returns a boolean value. It returns true if at least one vertex was successfully removed, and false if no vertices
|
||||
* were removed.
|
||||
*/
|
||||
removeAllVertices(vertices: V[] | VertexId[]): boolean {
|
||||
removeAllVertices(vertices: AbstractVertex<V>[] | VertexId[]): boolean {
|
||||
const removed: boolean[] = [];
|
||||
for (const v of vertices) {
|
||||
removed.push(this.removeVertex(v));
|
||||
|
@ -152,11 +189,11 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
return removed.length > 0;
|
||||
}
|
||||
|
||||
abstract degreeOf(vertexOrId: V | VertexId): number;
|
||||
abstract degreeOf(vertexOrId: AbstractVertex<V> | VertexId): number;
|
||||
|
||||
abstract edgeSet(): E[];
|
||||
abstract edgeSet(): AbstractEdge<E>[];
|
||||
|
||||
abstract edgesOf(vertexOrId: V | VertexId): E[];
|
||||
abstract edgesOf(vertexOrId: AbstractVertex<V> | VertexId): AbstractEdge<E>[];
|
||||
|
||||
/**
|
||||
* The function checks if there is an edge between two vertices in a graph.
|
||||
|
@ -167,12 +204,19 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* @returns The function `hasEdge` returns a boolean value. It returns `true` if there is an edge between the
|
||||
* vertices `v1` and `v2`, and `false` otherwise.
|
||||
*/
|
||||
hasEdge(v1: VertexId | V, v2: VertexId | V): boolean {
|
||||
hasEdge(v1: VertexId | AbstractVertex<V>, v2: VertexId | AbstractVertex<V>): boolean {
|
||||
const edge = this.getEdge(v1, v2);
|
||||
return !!edge;
|
||||
}
|
||||
|
||||
abstract addEdge(edge: E): boolean;
|
||||
createAddEdge(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId, weight: number, val: E): 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);
|
||||
}
|
||||
|
||||
abstract addEdge(edge: AbstractEdge<E>): boolean;
|
||||
|
||||
/**
|
||||
* The function sets the weight of an edge between two vertices in a graph.
|
||||
|
@ -185,7 +229,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* @returns a boolean value. If the edge exists between the source and destination vertices, the function will update
|
||||
* the weight of the edge and return true. If the edge does not exist, the function will return false.
|
||||
*/
|
||||
setEdgeWeight(srcOrId: VertexId | V, destOrId: VertexId | V, weight: number): boolean {
|
||||
setEdgeWeight(srcOrId: VertexId | AbstractVertex<V>, destOrId: VertexId | AbstractVertex<V>, weight: number): boolean {
|
||||
const edge = this.getEdge(srcOrId, destOrId);
|
||||
if (edge) {
|
||||
edge.weight = weight;
|
||||
|
@ -195,7 +239,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
}
|
||||
}
|
||||
|
||||
abstract getNeighbors(vertexOrId: V | VertexId): V[];
|
||||
abstract getNeighbors(vertexOrId: AbstractVertex<V> | VertexId): AbstractVertex<V>[];
|
||||
|
||||
/**
|
||||
* The function `getAllPathsBetween` finds all paths between two vertices in a graph using depth-first search.
|
||||
|
@ -206,15 +250,15 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* @returns an array of arrays of vertices (V[][]). Each inner array represents a path between the given vertices (v1
|
||||
* and v2).
|
||||
*/
|
||||
getAllPathsBetween(v1: V | VertexId, v2: V | VertexId): V[][] {
|
||||
const paths: V[][] = [];
|
||||
const vertex1 = this.getVertex(v1);
|
||||
const vertex2 = this.getVertex(v2);
|
||||
getAllPathsBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId): AbstractVertex<V>[][] {
|
||||
const paths: AbstractVertex<V>[][] = [];
|
||||
const vertex1 = this._getVertex(v1);
|
||||
const vertex2 = this._getVertex(v2);
|
||||
if (!(vertex1 && vertex2)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dfs = (cur: V, dest: V, visiting: Map<V, boolean>, path: V[]) => {
|
||||
const dfs = (cur: AbstractVertex<V>, dest: AbstractVertex<V>, visiting: Map<AbstractVertex<V>, boolean>, path: AbstractVertex<V>[]) => {
|
||||
visiting.set(cur, true);
|
||||
|
||||
if (cur === dest) {
|
||||
|
@ -226,14 +270,14 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
if (!visiting.get(neighbor)) {
|
||||
path.push(neighbor);
|
||||
dfs(neighbor, dest, visiting, path);
|
||||
arrayRemove(path, (vertex: AbstractVertex) => vertex === neighbor);
|
||||
arrayRemove(path, (vertex: AbstractVertex<V>) => vertex === neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
visiting.set(cur, false);
|
||||
};
|
||||
|
||||
dfs(vertex1, vertex2, new Map<V, boolean>(), []);
|
||||
dfs(vertex1, vertex2, new Map<AbstractVertex<V>, boolean>(), []);
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
@ -242,7 +286,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* @param {V[]} path - An array of vertices (V) representing a path in a graph.
|
||||
* @returns The function `getPathSumWeight` returns the sum of the weights of the edges in the given path.
|
||||
*/
|
||||
getPathSumWeight(path: V[]): number {
|
||||
getPathSumWeight(path: AbstractVertex<V>[]): number {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
sum += this.getEdge(path[i], path[i + 1])?.weight || 0;
|
||||
|
@ -264,7 +308,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* If `isWeight` is `false` or not provided, it calculates the minimum number of edges between the vertices. If the
|
||||
* vertices are not
|
||||
*/
|
||||
getMinCostBetween(v1: V | VertexId, v2: V | VertexId, isWeight?: boolean): number | null {
|
||||
getMinCostBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId, isWeight?: boolean): number | null {
|
||||
if (isWeight === undefined) isWeight = false;
|
||||
|
||||
if (isWeight) {
|
||||
|
@ -276,14 +320,14 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
return min;
|
||||
} else {
|
||||
// BFS
|
||||
const vertex2 = this.getVertex(v2);
|
||||
const vertex1 = this.getVertex(v1);
|
||||
const vertex2 = this._getVertex(v2);
|
||||
const vertex1 = this._getVertex(v1);
|
||||
if (!(vertex1 && vertex2)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const visited: Map<V, boolean> = new Map();
|
||||
const queue: V[] = [vertex1];
|
||||
const visited: Map<AbstractVertex<V>, boolean> = new Map();
|
||||
const queue: AbstractVertex<V>[] = [vertex1];
|
||||
visited.set(vertex1, true);
|
||||
let cost = 0;
|
||||
while (queue.length > 0) {
|
||||
|
@ -321,7 +365,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* @returns The function `getMinPathBetween` returns an array of vertices (`V[]`) representing the minimum path between
|
||||
* two vertices (`v1` and `v2`). If no path is found, it returns `null`.
|
||||
*/
|
||||
getMinPathBetween(v1: V | VertexId, v2: V | VertexId, isWeight?: boolean): V[] | null {
|
||||
getMinPathBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId, isWeight?: boolean): AbstractVertex<V>[] | null {
|
||||
if (isWeight === undefined) isWeight = false;
|
||||
|
||||
if (isWeight) {
|
||||
|
@ -340,14 +384,14 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
return allPaths[minIndex] || null;
|
||||
} else {
|
||||
// BFS
|
||||
let minPath: V[] = [];
|
||||
const vertex1 = this.getVertex(v1);
|
||||
const vertex2 = this.getVertex(v2);
|
||||
let minPath: AbstractVertex<V>[] = [];
|
||||
const vertex1 = this._getVertex(v1);
|
||||
const vertex2 = this._getVertex(v2);
|
||||
if (!(vertex1 && vertex2)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dfs = (cur: V, dest: V, visiting: Map<V, boolean>, path: V[]) => {
|
||||
const dfs = (cur: AbstractVertex<V>, dest: AbstractVertex<V>, visiting: Map<AbstractVertex<V>, boolean>, path: AbstractVertex<V>[]) => {
|
||||
visiting.set(cur, true);
|
||||
|
||||
if (cur === dest) {
|
||||
|
@ -360,14 +404,14 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
if (!visiting.get(neighbor)) {
|
||||
path.push(neighbor);
|
||||
dfs(neighbor, dest, visiting, path);
|
||||
arrayRemove(path, (vertex: AbstractVertex) => vertex === neighbor);
|
||||
arrayRemove(path, (vertex: AbstractVertex<V>) => vertex === neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
visiting.set(cur, false);
|
||||
};
|
||||
|
||||
dfs(vertex1, vertex2, new Map<V, boolean>(), []);
|
||||
dfs(vertex1, vertex2, new Map<AbstractVertex<V>, boolean>(), []);
|
||||
return minPath;
|
||||
}
|
||||
}
|
||||
|
@ -389,23 +433,23 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* shortest paths from the source vertex to all other vertices in the graph. If `genPaths
|
||||
* @returns The function `dijkstraWithoutHeap` returns an object of type `DijkstraResult<V>`.
|
||||
*/
|
||||
dijkstraWithoutHeap(src: V | VertexId, dest?: V | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<V> {
|
||||
dijkstraWithoutHeap(src: AbstractVertex<V> | VertexId, dest?: AbstractVertex<V> | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<AbstractVertex<V>> {
|
||||
if (getMinDist === undefined) getMinDist = false;
|
||||
if (genPaths === undefined) genPaths = false;
|
||||
|
||||
if (dest === undefined) dest = null;
|
||||
let minDist = Infinity;
|
||||
let minDest: V | null = null;
|
||||
let minPath: V[] = [];
|
||||
const paths: V[][] = [];
|
||||
let minDest: AbstractVertex<V> | null = null;
|
||||
let minPath: AbstractVertex<V>[] = [];
|
||||
const paths: AbstractVertex<V>[][] = [];
|
||||
|
||||
const vertices = this._vertices;
|
||||
const distMap: Map<V, number> = new Map();
|
||||
const seen: Set<V> = new Set();
|
||||
const preMap: Map<V, V | null> = new Map(); // predecessor
|
||||
const srcVertex = this.getVertex(src);
|
||||
const distMap: Map<AbstractVertex<V>, number> = new Map();
|
||||
const seen: Set<AbstractVertex<V>> = new Set();
|
||||
const preMap: Map<AbstractVertex<V>, AbstractVertex<V> | null> = new Map(); // predecessor
|
||||
const srcVertex = this._getVertex(src);
|
||||
|
||||
const destVertex = dest ? this.getVertex(dest) : null;
|
||||
const destVertex = dest ? this._getVertex(dest) : null;
|
||||
|
||||
if (!srcVertex) {
|
||||
return null;
|
||||
|
@ -420,7 +464,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
|
||||
const getMinOfNoSeen = () => {
|
||||
let min = Infinity;
|
||||
let minV: V | null = null;
|
||||
let minV: AbstractVertex<V> | null = null;
|
||||
for (const [key, val] of distMap) {
|
||||
if (!seen.has(key)) {
|
||||
if (val < min) {
|
||||
|
@ -432,12 +476,12 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
return minV;
|
||||
};
|
||||
|
||||
const getPaths = (minV: V | null) => {
|
||||
const getPaths = (minV: AbstractVertex<V> | null) => {
|
||||
for (const vertex of vertices) {
|
||||
const vertexOrId = vertex[1];
|
||||
|
||||
if (vertexOrId instanceof AbstractVertex) {
|
||||
const path: V[] = [vertexOrId];
|
||||
const path: AbstractVertex<V>[] = [vertexOrId];
|
||||
let parent = preMap.get(vertexOrId);
|
||||
while (parent) {
|
||||
path.push(parent);
|
||||
|
@ -498,17 +542,6 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
return {distMap, preMap, seen, paths, minDist, minPath};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dijkstra's algorithm only solves the single-source shortest path problem, while the Bellman-Ford algorithm and Floyd-Warshall algorithm can address shortest paths between all pairs of nodes.
|
||||
* Dijkstra's algorithm is suitable for graphs with non-negative edge weights, whereas the Bellman-Ford algorithm and Floyd-Warshall algorithm can handle negative-weight edges.
|
||||
* The time complexity of Dijkstra's algorithm and the Bellman-Ford algorithm depends on the size of the graph, while the time complexity of the Floyd-Warshall algorithm is O(V^3), where V is the number of nodes. For dense graphs, Floyd-Warshall might become slower.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dijkstra algorithm time: O(logVE) space: O(V + E)
|
||||
* Dijkstra's algorithm is used to find the shortest paths from a source node to all other nodes in a graph. Its basic idea is to repeatedly choose the node closest to the source node and update the distances of other nodes using this node as an intermediary. Dijkstra's algorithm requires that the edge weights in the graph are non-negative.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dijkstra algorithm time: O(logVE) space: O(V + E)
|
||||
* Dijkstra's algorithm is used to find the shortest paths from a source node to all other nodes in a graph. Its basic idea is to repeatedly choose the node closest to the source node and update the distances of other nodes using this node as an intermediary. Dijkstra's algorithm requires that the edge weights in the graph are non-negative.
|
||||
|
@ -527,22 +560,22 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* shortest paths from the source vertex to all other vertices in the graph. If `genPaths
|
||||
* @returns The function `dijkstra` returns an object of type `DijkstraResult<V>`.
|
||||
*/
|
||||
dijkstra(src: V | VertexId, dest?: V | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<V> {
|
||||
dijkstra(src: AbstractVertex<V> | VertexId, dest?: AbstractVertex<V> | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<AbstractVertex<V>> {
|
||||
if (getMinDist === undefined) getMinDist = false;
|
||||
if (genPaths === undefined) genPaths = false;
|
||||
|
||||
if (dest === undefined) dest = null;
|
||||
let minDist = Infinity;
|
||||
let minDest: V | null = null;
|
||||
let minPath: V[] = [];
|
||||
const paths: V[][] = [];
|
||||
let minDest: AbstractVertex<V> | null = null;
|
||||
let minPath: AbstractVertex<V>[] = [];
|
||||
const paths: AbstractVertex<V>[][] = [];
|
||||
const vertices = this._vertices;
|
||||
const distMap: Map<V, number> = new Map();
|
||||
const seen: Set<V> = new Set();
|
||||
const preMap: Map<V, V | null> = new Map(); // predecessor
|
||||
const distMap: Map<AbstractVertex<V>, number> = new Map();
|
||||
const seen: Set<AbstractVertex<V>> = new Set();
|
||||
const preMap: Map<AbstractVertex<V>, AbstractVertex<V> | null> = new Map(); // predecessor
|
||||
|
||||
const srcVertex = this.getVertex(src);
|
||||
const destVertex = dest ? this.getVertex(dest) : null;
|
||||
const srcVertex = this._getVertex(src);
|
||||
const destVertex = dest ? this._getVertex(dest) : null;
|
||||
|
||||
if (!srcVertex) {
|
||||
return null;
|
||||
|
@ -553,17 +586,17 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
if (vertexOrId instanceof AbstractVertex) distMap.set(vertexOrId, Infinity);
|
||||
}
|
||||
|
||||
const heap = new PriorityQueue<{ id: number, val: V }>({comparator: (a, b) => a.id - b.id});
|
||||
const heap = new PriorityQueue<{ id: number, val: AbstractVertex<V> }>({comparator: (a, b) => a.id - b.id});
|
||||
heap.add({id: 0, val: srcVertex});
|
||||
|
||||
distMap.set(srcVertex, 0);
|
||||
preMap.set(srcVertex, null);
|
||||
|
||||
const getPaths = (minV: V | null) => {
|
||||
const getPaths = (minV: AbstractVertex<V> | null) => {
|
||||
for (const vertex of vertices) {
|
||||
const vertexOrId = vertex[1];
|
||||
if (vertexOrId instanceof AbstractVertex) {
|
||||
const path: V[] = [vertexOrId];
|
||||
const path: AbstractVertex<V>[] = [vertexOrId];
|
||||
let parent = preMap.get(vertexOrId);
|
||||
while (parent) {
|
||||
path.push(parent);
|
||||
|
@ -634,15 +667,19 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
return {distMap, preMap, seen, paths, minDist, minPath};
|
||||
}
|
||||
|
||||
abstract getEndsOfEdge(edge: E): [V, V] | null;
|
||||
/**
|
||||
* Dijkstra's algorithm only solves the single-source shortest path problem, while the Bellman-Ford algorithm and Floyd-Warshall algorithm can address shortest paths between all pairs of nodes.
|
||||
* Dijkstra's algorithm is suitable for graphs with non-negative edge weights, whereas the Bellman-Ford algorithm and Floyd-Warshall algorithm can handle negative-weight edges.
|
||||
* The time complexity of Dijkstra's algorithm and the Bellman-Ford algorithm depends on the size of the graph, while the time complexity of the Floyd-Warshall algorithm is O(V^3), where V is the number of nodes. For dense graphs, Floyd-Warshall might become slower.
|
||||
*/
|
||||
|
||||
/**
|
||||
* BellmanFord time:O(VE) space:O(V)
|
||||
* one to rest pairs
|
||||
* The Bellman-Ford algorithm is also used to find the shortest paths from a source node to all other nodes in a graph. Unlike Dijkstra's algorithm, it can handle edge weights that are negative. Its basic idea involves iterative relaxation of all edges for several rounds to gradually approximate the shortest paths. Due to its ability to handle negative-weight edges, the Bellman-Ford algorithm is more flexible in some scenarios.
|
||||
* The `bellmanFord` function implements the Bellman-Ford algorithm to find the shortest path from a source vertex to
|
||||
* Dijkstra algorithm time: O(logVE) space: O(V + E)
|
||||
* Dijkstra's algorithm is used to find the shortest paths from a source node to all other nodes in a graph. Its basic idea is to repeatedly choose the node closest to the source node and update the distances of other nodes using this node as an intermediary. Dijkstra's algorithm requires that the edge weights in the graph are non-negative.
|
||||
*/
|
||||
|
||||
abstract getEndsOfEdge(edge: AbstractEdge<E>): [AbstractVertex<V>, AbstractVertex<V>] | null;
|
||||
|
||||
/**
|
||||
* BellmanFord time:O(VE) space:O(V)
|
||||
* one to rest pairs
|
||||
|
@ -659,16 +696,16 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* vertex.
|
||||
* @returns The function `bellmanFord` returns an object with the following properties:
|
||||
*/
|
||||
bellmanFord(src: V | VertexId, scanNegativeCycle?: boolean, getMin?: boolean, genPath?: boolean) {
|
||||
bellmanFord(src: AbstractVertex<V> | VertexId, scanNegativeCycle?: boolean, getMin?: boolean, genPath?: boolean) {
|
||||
if (getMin === undefined) getMin = false;
|
||||
if (genPath === undefined) genPath = false;
|
||||
|
||||
const srcVertex = this.getVertex(src);
|
||||
const paths: V[][] = [];
|
||||
const distMap: Map<V, number> = new Map();
|
||||
const preMap: Map<V, V> = new Map(); // predecessor
|
||||
const srcVertex = this._getVertex(src);
|
||||
const paths: AbstractVertex<V>[][] = [];
|
||||
const distMap: Map<AbstractVertex<V>, number> = new Map();
|
||||
const preMap: Map<AbstractVertex<V>, AbstractVertex<V>> = new Map(); // predecessor
|
||||
let min = Infinity;
|
||||
let minPath: V[] = [];
|
||||
let minPath: AbstractVertex<V>[] = [];
|
||||
// TODO
|
||||
let hasNegativeCycle: boolean | undefined;
|
||||
if (scanNegativeCycle) hasNegativeCycle = false;
|
||||
|
@ -703,7 +740,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
}
|
||||
}
|
||||
|
||||
let minDest: V | null = null;
|
||||
let minDest: AbstractVertex<V> | null = null;
|
||||
if (getMin) {
|
||||
distMap.forEach((d, v) => {
|
||||
if (v !== srcVertex) {
|
||||
|
@ -719,7 +756,7 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
for (const vertex of vertices) {
|
||||
const vertexOrId = vertex[1];
|
||||
if (vertexOrId instanceof AbstractVertex) {
|
||||
const path: V[] = [vertexOrId];
|
||||
const path: AbstractVertex<V>[] = [vertexOrId];
|
||||
let parent = preMap.get(vertexOrId);
|
||||
while (parent !== undefined) {
|
||||
path.push(parent);
|
||||
|
@ -748,9 +785,10 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
}
|
||||
|
||||
/**
|
||||
* Floyd algorithm time: O(V^3) space: O(V^2), not support graph with negative weight cycle
|
||||
* all pairs
|
||||
* The Floyd-Warshall algorithm is used to find the shortest paths between all pairs of nodes in a graph. It employs dynamic programming to compute the shortest paths from any node to any other node. The Floyd-Warshall algorithm's advantage lies in its ability to handle graphs with negative-weight edges, and it can simultaneously compute shortest paths between any two nodes.
|
||||
* BellmanFord time:O(VE) space:O(V)
|
||||
* one to rest pairs
|
||||
* The Bellman-Ford algorithm is also used to find the shortest paths from a source node to all other nodes in a graph. Unlike Dijkstra's algorithm, it can handle edge weights that are negative. Its basic idea involves iterative relaxation of all edges for several rounds to gradually approximate the shortest paths. Due to its ability to handle negative-weight edges, the Bellman-Ford algorithm is more flexible in some scenarios.
|
||||
* The `bellmanFord` function implements the Bellman-Ford algorithm to find the shortest path from a source vertex to
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -764,12 +802,12 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
* `predecessor` property is a 2D array of vertices (or `null`) representing the predecessor vertices in the shortest
|
||||
* path between vertices in the
|
||||
*/
|
||||
floyd(): { costs: number[][], predecessor: (V | null)[][] } {
|
||||
floyd(): { costs: number[][], predecessor: (AbstractVertex<V> | null)[][] } {
|
||||
const idAndVertices = [...this._vertices];
|
||||
const n = idAndVertices.length;
|
||||
|
||||
const costs: number[][] = [];
|
||||
const predecessor: (V | null)[][] = [];
|
||||
const predecessor: (AbstractVertex<V> | null)[][] = [];
|
||||
// successors
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
|
@ -800,8 +838,11 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**--- start find cycles --- */
|
||||
/**
|
||||
* Floyd algorithm time: O(V^3) space: O(V^2), not support graph with negative weight cycle
|
||||
* all pairs
|
||||
* The Floyd-Warshall algorithm is used to find the shortest paths between all pairs of nodes in a graph. It employs dynamic programming to compute the shortest paths from any node to any other node. The Floyd-Warshall algorithm's advantage lies in its ability to handle graphs with negative-weight edges, and it can simultaneously compute shortest paths between any two nodes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tarjan is an algorithm based on DFS,which is used to solve the connectivity problem of graphs.
|
||||
|
@ -835,8 +876,8 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
if (needSCCs === undefined) needSCCs = defaultConfig;
|
||||
if (needCycles === undefined) needCycles = defaultConfig;
|
||||
|
||||
const dfnMap: Map<V, number> = new Map();
|
||||
const lowMap: Map<V, number> = new Map();
|
||||
const dfnMap: Map<AbstractVertex<V>, number> = new Map();
|
||||
const lowMap: Map<AbstractVertex<V>, number> = new Map();
|
||||
const vertices = this._vertices;
|
||||
vertices.forEach(v => {
|
||||
dfnMap.set(v, -1);
|
||||
|
@ -845,10 +886,10 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
|
||||
const [root] = vertices.values();
|
||||
|
||||
const articulationPoints: V[] = [];
|
||||
const bridges: E[] = [];
|
||||
const articulationPoints: AbstractVertex<V>[] = [];
|
||||
const bridges: AbstractEdge<E>[] = [];
|
||||
let dfn = 0;
|
||||
const dfs = (cur: V, parent: V | null) => {
|
||||
const dfs = (cur: AbstractVertex<V>, parent: AbstractVertex<V> | null) => {
|
||||
dfn++;
|
||||
dfnMap.set(cur, dfn);
|
||||
lowMap.set(cur, dfn);
|
||||
|
@ -892,10 +933,10 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
|
||||
dfs(root, null);
|
||||
|
||||
let SCCs: Map<number, V[]> = new Map();
|
||||
let SCCs: Map<number, AbstractVertex<V>[]> = new Map();
|
||||
|
||||
const getSCCs = () => {
|
||||
const SCCs: Map<number, V[]> = new Map();
|
||||
const SCCs: Map<number, AbstractVertex<V>[]> = new Map();
|
||||
lowMap.forEach((low, vertex) => {
|
||||
if (!SCCs.has(low)) {
|
||||
SCCs.set(low, [vertex]);
|
||||
|
@ -910,9 +951,9 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
SCCs = getSCCs();
|
||||
}
|
||||
|
||||
const cycles: Map<number, V[]> = new Map();
|
||||
const cycles: Map<number, AbstractVertex<V>[]> = new Map();
|
||||
if (needCycles) {
|
||||
let SCCs: Map<number, V[]> = new Map();
|
||||
let SCCs: Map<number, AbstractVertex<V>[]> = new Map();
|
||||
if (SCCs.size < 1) {
|
||||
SCCs = getSCCs();
|
||||
}
|
||||
|
@ -928,6 +969,13 @@ export abstract class AbstractGraph<V extends AbstractVertex, E extends Abstract
|
|||
}
|
||||
|
||||
|
||||
/**--- start find cycles --- */
|
||||
|
||||
protected _setVertices(value: Map<VertexId, AbstractVertex<V>>) {
|
||||
this._vertices = value;
|
||||
}
|
||||
|
||||
|
||||
// unionFind() {}
|
||||
|
||||
/**--- end find cycles --- */
|
||||
|
|
|
@ -10,34 +10,45 @@ import {AbstractEdge, AbstractGraph, AbstractVertex} from './abstract-graph';
|
|||
import type {TopologicalStatus, VertexId} from '../types';
|
||||
import {IDirectedGraph} from '../interfaces';
|
||||
|
||||
export class DirectedVertex extends AbstractVertex {
|
||||
export class DirectedVertex<V = number> extends AbstractVertex<V> {
|
||||
/**
|
||||
* The constructor function initializes an object with a given id.
|
||||
* @param {VertexId} id - The `id` parameter is the identifier for the vertex. It is used to uniquely identify the
|
||||
* vertex within a graph or network.
|
||||
* The constructor function initializes a vertex with an optional value.
|
||||
* @param {VertexId} id - The `id` parameter is the identifier for the vertex. It is of type `VertexId`, which is
|
||||
* typically a unique identifier for each vertex in a graph.
|
||||
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to specify the value
|
||||
* associated with the vertex.
|
||||
*/
|
||||
constructor(id: VertexId) {
|
||||
super(id);
|
||||
constructor(id: VertexId, val?: V) {
|
||||
super(id, val);
|
||||
}
|
||||
|
||||
// _createVertex(id: VertexId, val?: V): DirectedVertex<V> {
|
||||
// return new DirectedVertex<V>(id, val);
|
||||
// }
|
||||
}
|
||||
|
||||
export class DirectedEdge extends AbstractEdge {
|
||||
export class DirectedEdge<E = number> extends AbstractEdge<E> {
|
||||
|
||||
/**
|
||||
* The constructor function initializes the source and destination vertices of an edge, with an optional weight.
|
||||
* The constructor function initializes the source and destination vertices of an edge, along with an optional weight
|
||||
* and value.
|
||||
* @param {VertexId} src - The `src` parameter is the source vertex ID. It represents the starting point of an edge in
|
||||
* a graph.
|
||||
* @param {VertexId} dest - The `dest` parameter is the identifier of the destination vertex. It represents the vertex
|
||||
* to which an edge is directed.
|
||||
* @param {number} [weight] - The `weight` parameter is an optional number that represents the weight of the edge
|
||||
* between two vertices.
|
||||
* @param {VertexId} dest - The `dest` parameter is the identifier of the destination vertex for an edge.
|
||||
* @param {number} [weight] - The `weight` parameter is an optional number that represents the weight of the edge. It
|
||||
* is used to assign a numerical value to the edge, which can be used in algorithms such as shortest path algorithms.
|
||||
* If the weight is not provided, it will default to `undefined`.
|
||||
* @param {E} [val] - The "val" parameter is an optional parameter of type E. It represents the value associated with
|
||||
* the edge.
|
||||
*/
|
||||
constructor(src: VertexId, dest: VertexId, weight?: number) {
|
||||
super(weight);
|
||||
constructor(src: VertexId, dest: VertexId, weight?: number, val?: E) {
|
||||
super(weight, val);
|
||||
this._src = src;
|
||||
this._dest = dest;
|
||||
}
|
||||
|
||||
private _src: VertexId;
|
||||
|
||||
get src(): VertexId {
|
||||
return this._src;
|
||||
}
|
||||
|
@ -55,33 +66,69 @@ export class DirectedEdge extends AbstractEdge {
|
|||
set dest(v: VertexId) {
|
||||
this._dest = v;
|
||||
}
|
||||
|
||||
// _createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): DirectedEdge<E> {
|
||||
// if (weight === undefined || weight === null) weight = 1;
|
||||
// return new DirectedEdge(src, dest, weight, val);
|
||||
// }
|
||||
}
|
||||
|
||||
// Strongly connected, One direction connected, Weakly connected
|
||||
export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> extends AbstractGraph<V, E> implements IDirectedGraph<V, E> {
|
||||
|
||||
protected _outEdgeMap: Map<V, E[]> = new Map<V, E[]>();
|
||||
|
||||
protected _inEdgeMap: Map<V, E[]> = new Map<V, E[]>();
|
||||
export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> implements IDirectedGraph<V, E> {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
private _outEdgeMap: Map<DirectedVertex<V>, DirectedEdge<E>[]> = new Map<DirectedVertex<V>, DirectedEdge<E>[]>();
|
||||
|
||||
get outEdgeMap(): Map<DirectedVertex<V>, DirectedEdge<E>[]> {
|
||||
return this._outEdgeMap;
|
||||
}
|
||||
|
||||
private _inEdgeMap: Map<DirectedVertex<V>, DirectedEdge<E>[]> = new Map<DirectedVertex<V>, DirectedEdge<E>[]>();
|
||||
|
||||
get inEdgeMap(): Map<DirectedVertex<V>, DirectedEdge<E>[]> {
|
||||
return this._inEdgeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `getEdge` returns the first edge between two vertices, given their source and destination.
|
||||
* @param {V | null | VertexId} srcOrId - The `srcOrId` parameter can be either a vertex object (`V`), a vertex ID
|
||||
* (`VertexId`), or `null`. It represents the source vertex of the edge.
|
||||
* @param {V | null | VertexId} destOrId - The `destOrId` parameter is either a vertex object (`V`), a vertex ID
|
||||
* (`VertexId`), or `null`. It represents the destination vertex of the edge.
|
||||
* @returns an edge (E) or null.
|
||||
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
* @param id
|
||||
* @param val
|
||||
*/
|
||||
getEdge(srcOrId: V | null | VertexId, destOrId: V | null | VertexId): E | null {
|
||||
let edges: E[] = [];
|
||||
_createVertex(id: VertexId, val?: V): DirectedVertex<V> {
|
||||
return new DirectedVertex<V>(id, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
* @param src
|
||||
* @param dest
|
||||
* @param weight
|
||||
* @param val
|
||||
*/
|
||||
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): DirectedEdge<E> {
|
||||
if (weight === undefined || weight === null) weight = 1;
|
||||
return new DirectedEdge(src, dest, weight, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `getEdge` returns the directed edge between two vertices, given their source and destination.
|
||||
* @param {DirectedVertex<V> | null | VertexId} srcOrId - The source vertex or its ID. It can be either a
|
||||
* DirectedVertex object or a VertexId.
|
||||
* @param {DirectedVertex<V> | null | VertexId} destOrId - The `destOrId` parameter is the destination vertex or its
|
||||
* ID. It can be either a `DirectedVertex` object or a `VertexId` value.
|
||||
* @returns a DirectedEdge<E> object or null.
|
||||
*/
|
||||
getEdge(srcOrId: DirectedVertex<V> | null | VertexId, destOrId: DirectedVertex<V> | null | VertexId): DirectedEdge<E> | null {
|
||||
let edges: DirectedEdge<E>[] = [];
|
||||
|
||||
if (srcOrId !== null && destOrId !== null) {
|
||||
const src: V | null = this.getVertex(srcOrId);
|
||||
const dest: V | null = this.getVertex(destOrId);
|
||||
const src: DirectedVertex<V> | null = this._getVertex(srcOrId);
|
||||
const dest: DirectedVertex<V> | null = this._getVertex(destOrId);
|
||||
|
||||
if (src && dest) {
|
||||
const srcOutEdges = this._outEdgeMap.get(src);
|
||||
|
@ -95,19 +142,19 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
}
|
||||
|
||||
/**
|
||||
* The `addEdge` function adds an edge to a graph if the source and destination vertices exist.
|
||||
* @param {E} edge - The parameter `edge` is of type `E`, which represents an edge in the graph. It contains
|
||||
* information about the source vertex (`src`) and the destination vertex (`dest`) of the edge.
|
||||
* @returns The `addEdge` function returns a boolean value. It returns `true` if the edge was successfully added to the
|
||||
* graph, and `false` if either the source or destination vertices of the edge are not present in the graph.
|
||||
* The `addEdge` function adds a directed edge to a graph if the source and destination vertices exist.
|
||||
* @param edge - The parameter `edge` is of type `DirectedEdge<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
|
||||
* graph, and `false` if either the source or destination vertex of the edge is not present in the graph.
|
||||
*/
|
||||
addEdge(edge: E): boolean {
|
||||
addEdge(edge: DirectedEdge<E>): boolean {
|
||||
if (!(this.hasVertex(edge.src) && this.hasVertex(edge.dest))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const srcVertex = this.getVertex(edge.src);
|
||||
const destVertex = this.getVertex(edge.dest);
|
||||
const srcVertex = this._getVertex(edge.src);
|
||||
const destVertex = this._getVertex(edge.dest);
|
||||
|
||||
// TODO after no-non-null-assertion not ensure the logic
|
||||
if (srcVertex && destVertex) {
|
||||
|
@ -131,20 +178,20 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
}
|
||||
|
||||
/**
|
||||
* The function removes an edge between two vertices in a directed graph and returns the removed edge.
|
||||
* @param {V | VertexId} srcOrId - The source vertex or its ID.
|
||||
* @param {V | VertexId} destOrId - The `destOrId` parameter in the `removeEdgeBetween` function represents the
|
||||
* destination vertex of the edge that needs to be removed. It can be either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`).
|
||||
* @returns The function `removeEdgeBetween` returns the removed edge (`E`) if the edge between the source and
|
||||
* destination vertices is successfully removed. If either the source or destination vertex is not found, or if the
|
||||
* edge does not exist, it returns `null`.
|
||||
* The `removeEdgeBetween` function removes an edge between two vertices in a directed graph and returns the removed
|
||||
* edge, or null if the edge was not found.
|
||||
* @param {DirectedVertex<V> | VertexId} srcOrId - The `srcOrId` parameter represents either a `DirectedVertex<V>`
|
||||
* object or a `VertexId` value. It is used to specify the source vertex of the edge that you want to remove.
|
||||
* @param {DirectedVertex<V> | VertexId} destOrId - The `destOrId` parameter represents the destination vertex of the
|
||||
* edge that you want to remove. It can be either a `DirectedVertex<V>` object or a `VertexId` value.
|
||||
* @returns The function `removeEdgeBetween` returns the removed edge (`DirectedEdge<E>`) if it exists, or `null` if
|
||||
* the edge does not exist.
|
||||
*/
|
||||
removeEdgeBetween(srcOrId: V | VertexId, destOrId: V | VertexId): E | null {
|
||||
removeEdgeBetween(srcOrId: DirectedVertex<V> | VertexId, destOrId: DirectedVertex<V> | VertexId): DirectedEdge<E> | null {
|
||||
|
||||
const src: V | null = this.getVertex(srcOrId);
|
||||
const dest: V | null = this.getVertex(destOrId);
|
||||
let removed: E | null = null;
|
||||
const src: DirectedVertex<V> | null = this._getVertex(srcOrId);
|
||||
const dest: DirectedVertex<V> | null = this._getVertex(destOrId);
|
||||
let removed: DirectedEdge<E> | null = null;
|
||||
if (!src || !dest) {
|
||||
return null;
|
||||
}
|
||||
|
@ -154,41 +201,42 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
/**
|
||||
* The removeEdge function removes an edge from a graph and returns the removed edge, or null if the edge was not
|
||||
* found.
|
||||
* @param {E} edge - The `edge` parameter represents the edge that you want to remove from the graph. It should be an
|
||||
* @param {DirectedEdge<E>} edge - The `edge` parameter represents the edge that you want to remove from the graph. It should be an
|
||||
* object that has `src` and `dest` properties, which represent the source and destination vertices of the edge,
|
||||
* respectively.
|
||||
* @returns The method `removeEdge` returns the removed edge (`E`) if it exists, or `null` if the edge does not exist.
|
||||
* @returns The method `removeEdge` returns the removed edge (`DirectedEdge<E>`) if it exists, or `null` if the edge does not exist.
|
||||
*/
|
||||
arrayRemove<E>(srcOutEdges, (edge: DirectedEdge) => edge.dest === dest.id);
|
||||
arrayRemove<DirectedEdge<E>>(srcOutEdges, (edge: DirectedEdge<E>) => edge.dest === dest.id);
|
||||
}
|
||||
|
||||
const destInEdges = this._inEdgeMap.get(dest);
|
||||
if (destInEdges) {
|
||||
removed = arrayRemove<E>(destInEdges, (edge: DirectedEdge) => edge.src === src.id)[0] || null;
|
||||
removed = arrayRemove<DirectedEdge<E>>(destInEdges, (edge: DirectedEdge<E>) => edge.src === src.id)[0] || null;
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* The removeEdge function removes an edge from a graph and returns the removed edge, or null if the edge was not
|
||||
* found.
|
||||
* @param {E} edge - The `edge` parameter is an object that represents an edge in a graph. It has two properties: `src`
|
||||
* and `dest`, which represent the source and destination vertices of the edge, respectively.
|
||||
* @returns The method `removeEdge` returns the removed edge (`E`) if it exists, or `null` if the edge does not exist.
|
||||
* The `removeEdge` function removes a directed edge from a graph and returns the removed edge, or null if the edge was
|
||||
* not found.
|
||||
* @param edge - The `edge` parameter is an object of type `DirectedEdge<E>`, which represents a directed edge in a
|
||||
* graph. It has two properties:
|
||||
* @returns The function `removeEdge` returns a `DirectedEdge<E>` object if an edge is successfully removed, or `null`
|
||||
* if no edge is removed.
|
||||
*/
|
||||
removeEdge(edge: E): E | null {
|
||||
let removed: E | null = null;
|
||||
const src = this.getVertex(edge.src);
|
||||
const dest = this.getVertex(edge.dest);
|
||||
removeEdge(edge: DirectedEdge<E>): DirectedEdge<E> | null {
|
||||
let removed: DirectedEdge<E> | null = null;
|
||||
const src = this._getVertex(edge.src);
|
||||
const dest = this._getVertex(edge.dest);
|
||||
if (src && dest) {
|
||||
const srcOutEdges = this._outEdgeMap.get(src);
|
||||
if (srcOutEdges && srcOutEdges.length > 0) {
|
||||
arrayRemove(srcOutEdges, (edge: DirectedEdge) => edge.src === src.id);
|
||||
arrayRemove(srcOutEdges, (edge: DirectedEdge<E>) => edge.src === src.id);
|
||||
}
|
||||
|
||||
const destInEdges = this._inEdgeMap.get(dest);
|
||||
if (destInEdges && destInEdges.length > 0) {
|
||||
removed = arrayRemove(destInEdges, (edge: E) => edge.dest === dest.id)[0];
|
||||
removed = arrayRemove(destInEdges, (edge: DirectedEdge<E>) => edge.dest === dest.id)[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -198,38 +246,37 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
|
||||
/**
|
||||
* The function removeAllEdges removes all edges between two vertices.
|
||||
* @param {VertexId | V} src - The `src` parameter represents the source vertex from which the edges will be removed.
|
||||
* It can be either a `VertexId` or a `V` type, which represents the identifier or object of the vertex.
|
||||
* @param {VertexId | V} dest - The `dest` parameter represents the destination vertex of an edge. It can be either a
|
||||
* `VertexId` or a vertex object `V`.
|
||||
* @returns An empty array is being returned.
|
||||
* @param {VertexId | DirectedVertex<V>} src - The `src` parameter can be either a `VertexId` or a `DirectedVertex<V>`.
|
||||
* @param {VertexId | DirectedVertex<V>} dest - The `dest` parameter represents the destination vertex of an edge. It
|
||||
* can be either a `VertexId` or a `DirectedVertex<V>`.
|
||||
* @returns An empty array of DirectedEdge objects is being returned.
|
||||
*/
|
||||
removeAllEdges(src: VertexId | V, dest: VertexId | V): E[] {
|
||||
removeAllEdges(src: VertexId | DirectedVertex<V>, dest: VertexId | DirectedVertex<V>): DirectedEdge<E>[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `incomingEdgesOf` returns an array of incoming edges for a given vertex or vertex ID.
|
||||
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`).
|
||||
* @returns The method `incomingEdgesOf` returns an array of edges (`E[]`).
|
||||
* The function returns an array of incoming edges of a given vertex or vertex ID.
|
||||
* @param {DirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `DirectedVertex<V>`
|
||||
* object or a `VertexId`.
|
||||
* @returns The method `incomingEdgesOf` returns an array of `DirectedEdge<E>` objects.
|
||||
*/
|
||||
incomingEdgesOf(vertexOrId: V | VertexId): E[] {
|
||||
const target = this.getVertex(vertexOrId);
|
||||
incomingEdgesOf(vertexOrId: DirectedVertex<V> | VertexId): DirectedEdge<E>[] {
|
||||
const target = this._getVertex(vertexOrId);
|
||||
if (target) {
|
||||
return this._inEdgeMap.get(target) || [];
|
||||
return this.inEdgeMap.get(target) || []
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `outgoingEdgesOf` returns an array of outgoing edges from a given vertex or vertex ID.
|
||||
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can accept either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`).
|
||||
* @returns The method `outgoingEdgesOf` returns an array of outgoing edges from a given vertex or vertex ID.
|
||||
* The function `outgoingEdgesOf` returns an array of outgoing directed edges from a given vertex or vertex ID.
|
||||
* @param {DirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `DirectedVertex<V>`
|
||||
* object or a `VertexId`.
|
||||
* @returns The method `outgoingEdgesOf` returns an array of `DirectedEdge<E>` objects.
|
||||
*/
|
||||
outgoingEdgesOf(vertexOrId: V | VertexId): E[] {
|
||||
const target = this.getVertex(vertexOrId);
|
||||
outgoingEdgesOf(vertexOrId: DirectedVertex<V> | VertexId): DirectedEdge<E>[] {
|
||||
const target = this._getVertex(vertexOrId);
|
||||
if (target) {
|
||||
return this._outEdgeMap.get(target) || [];
|
||||
}
|
||||
|
@ -237,71 +284,76 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
}
|
||||
|
||||
/**
|
||||
* The function "degreeOf" returns the total degree of a vertex, which is the sum of its out-degree and in-degree.
|
||||
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a `V`.
|
||||
* @returns the sum of the out-degree and in-degree of the specified vertex or vertex ID.
|
||||
* The function "degreeOf" returns the total degree of a vertex in a directed graph, which is the sum of its out-degree
|
||||
* and in-degree.
|
||||
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
|
||||
* `DirectedVertex<V>`.
|
||||
* @returns The sum of the out-degree and in-degree of the given vertex or vertex ID.
|
||||
*/
|
||||
degreeOf(vertexOrId: VertexId | V): number {
|
||||
degreeOf(vertexOrId: VertexId | DirectedVertex<V>): number {
|
||||
return this.outDegreeOf(vertexOrId) + this.inDegreeOf(vertexOrId);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "inDegreeOf" returns the number of incoming edges for a given vertex.
|
||||
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a `V`.
|
||||
* The function "inDegreeOf" returns the number of incoming edges for a given vertex or vertex ID in a directed graph.
|
||||
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
|
||||
* `DirectedVertex<V>`.
|
||||
* @returns The number of incoming edges of the specified vertex or vertex ID.
|
||||
*/
|
||||
inDegreeOf(vertexOrId: VertexId | V): number {
|
||||
inDegreeOf(vertexOrId: VertexId | DirectedVertex<V>): number {
|
||||
return this.incomingEdgesOf(vertexOrId).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `outDegreeOf` returns the number of outgoing edges from a given vertex.
|
||||
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a `V`.
|
||||
* The function "outDegreeOf" returns the number of outgoing edges from a given vertex.
|
||||
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
|
||||
* `DirectedVertex<V>`.
|
||||
* @returns The number of outgoing edges from the specified vertex or vertex ID.
|
||||
*/
|
||||
outDegreeOf(vertexOrId: VertexId | V): number {
|
||||
outDegreeOf(vertexOrId: VertexId | DirectedVertex<V>): number {
|
||||
return this.outgoingEdgesOf(vertexOrId).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "edgesOf" returns an array of both outgoing and incoming edges of a given vertex or vertex ID.
|
||||
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a `V`.
|
||||
* @returns The function `edgesOf` returns an array of edges.
|
||||
* The function "edgesOf" returns an array of both outgoing and incoming directed edges of a given vertex or vertex ID.
|
||||
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
|
||||
* `DirectedVertex<V>`.
|
||||
* @returns an array of directed edges.
|
||||
*/
|
||||
edgesOf(vertexOrId: VertexId | V): E[] {
|
||||
edgesOf(vertexOrId: VertexId | DirectedVertex<V>): DirectedEdge<E>[] {
|
||||
return [...this.outgoingEdgesOf(vertexOrId), ...this.incomingEdgesOf(vertexOrId)];
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "getEdgeSrc" returns the source vertex of an edge, or null if the edge does not exist.
|
||||
* @param {E} e - E - an edge object
|
||||
* @returns the source vertex of the given edge, or null if the edge does not exist.
|
||||
* The function "getEdgeSrc" returns the source vertex of a directed edge, or null if the edge does not exist.
|
||||
* @param e - A directed edge object of type E.
|
||||
* @returns either a DirectedVertex object or null.
|
||||
*/
|
||||
getEdgeSrc(e: E): V | null {
|
||||
return this.getVertex(e.src);
|
||||
getEdgeSrc(e: DirectedEdge<E>): DirectedVertex<V> | null {
|
||||
return this._getVertex(e.src);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "getEdgeDest" returns the vertex associated with the destination of an edge.
|
||||
* @param {E} e - The parameter `e` is of type `E`, which represents an edge in a graph.
|
||||
* @returns either a vertex object (of type V) or null.
|
||||
* The function "getEdgeDest" returns the destination vertex of a directed edge.
|
||||
* @param e - DirectedEdge<E> - This is an object representing a directed edge in a graph. It contains information
|
||||
* about the source vertex, destination vertex, and any associated data.
|
||||
* @returns either a DirectedVertex object or null.
|
||||
*/
|
||||
getEdgeDest(e: E): V | null {
|
||||
return this.getVertex(e.dest);
|
||||
getEdgeDest(e: DirectedEdge<E>): DirectedVertex<V> | null {
|
||||
return this._getVertex(e.dest);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `getDestinations` returns an array of destination vertices connected to a given vertex.
|
||||
* @param {V | VertexId | null} vertex - The `vertex` parameter represents the starting vertex from which we want to
|
||||
* find the destinations. It can be either a `V` object, a `VertexId` (which is a unique identifier for a vertex), or
|
||||
* `null` if we want to find destinations from all vertices.
|
||||
* @returns an array of vertices (V[]).
|
||||
* The function `getDestinations` returns an array of directed vertices that are the destinations of outgoing edges
|
||||
* from a given vertex.
|
||||
* @param {DirectedVertex<V> | VertexId | null} vertex - The `vertex` parameter can be one of the following:
|
||||
* @returns an array of DirectedVertex objects.
|
||||
*/
|
||||
getDestinations(vertex: V | VertexId | null): V[] {
|
||||
getDestinations(vertex: DirectedVertex<V> | VertexId | null): DirectedVertex<V>[] {
|
||||
if (vertex === null) {
|
||||
return [];
|
||||
}
|
||||
const destinations: V[] = [];
|
||||
const destinations: DirectedVertex<V>[] = [];
|
||||
const outgoingEdges = this.outgoingEdgesOf(vertex);
|
||||
for (const outEdge of outgoingEdges) {
|
||||
const child = this.getEdgeDest(outEdge);
|
||||
|
@ -312,27 +364,23 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
return destinations;
|
||||
}
|
||||
|
||||
/**--- start find cycles --- */
|
||||
|
||||
/**
|
||||
* when stored with adjacency list time: O(V+E)
|
||||
* when stored with adjacency matrix time: O(V^2)
|
||||
* The `topologicalSort` function performs a topological sort on a 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 vertices in topological order if there is no cycle in
|
||||
* the graph. If there is a cycle, it returns `null`.
|
||||
* 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 `DirectedVertex<V>` or `VertexId` objects in
|
||||
* topological order, or `null` if there is a cycle in the graph.
|
||||
*/
|
||||
topologicalSort(): Array<V | VertexId> | null {
|
||||
topologicalSort(): Array<DirectedVertex<V> | VertexId> | null {
|
||||
// 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<V | VertexId, TopologicalStatus> = new Map<V | VertexId, TopologicalStatus>();
|
||||
for (const entry of this._vertices) {
|
||||
const statusMap: Map<DirectedVertex<V> | VertexId, TopologicalStatus> = new Map<DirectedVertex<V> | VertexId, TopologicalStatus>();
|
||||
for (const entry of this.vertices) {
|
||||
statusMap.set(entry[1], 0);
|
||||
}
|
||||
|
||||
const sorted: (V | VertexId)[] = [];
|
||||
const sorted: (DirectedVertex<V> | VertexId)[] = [];
|
||||
let hasCycle = false;
|
||||
const dfs = (cur: V | VertexId) => {
|
||||
const dfs = (cur: DirectedVertex<V> | VertexId) => {
|
||||
statusMap.set(cur, 1);
|
||||
const children = this.getDestinations(cur);
|
||||
for (const child of children) {
|
||||
|
@ -347,7 +395,7 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
sorted.push(cur);
|
||||
};
|
||||
|
||||
for (const entry of this._vertices) {
|
||||
for (const entry of this.vertices) {
|
||||
if (statusMap.get(entry[1]) === 0) {
|
||||
dfs(entry[1]);
|
||||
}
|
||||
|
@ -358,33 +406,33 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
return sorted.reverse();
|
||||
}
|
||||
|
||||
/**--- end find cycles --- */
|
||||
|
||||
/**
|
||||
* The `edgeSet` function returns an array of all the edges in the graph.
|
||||
* @returns The `edgeSet()` method returns an array of edges (`E[]`).
|
||||
* The `edgeSet` function returns an array of all directed edges in the graph.
|
||||
* @returns The `edgeSet()` method returns an array of `DirectedEdge<E>` objects.
|
||||
*/
|
||||
edgeSet(): E[] {
|
||||
let edges: E[] = [];
|
||||
edgeSet(): DirectedEdge<E>[] {
|
||||
let edges: DirectedEdge<E>[] = [];
|
||||
this._outEdgeMap.forEach(outEdges => {
|
||||
edges = [...edges, ...outEdges];
|
||||
});
|
||||
return edges;
|
||||
}
|
||||
|
||||
/**--- start find cycles --- */
|
||||
|
||||
/**
|
||||
* The function `getNeighbors` returns an array of neighboring vertices for a given vertex or vertex ID.
|
||||
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`).
|
||||
* @returns an array of vertices (V[]).
|
||||
* The function `getNeighbors` returns an array of neighboring vertices of a given vertex in a directed graph.
|
||||
* @param {DirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `DirectedVertex<V>`
|
||||
* object or a `VertexId`.
|
||||
* @returns an array of DirectedVertex objects.
|
||||
*/
|
||||
getNeighbors(vertexOrId: V | VertexId): V[] {
|
||||
const neighbors: V[] = [];
|
||||
const vertex = this.getVertex(vertexOrId);
|
||||
getNeighbors(vertexOrId: DirectedVertex<V> | VertexId): DirectedVertex<V>[] {
|
||||
const neighbors: DirectedVertex<V>[] = [];
|
||||
const vertex = this._getVertex(vertexOrId);
|
||||
if (vertex) {
|
||||
const outEdges = this.outgoingEdgesOf(vertex);
|
||||
for (const outEdge of outEdges) {
|
||||
const neighbor = this.getVertex(outEdge.dest);
|
||||
const neighbor = this._getVertex(outEdge.dest);
|
||||
// TODO after no-non-null-assertion not ensure the logic
|
||||
if (neighbor) {
|
||||
neighbors.push(neighbor);
|
||||
|
@ -394,23 +442,33 @@ export class DirectedGraph<V extends DirectedVertex, E extends DirectedEdge> ext
|
|||
return neighbors;
|
||||
}
|
||||
|
||||
/**--- end find cycles --- */
|
||||
|
||||
/**
|
||||
* The function "getEndsOfEdge" returns the source and destination vertices of an edge if it exists in the graph,
|
||||
* otherwise it returns null.
|
||||
* @param {E} edge - The parameter "edge" is of type E, which represents an edge in a graph.
|
||||
* @returns an array containing two vertices [V, V] if the edge is found in the graph. If the edge is not found, it
|
||||
* returns null.
|
||||
* The function "getEndsOfEdge" returns the source and destination vertices of a directed edge if it exists in the
|
||||
* graph, otherwise it returns null.
|
||||
* @param edge - A directed edge object with a generic type E.
|
||||
* @returns an array containing the starting and ending vertices of the given directed edge, or null if the edge does
|
||||
* not exist in the graph.
|
||||
*/
|
||||
getEndsOfEdge(edge: E): [V, V] | null {
|
||||
getEndsOfEdge(edge: DirectedEdge<E>): [DirectedVertex<V>, DirectedVertex<V>] | null {
|
||||
if (!this.hasEdge(edge.src, edge.dest)) {
|
||||
return null;
|
||||
}
|
||||
const v1 = this.getVertex(edge.src);
|
||||
const v2 = this.getVertex(edge.dest);
|
||||
const v1 = this._getVertex(edge.src);
|
||||
const v2 = this._getVertex(edge.dest);
|
||||
if (v1 && v2) {
|
||||
return [v1, v2];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected _setOutEdgeMap(value: Map<DirectedVertex<V>, DirectedEdge<E>[]>) {
|
||||
this._outEdgeMap = value;
|
||||
}
|
||||
|
||||
protected _setInEdgeMap(value: Map<DirectedVertex<V>, DirectedEdge<E>[]>) {
|
||||
this._inEdgeMap = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,32 +9,41 @@ import {arrayRemove} from '../../utils';
|
|||
import {AbstractEdge, AbstractGraph, AbstractVertex} from './abstract-graph';
|
||||
import type {VertexId} from '../types';
|
||||
|
||||
export class UndirectedVertex extends AbstractVertex {
|
||||
export class UndirectedVertex<V = number> extends AbstractVertex<V> {
|
||||
/**
|
||||
* The constructor function initializes an object with a given id.
|
||||
* @param {VertexId} id - The `id` parameter is the identifier for the vertex. It is used to uniquely identify the
|
||||
* vertex within a graph or network.
|
||||
* The constructor function initializes a vertex with an optional value.
|
||||
* @param {VertexId} id - The `id` parameter is the identifier for the vertex. It is of type `VertexId`, which is
|
||||
* typically a unique identifier for each vertex in a graph.
|
||||
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to initialize the value of the
|
||||
* vertex. If no value is provided, the vertex will be initialized with a default value.
|
||||
*/
|
||||
constructor(id: VertexId) {
|
||||
super(id);
|
||||
constructor(id: VertexId, val?: V) {
|
||||
super(id, val);
|
||||
}
|
||||
|
||||
// _createVertex(id: VertexId, val?: V): UndirectedVertex<V> {
|
||||
// return new UndirectedVertex<V>(id, val);
|
||||
// }
|
||||
}
|
||||
|
||||
export class UndirectedEdge extends AbstractEdge {
|
||||
export class UndirectedEdge<E = number> extends AbstractEdge<E> {
|
||||
/**
|
||||
* The constructor function initializes an instance of a class with two vertex IDs and an optional weight.
|
||||
* The constructor function initializes an instance of a class with two vertex IDs, an optional weight, and an optional
|
||||
* value.
|
||||
* @param {VertexId} v1 - The parameter `v1` is of type `VertexId` and represents the first vertex in the edge.
|
||||
* @param {VertexId} v2 - The parameter `v2` is a `VertexId`, which represents the identifier of the second vertex in a
|
||||
* graph.
|
||||
* @param {number} [weight] - The `weight` parameter is an optional number that represents the weight of the edge
|
||||
* between two vertices.
|
||||
* graph edge.
|
||||
* @param {number} [weight] - The weight parameter is an optional number that represents the weight of the edge.
|
||||
* @param {E} [val] - The "val" parameter is an optional parameter of type E. It represents the value associated with
|
||||
* the edge.
|
||||
*/
|
||||
constructor(v1: VertexId, v2: VertexId, weight?: number) {
|
||||
super(weight);
|
||||
constructor(v1: VertexId, v2: VertexId, weight?: number, val?: E) {
|
||||
super(weight, val);
|
||||
this._vertices = [v1, v2];
|
||||
}
|
||||
|
||||
private _vertices: [VertexId, VertexId];
|
||||
|
||||
get vertices() {
|
||||
return this._vertices;
|
||||
}
|
||||
|
@ -43,40 +52,65 @@ export class UndirectedEdge extends AbstractEdge {
|
|||
this._vertices = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting from TypeScript version 5.0 and onwards, the use of distinct access modifiers for Getters and Setters is not permitted. As an alternative, to ensure compatibility, it is necessary to adopt a Java-style approach for Setters (using the same name as the property) while utilizing separate method names for Getters.
|
||||
*/
|
||||
getVertices() {
|
||||
return this._vertices;
|
||||
}
|
||||
// _createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): UndirectedEdge<E> {
|
||||
// if (weight === undefined || weight === null) weight = 1;
|
||||
// return new UndirectedEdge(src, dest, weight, val);
|
||||
// }
|
||||
}
|
||||
|
||||
export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdge> extends AbstractGraph<V, E> {
|
||||
/**
|
||||
* The constructor initializes a new instance of the class with an empty map of edges.
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this._edges = new Map<V, E[]>();
|
||||
this._edges = new Map<UndirectedVertex<V>, UndirectedEdge<E>[]>();
|
||||
}
|
||||
|
||||
protected _edges: Map<V, E[]>;
|
||||
protected _edges: Map<UndirectedVertex<V>, UndirectedEdge<E>[]>;
|
||||
|
||||
get edges(): Map<V, E[]> {
|
||||
get edges(): Map<UndirectedVertex<V>, UndirectedEdge<E>[]> {
|
||||
return this._edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `getEdge` returns the first edge that connects two vertices, or null if no such edge exists.
|
||||
* @param {V | null | VertexId} v1 - The parameter `v1` represents either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`). It can also be `null`.
|
||||
* @param {V | null | VertexId} v2 - The parameter `v2` represents a vertex or vertex ID. It can be of type `V` (vertex
|
||||
* object), `null`, or `VertexId` (vertex ID).
|
||||
* @returns an edge (E) or null.
|
||||
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
* @param id
|
||||
* @param val
|
||||
*/
|
||||
getEdge(v1: V | null | VertexId, v2: V | null | VertexId): E | null {
|
||||
let edges: E[] | undefined = [];
|
||||
_createVertex(id: VertexId, val?: V): UndirectedVertex<V> {
|
||||
return new UndirectedVertex<V>(id, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
|
||||
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
|
||||
* @param src
|
||||
* @param dest
|
||||
* @param weight
|
||||
* @param val
|
||||
*/
|
||||
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): UndirectedEdge<E> {
|
||||
if (weight === undefined || weight === null) weight = 1;
|
||||
return new UndirectedEdge(src, dest, weight, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `getEdge` returns the first undirected edge that connects two given vertices, or null if no such edge
|
||||
* exists.
|
||||
* @param {UndirectedVertex<V> | null | VertexId} v1 - The parameter `v1` represents either an `UndirectedVertex<V>`
|
||||
* object, `null`, or a `VertexId`. It is used to specify one of the vertices of the edge.
|
||||
* @param {UndirectedVertex<V> | null | VertexId} v2 - The parameter `v2` represents either an `UndirectedVertex`
|
||||
* object or a `VertexId` (identifier) of an undirected vertex.
|
||||
* @returns an instance of `UndirectedEdge<E>` or `null`.
|
||||
*/
|
||||
getEdge(v1: UndirectedVertex<V> | null | VertexId, v2: UndirectedVertex<V> | null | VertexId): UndirectedEdge<E> | null {
|
||||
let edges: UndirectedEdge<E>[] | undefined = [];
|
||||
|
||||
if (v1 !== null && v2 !== null) {
|
||||
const vertex1: V | null = this.getVertex(v1);
|
||||
const vertex2: V | null = this.getVertex(v2);
|
||||
const vertex1: UndirectedVertex<V> | null = this._getVertex(v1);
|
||||
const vertex2: UndirectedVertex<V> | null = this._getVertex(v2);
|
||||
|
||||
if (vertex1 && vertex2) {
|
||||
edges = this._edges.get(vertex1)?.filter(e => e.vertices.includes(vertex2.id));
|
||||
|
@ -87,13 +121,14 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
|
||||
/**
|
||||
* The function adds an edge to a graph by connecting two vertices.
|
||||
* @param {E} edge - The `edge` parameter is an object of type `E`, which represents an edge in a graph.
|
||||
* The function adds an undirected edge to a graph by updating the adjacency list.
|
||||
* @param edge - An object representing an undirected edge in a graph. It has a property called "vertices" which is an
|
||||
* array of two vertices connected by the edge.
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
addEdge(edge: E): boolean {
|
||||
addEdge(edge: UndirectedEdge<E>): boolean {
|
||||
for (const end of edge.vertices) {
|
||||
const endVertex = this.getVertex(end);
|
||||
const endVertex = this._getVertex(end);
|
||||
if (endVertex === null) return false;
|
||||
if (endVertex) {
|
||||
const edges = this._edges.get(endVertex);
|
||||
|
@ -108,51 +143,55 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
|
||||
/**
|
||||
* The function removes an edge between two vertices in a graph and returns the removed edge, or null if either of the
|
||||
* vertices does not exist.
|
||||
* @param {V | VertexId} v1 - The parameter `v1` represents either a vertex object (`V`) or a vertex ID (`VertexId`).
|
||||
* @param {V | VertexId} v2 - V | VertexId: The second vertex or vertex ID of the edge to be removed.
|
||||
* @returns the removed edge (E) if it exists, or null if either of the vertices (v1 or v2) does not exist.
|
||||
* The function removes an edge between two vertices in an undirected graph.
|
||||
* @param {UndirectedVertex<V> | VertexId} v1 - The parameter `v1` represents either an `UndirectedVertex<V>` object or
|
||||
* a `VertexId`. It is used to specify one of the vertices of the edge that needs to be removed.
|
||||
* @param {UndirectedVertex<V> | VertexId} v2 - The parameter `v2` represents either an instance of the
|
||||
* `UndirectedVertex` class or a `VertexId`. It is used to identify the second vertex of the edge that needs to be
|
||||
* removed.
|
||||
* @returns The function `removeEdgeBetween` returns an `UndirectedEdge<E>` object if an edge is successfully removed
|
||||
* between the two vertices `v1` and `v2`. If either `v1` or `v2` is not found in the graph, or if there is no edge
|
||||
* between them, the function returns `null`.
|
||||
*/
|
||||
removeEdgeBetween(v1: V | VertexId, v2: V | VertexId): E | null {
|
||||
removeEdgeBetween(v1: UndirectedVertex<V> | VertexId, v2: UndirectedVertex<V> | VertexId): UndirectedEdge<E> | null {
|
||||
|
||||
const vertex1: V | null = this.getVertex(v1);
|
||||
const vertex2: V | null = this.getVertex(v2);
|
||||
const vertex1: UndirectedVertex<V> | null = this._getVertex(v1);
|
||||
const vertex2: UndirectedVertex<V> | null = this._getVertex(v2);
|
||||
|
||||
if (!vertex1 || !vertex2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const v1Edges = this._edges.get(vertex1);
|
||||
let removed: E | null = null;
|
||||
let removed: UndirectedEdge<E> | null = null;
|
||||
if (v1Edges) {
|
||||
removed = arrayRemove<E>(v1Edges, (e: UndirectedEdge) => e.vertices.includes(vertex2.id))[0] || null;
|
||||
removed = arrayRemove<UndirectedEdge<E>>(v1Edges, (e: UndirectedEdge<E>) => e.vertices.includes(vertex2.id))[0] || null;
|
||||
}
|
||||
const v2Edges = this._edges.get(vertex2);
|
||||
if (v2Edges) {
|
||||
arrayRemove<E>(v2Edges, (e: UndirectedEdge) => e.vertices.includes(vertex1.id));
|
||||
arrayRemove<UndirectedEdge<E>>(v2Edges, (e: UndirectedEdge<E>) => e.vertices.includes(vertex1.id));
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* The removeEdge function removes an edge between two vertices in a graph.
|
||||
* @param {E} edge - The parameter "edge" is of type E, which represents an edge in a graph.
|
||||
* @returns The method is returning either the removed edge (of type E) or null if the edge was not found.
|
||||
* The removeEdge function removes an edge between two vertices in an undirected graph.
|
||||
* @param edge - An object representing an undirected edge. It has a property called "vertices" which is an array
|
||||
* containing the two vertices connected by the edge.
|
||||
* @returns The method is returning an UndirectedEdge object or null.
|
||||
*/
|
||||
removeEdge(edge: E): E | null {
|
||||
removeEdge(edge: UndirectedEdge<E>): UndirectedEdge<E> | null {
|
||||
return this.removeEdgeBetween(edge.vertices[0], edge.vertices[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `degreeOf` returns the degree of a vertex in a graph, which is the number of edges connected to that
|
||||
* vertex.
|
||||
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a `V`.
|
||||
* @returns The function `degreeOf` returns the degree of a vertex in a graph. The degree of a vertex is the number of
|
||||
* edges that are incident to that vertex.
|
||||
* The function "degreeOf" returns the degree of a given vertex in an undirected graph.
|
||||
* @param {VertexId | UndirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or an
|
||||
* `UndirectedVertex<V>`.
|
||||
* @returns the degree of the vertex.
|
||||
*/
|
||||
degreeOf(vertexOrId: VertexId | V): number {
|
||||
const vertex = this.getVertex(vertexOrId);
|
||||
degreeOf(vertexOrId: VertexId | UndirectedVertex<V>): number {
|
||||
const vertex = this._getVertex(vertexOrId);
|
||||
if (vertex) {
|
||||
return this._edges.get(vertex)?.length || 0;
|
||||
} else {
|
||||
|
@ -161,14 +200,13 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
|
||||
/**
|
||||
* The function "edgesOf" returns an array of edges connected to a given vertex.
|
||||
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a `V`.
|
||||
* @returns an array of edges connected to the specified vertex. If the vertex exists in the graph, the function
|
||||
* returns the array of edges connected to that vertex. If the vertex does not exist in the graph, the function returns
|
||||
* an empty array.
|
||||
* The function "edgesOf" returns an array of undirected edges connected to a given vertex or vertex ID.
|
||||
* @param {VertexId | UndirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or an
|
||||
* `UndirectedVertex<V>`.
|
||||
* @returns an array of UndirectedEdge objects.
|
||||
*/
|
||||
edgesOf(vertexOrId: VertexId | V): E[] {
|
||||
const vertex = this.getVertex(vertexOrId);
|
||||
edgesOf(vertexOrId: VertexId | UndirectedVertex<V>): UndirectedEdge<E>[] {
|
||||
const vertex = this._getVertex(vertexOrId);
|
||||
if (vertex) {
|
||||
return this._edges.get(vertex) || [];
|
||||
} else {
|
||||
|
@ -177,11 +215,11 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
|
||||
/**
|
||||
* The function "edgeSet" returns an array of unique edges from a set of edges.
|
||||
* @returns The method `edgeSet()` returns an array of type `E[]`.
|
||||
* The function "edgeSet" returns an array of unique undirected edges from a set of edges.
|
||||
* @returns The method `edgeSet()` returns an array of `UndirectedEdge<E>` objects.
|
||||
*/
|
||||
edgeSet(): E[] {
|
||||
const edgeSet: Set<E> = new Set();
|
||||
edgeSet(): UndirectedEdge<E>[] {
|
||||
const edgeSet: Set<UndirectedEdge<E>> = new Set();
|
||||
this._edges.forEach(edges => {
|
||||
edges.forEach(edge => {
|
||||
edgeSet.add(edge);
|
||||
|
@ -191,13 +229,13 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
|
||||
/**
|
||||
* The function "getEdgesOf" returns an array of edges connected to a given vertex or vertex ID.
|
||||
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can accept either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`).
|
||||
* @returns an array of edges (E[]) that are connected to the specified vertex or vertex ID.
|
||||
* The function "getEdgesOf" returns an array of undirected edges connected to a given vertex or vertex ID.
|
||||
* @param {UndirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either an
|
||||
* `UndirectedVertex<V>` object or a `VertexId`.
|
||||
* @returns The function `getEdgesOf` returns an array of `UndirectedEdge<E>` objects.
|
||||
*/
|
||||
getEdgesOf(vertexOrId: V | VertexId): E[] {
|
||||
const vertex = this.getVertex(vertexOrId);
|
||||
getEdgesOf(vertexOrId: UndirectedVertex<V> | VertexId): UndirectedEdge<E>[] {
|
||||
const vertex = this._getVertex(vertexOrId);
|
||||
if (!vertex) {
|
||||
return [];
|
||||
}
|
||||
|
@ -205,18 +243,18 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
|
||||
/**
|
||||
* The function "getNeighbors" returns an array of neighboring vertices of a given vertex.
|
||||
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either a vertex object (`V`) or a vertex ID
|
||||
* (`VertexId`).
|
||||
* @returns an array of vertices (V[]).
|
||||
* The function `getNeighbors` returns an array of neighboring vertices of a given vertex in an undirected graph.
|
||||
* @param {UndirectedVertex<V> | VertexId} vertexOrId - The `vertexOrId` parameter can be either an
|
||||
* `UndirectedVertex<V>` object or a `VertexId`. It represents the vertex for which we want to find the neighbors.
|
||||
* @returns an array of UndirectedVertex objects.
|
||||
*/
|
||||
getNeighbors(vertexOrId: V | VertexId): V[] {
|
||||
const neighbors: V[] = [];
|
||||
const vertex = this.getVertex(vertexOrId);
|
||||
getNeighbors(vertexOrId: UndirectedVertex<V> | VertexId): UndirectedVertex<V>[] {
|
||||
const neighbors: UndirectedVertex<V>[] = [];
|
||||
const vertex = this._getVertex(vertexOrId);
|
||||
if (vertex) {
|
||||
const neighborEdges = this.getEdgesOf(vertex);
|
||||
for (const edge of neighborEdges) {
|
||||
const neighbor = this.getVertex(edge.vertices.filter(e => e !== vertex.id)[0]);
|
||||
const neighbor = this._getVertex(edge.vertices.filter(e => e !== vertex.id)[0]);
|
||||
if (neighbor) {
|
||||
neighbors.push(neighbor);
|
||||
}
|
||||
|
@ -226,19 +264,19 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
|
||||
/**
|
||||
* The function "getEndsOfEdge" returns the vertices at the ends of a given edge, or null if the edge does not exist in
|
||||
* the graph.
|
||||
* @param {E} edge - The parameter "edge" is of type E, which represents an edge in a graph.
|
||||
* @returns The function `getEndsOfEdge` returns an array containing two vertices `[V, V]` if the edge exists in the
|
||||
* graph and both vertices are found. If the edge does not exist or one or both vertices are not found, it returns
|
||||
* `null`.
|
||||
* The function "getEndsOfEdge" returns the two vertices that form the ends of a given undirected edge, or null if the
|
||||
* edge does not exist in the graph.
|
||||
* @param edge - An object representing an undirected edge in a graph. It has a property called "vertices" which is an
|
||||
* array containing two vertices that the edge connects.
|
||||
* @returns The function `getEndsOfEdge` returns an array containing the two ends of the given `edge` if the edge
|
||||
* exists in the graph. If the edge does not exist, it returns `null`.
|
||||
*/
|
||||
getEndsOfEdge(edge: E): [V, V] | null {
|
||||
getEndsOfEdge(edge: UndirectedEdge<E>): [UndirectedVertex<V>, UndirectedVertex<V>] | null {
|
||||
if (!this.hasEdge(edge.vertices[0], edge.vertices[1])) {
|
||||
return null;
|
||||
}
|
||||
const v1 = this.getVertex(edge.vertices[0]);
|
||||
const v2 = this.getVertex(edge.vertices[1]);
|
||||
const v1 = this._getVertex(edge.vertices[0]);
|
||||
const v2 = this._getVertex(edge.vertices[1]);
|
||||
if (v1 && v2) {
|
||||
return [v1, v2];
|
||||
} else {
|
||||
|
@ -246,7 +284,7 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
|
|||
}
|
||||
}
|
||||
|
||||
protected _setEdges(v: Map<V, E[]>) {
|
||||
protected _setEdges(v: Map<UndirectedVertex<V>, UndirectedEdge<E>[]>) {
|
||||
this._edges = v;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,43 @@
|
|||
import {VertexId} from '../types';
|
||||
import {AbstractEdge, AbstractVertex} from '../graph';
|
||||
|
||||
export interface IGraph<V, E> {
|
||||
|
||||
hasVertex(vertexOrId: V | VertexId): boolean;
|
||||
hasVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean;
|
||||
|
||||
getVertex(vertexOrId: VertexId | V): V | null;
|
||||
_getVertex(vertexOrId: VertexId | AbstractVertex<V>): AbstractVertex<V> | null;
|
||||
|
||||
getVertexId(vertexOrId: V | VertexId): VertexId;
|
||||
_getVertexId(vertexOrId: AbstractVertex<V> | VertexId): VertexId;
|
||||
|
||||
vertexSet(): Map<VertexId, V>;
|
||||
createAddVertex(id: VertexId, val?: V): boolean;
|
||||
|
||||
addVertex(v: V): boolean;
|
||||
addVertex(newVertex: AbstractVertex<V>): boolean;
|
||||
|
||||
removeVertex(vertexOrId: V | VertexId): boolean;
|
||||
removeVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean;
|
||||
|
||||
removeAllVertices(vertices: V[] | VertexId[]): boolean;
|
||||
removeAllVertices(vertices: AbstractVertex<V>[] | VertexId[]): boolean;
|
||||
|
||||
degreeOf(vertexOrId: V | VertexId): number;
|
||||
degreeOf(vertexOrId: AbstractVertex<V> | VertexId): number;
|
||||
|
||||
edgesOf(vertexOrId: V | VertexId): E[];
|
||||
edgesOf(vertexOrId: AbstractVertex<V> | VertexId): AbstractEdge<E>[];
|
||||
|
||||
hasEdge(src: V | VertexId, dest: V | VertexId): boolean;
|
||||
hasEdge(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId): boolean;
|
||||
|
||||
getEdge(srcOrId: V | VertexId, destOrId: V | VertexId): E | null;
|
||||
getEdge(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
|
||||
|
||||
edgeSet(): E[];
|
||||
edgeSet(): AbstractEdge<E>[];
|
||||
|
||||
addEdge(edge: E): boolean;
|
||||
createAddEdge(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId, weight: number, val: E): boolean;
|
||||
|
||||
removeEdgeBetween(srcOrId: V | VertexId, destOrId: V | VertexId): E | null;
|
||||
addEdge(edge: AbstractEdge<E>): boolean;
|
||||
|
||||
removeEdge(edge: E): E | null;
|
||||
removeEdgeBetween(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
|
||||
|
||||
setEdgeWeight(srcOrId: V | VertexId, destOrId: V | VertexId, weight: number): boolean;
|
||||
removeEdge(edge: AbstractEdge<E>): AbstractEdge<E> | null;
|
||||
|
||||
getMinPathBetween(v1: V | VertexId, v2: V | VertexId, isWeight?: boolean): V[] | null;
|
||||
setEdgeWeight(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId, weight: number): boolean;
|
||||
|
||||
getNeighbors(vertexOrId: V | VertexId): V[];
|
||||
getMinPathBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId, isWeight?: boolean): AbstractVertex<V>[] | null;
|
||||
|
||||
getNeighbors(vertexOrId: AbstractVertex<V> | VertexId): AbstractVertex<V>[];
|
||||
}
|
|
@ -1,15 +1,16 @@
|
|||
import {VertexId} from '../types';
|
||||
import {DirectedEdge, DirectedVertex} from '../graph';
|
||||
|
||||
export interface IDirectedGraph<V, E> {
|
||||
incomingEdgesOf(vertex: V): E[];
|
||||
incomingEdgesOf(vertex: DirectedVertex<V>): DirectedEdge<E>[];
|
||||
|
||||
outgoingEdgesOf(vertex: V): E[];
|
||||
outgoingEdgesOf(vertex: DirectedVertex<V>): DirectedEdge<E>[];
|
||||
|
||||
inDegreeOf(vertexOrId: V | VertexId): number;
|
||||
inDegreeOf(vertexOrId: DirectedVertex<V> | VertexId): number;
|
||||
|
||||
outDegreeOf(vertexOrId: V | VertexId): number;
|
||||
outDegreeOf(vertexOrId: DirectedVertex<V> | VertexId): number;
|
||||
|
||||
getEdgeSrc(e: E): V | null;
|
||||
getEdgeSrc(e: DirectedEdge<E>): DirectedVertex<V> | null;
|
||||
|
||||
getEdgeDest(e: E): V | null;
|
||||
getEdgeDest(e: DirectedEdge<E>): DirectedVertex<V> | null;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
export type VertexId = string | number;
|
||||
export type EdgeId = string;
|
||||
export type DijkstraResult<V> =
|
||||
{ distMap: Map<V, number>, preMap: Map<V, V | null>, seen: Set<V>, paths: V[][], minDist: number, minPath: V[] }
|
||||
| null;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {DirectedEdge, DirectedGraph, DirectedVertex, VertexId} from '../../../../src';
|
||||
|
||||
describe('DirectedGraph Operation Test', () => {
|
||||
let graph: DirectedGraph<DirectedVertex, DirectedEdge>;
|
||||
let graph: DirectedGraph<number, number>;
|
||||
|
||||
beforeEach(() => {
|
||||
graph = new DirectedGraph();
|
||||
|
@ -63,46 +63,61 @@ describe('DirectedGraph Operation Test', () => {
|
|||
});
|
||||
});
|
||||
|
||||
class MyVertex<V extends string> extends DirectedVertex<V> {
|
||||
|
||||
class MyVertex extends DirectedVertex {
|
||||
constructor(id: VertexId, data: string) {
|
||||
super(id);
|
||||
this._data = data;
|
||||
|
||||
constructor(id: VertexId, val?: V) {
|
||||
super(id, val);
|
||||
this._data = val;
|
||||
}
|
||||
|
||||
private _data: string;
|
||||
private _data: string | undefined;
|
||||
|
||||
get data(): string {
|
||||
get data(): string | undefined {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
set data(value: string) {
|
||||
set data(value: string | undefined) {
|
||||
this._data = value;
|
||||
}
|
||||
}
|
||||
|
||||
class MyEdge extends DirectedEdge {
|
||||
constructor(v1: VertexId, v2: VertexId, weight: number, data: string) {
|
||||
super(v1, v2, weight);
|
||||
this._data = data;
|
||||
class MyEdge<E extends string> extends DirectedEdge<E> {
|
||||
|
||||
constructor(v1: VertexId, v2: VertexId, weight: number, val?: E) {
|
||||
super(v1, v2, weight, val);
|
||||
this._data = val;
|
||||
}
|
||||
|
||||
private _data: string;
|
||||
private _data: string | undefined;
|
||||
|
||||
get data(): string {
|
||||
get data(): string | undefined {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
set data(value: string) {
|
||||
set data(value: string | undefined) {
|
||||
this._data = value;
|
||||
}
|
||||
}
|
||||
|
||||
class MyDirectedGraph<V extends string, E extends string> extends DirectedGraph<V, E> {
|
||||
_createVertex(id: VertexId, val?: V): MyVertex<V> {
|
||||
return new MyVertex<V>(id, val);
|
||||
}
|
||||
|
||||
describe('DirectedGraph Test2 operations', () => {
|
||||
const myGraph = new DirectedGraph<MyVertex, MyEdge>();
|
||||
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): MyEdge<E> {
|
||||
if (weight === undefined || weight === null) weight = 1;
|
||||
return new MyEdge<E>(src, dest, weight, val);
|
||||
}
|
||||
}
|
||||
|
||||
test('Add vertices', () => {
|
||||
describe('Inherit from DirectedGraph and perform operations', () => {
|
||||
let myGraph = new MyDirectedGraph();
|
||||
beforeEach(() => {
|
||||
myGraph = new MyDirectedGraph();
|
||||
});
|
||||
|
||||
it('Add vertices', () => {
|
||||
myGraph.addVertex(new MyVertex(1, 'data1'));
|
||||
myGraph.addVertex(new MyVertex(2, 'data2'));
|
||||
myGraph.addVertex(new MyVertex(3, 'data3'));
|
||||
|
@ -115,7 +130,7 @@ describe('DirectedGraph Test2 operations', () => {
|
|||
|
||||
});
|
||||
|
||||
test('Add edges', () => {
|
||||
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'));
|
||||
|
@ -127,25 +142,28 @@ describe('DirectedGraph Test2 operations', () => {
|
|||
expect(myGraph.getEdge(2, 1)).toBeInstanceOf(MyEdge);
|
||||
});
|
||||
|
||||
test('Get edge', () => {
|
||||
|
||||
it('Get edge', () => {
|
||||
myGraph.createAddVertex(1, 'val1');
|
||||
myGraph.createAddVertex(2, 'val1');
|
||||
myGraph.createAddEdge(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');
|
||||
|
||||
// edge1.data has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
|
||||
expect(edge1?.val).toBe('val1');
|
||||
expect(edge1).toBeInstanceOf(MyEdge);
|
||||
edge1 && expect(edge1.src).toBe(1);
|
||||
expect(edge1).toEqual(edge2);
|
||||
expect(edge3).toBeNull();
|
||||
});
|
||||
|
||||
test('Edge set and vertex set', () => {
|
||||
it('Edge set and vertex set', () => {
|
||||
const edges = myGraph.edgeSet();
|
||||
const vertices = myGraph.vertexSet();
|
||||
const vertices = myGraph.vertices;
|
||||
|
||||
});
|
||||
|
||||
test('Remove edge between vertices', () => {
|
||||
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'));
|
||||
|
@ -155,13 +173,13 @@ describe('DirectedGraph Test2 operations', () => {
|
|||
|
||||
expect(removedEdge).toBeInstanceOf(MyEdge);
|
||||
if (removedEdge) {
|
||||
removedEdge && expect(removedEdge.data).toBe('edge-data1-2');
|
||||
removedEdge && expect(removedEdge.val).toBe('edge-data1-2');
|
||||
removedEdge && expect(removedEdge.src).toBe(1)
|
||||
}
|
||||
expect(edgeAfterRemoval).toBeNull();
|
||||
});
|
||||
|
||||
test('Topological sort', () => {
|
||||
it('Topological sort', () => {
|
||||
|
||||
const sorted = myGraph.topologicalSort();
|
||||
|
||||
|
@ -175,7 +193,7 @@ describe('DirectedGraph Test2 operations', () => {
|
|||
|
||||
});
|
||||
|
||||
test('Minimum path between vertices', () => {
|
||||
it('Minimum path between vertices', () => {
|
||||
myGraph.addVertex(new MyVertex(1, 'data1'));
|
||||
myGraph.addVertex(new MyVertex(2, 'data2'));
|
||||
myGraph.addEdge(new MyEdge(1, 2, 10, 'edge-data1-2'));
|
||||
|
@ -183,7 +201,7 @@ describe('DirectedGraph Test2 operations', () => {
|
|||
const minPath = myGraph.getMinPathBetween(1, 2);
|
||||
});
|
||||
|
||||
test('All paths between vertices', () => {
|
||||
it('All paths between vertices', () => {
|
||||
// Add vertices and edges as needed for this test
|
||||
myGraph.addVertex(new MyVertex(1, 'data1'));
|
||||
myGraph.addVertex(new MyVertex(2, 'data2'));
|
||||
|
@ -195,9 +213,8 @@ describe('DirectedGraph Test2 operations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
describe('DirectedGraph Test3', () => {
|
||||
const myGraph = new DirectedGraph<MyVertex, MyEdge>();
|
||||
describe('Inherit from DirectedGraph and perform operations test2.', () => {
|
||||
const myGraph = new MyDirectedGraph<string, string>();
|
||||
|
||||
it('should test graph operations', () => {
|
||||
const vertex1 = new MyVertex(1, 'data1');
|
||||
|
|
Loading…
Reference in a new issue