trie-typed
Version:
Trie, prefix tree
425 lines (424 loc) • 14.1 kB
JavaScript
"use strict";
/**
* data-structure-typed
*
* @author Pablo Zeng
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
* @license MIT License
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UndirectedGraph = exports.UndirectedEdge = exports.UndirectedVertex = void 0;
const abstract_graph_1 = require("./abstract-graph");
const utils_1 = require("../../utils");
class UndirectedVertex extends abstract_graph_1.AbstractVertex {
constructor(key, value) {
super(key, value);
}
}
exports.UndirectedVertex = UndirectedVertex;
class UndirectedEdge extends abstract_graph_1.AbstractEdge {
constructor(v1, v2, weight, value) {
super(weight, value);
this.endpoints = [v1, v2];
}
}
exports.UndirectedEdge = UndirectedEdge;
/**
* Undirected graph implementation.
* @template V - Vertex value type.
* @template E - Edge value type.
* @template VO - Concrete vertex class (extends AbstractVertex<V>).
* @template EO - Concrete edge class (extends AbstractEdge<E>).
* @remarks Time O(1), Space O(1)
* @example examples will be generated by unit test
*/
class UndirectedGraph extends abstract_graph_1.AbstractGraph {
/**
* Construct an undirected graph with runtime defaults.
* @param options - `GraphOptions<V>` (e.g. `vertexValueInitializer`, `defaultEdgeWeight`).
* @remarks Time O(1), Space O(1)
*/
constructor(options) {
super(options);
this._edgeMap = new Map();
}
get edgeMap() {
return this._edgeMap;
}
set edgeMap(v) {
this._edgeMap = v;
}
/**
* Construct an undirected graph from keys with value initializer `v => v`.
* @template K - Vertex key type.
* @param keys - Iterable of vertex keys.
* @returns UndirectedGraph with all keys added.
* @remarks Time O(V), Space O(V)
*/
static fromKeys(keys) {
const g = new UndirectedGraph({
vertexValueInitializer: (k) => k
});
for (const k of keys)
g.addVertex(k);
return g;
}
/**
* Construct an undirected graph from `[key, value]` entries.
* @template V - Vertex value type.
* @param entries - Iterable of `[key, value]` pairs.
* @returns UndirectedGraph with all vertices added.
* @remarks Time O(V), Space O(V)
*/
static fromEntries(entries) {
const g = new UndirectedGraph();
for (const [k, v] of entries)
g.addVertex(k, v);
return g;
}
/**
* Create an undirected vertex instance. Does not insert into the graph.
* @param key - Vertex identifier.
* @param value - Optional payload.
* @returns Concrete vertex instance.
* @remarks Time O(1), Space O(1)
*/
createVertex(key, value) {
return new UndirectedVertex(key, value);
}
/**
* Create an undirected edge instance. Does not insert into the graph.
* @param v1 - One endpoint key.
* @param v2 - The other endpoint key.
* @param weight - Edge weight; defaults to `defaultEdgeWeight`.
* @param value - Edge payload.
* @returns Concrete edge instance.
* @remarks Time O(1), Space O(1)
*/
createEdge(v1, v2, weight, value) {
var _a;
return new UndirectedEdge(v1, v2, (_a = weight !== null && weight !== void 0 ? weight : this.options.defaultEdgeWeight) !== null && _a !== void 0 ? _a : 1, value);
}
/**
* Get an undirected edge between two vertices, if present.
* @param v1 - One vertex or key.
* @param v2 - The other vertex or key.
* @returns Edge instance or `undefined`.
* @remarks Time O(1) avg, Space O(1)
*/
getEdge(v1, v2) {
var _a;
let edgeMap = [];
if (v1 !== undefined && v2 !== undefined) {
const vertex1 = this._getVertex(v1);
const vertex2 = this._getVertex(v2);
if (vertex1 && vertex2) {
edgeMap = (_a = this._edgeMap.get(vertex1)) === null || _a === void 0 ? void 0 : _a.filter(e => e.endpoints.includes(vertex2.key));
}
}
return edgeMap ? edgeMap[0] || undefined : undefined;
}
/**
* Delete a single undirected edge between two vertices.
* @param v1 - One vertex or key.
* @param v2 - The other vertex or key.
* @returns Removed edge or `undefined`.
* @remarks Time O(1) avg, Space O(1)
*/
deleteEdgeBetween(v1, v2) {
const vertex1 = this._getVertex(v1);
const vertex2 = this._getVertex(v2);
if (!vertex1 || !vertex2) {
return undefined;
}
const v1Edges = this._edgeMap.get(vertex1);
let removed = undefined;
if (v1Edges) {
removed = (0, utils_1.arrayRemove)(v1Edges, (e) => e.endpoints.includes(vertex2.key))[0] || undefined;
}
const v2Edges = this._edgeMap.get(vertex2);
if (v2Edges) {
(0, utils_1.arrayRemove)(v2Edges, (e) => e.endpoints.includes(vertex1.key));
}
return removed;
}
/**
* Delete an edge by instance or by a pair of keys.
* @param edgeOrOneSideVertexKey - Edge instance or one endpoint vertex/key.
* @param otherSideVertexKey - Required second endpoint when deleting by pair.
* @returns Removed edge or `undefined`.
* @remarks Time O(1) avg, Space O(1)
*/
deleteEdge(edgeOrOneSideVertexKey, otherSideVertexKey) {
let oneSide, otherSide;
if (this.isVertexKey(edgeOrOneSideVertexKey)) {
if (this.isVertexKey(otherSideVertexKey)) {
oneSide = this._getVertex(edgeOrOneSideVertexKey);
otherSide = this._getVertex(otherSideVertexKey);
}
else {
return;
}
}
else {
oneSide = this._getVertex(edgeOrOneSideVertexKey.endpoints[0]);
otherSide = this._getVertex(edgeOrOneSideVertexKey.endpoints[1]);
}
if (oneSide && otherSide) {
return this.deleteEdgeBetween(oneSide, otherSide);
}
else {
return;
}
}
/**
* Delete a vertex and remove it from all neighbor lists.
* @param vertexOrKey - Vertex or key.
* @returns `true` if removed; otherwise `false`.
* @remarks Time O(deg), Space O(1)
*/
deleteVertex(vertexOrKey) {
let vertexKey;
let vertex;
if (this.isVertexKey(vertexOrKey)) {
vertex = this.getVertex(vertexOrKey);
vertexKey = vertexOrKey;
}
else {
vertex = vertexOrKey;
vertexKey = this._getVertexKey(vertexOrKey);
}
/**
* All neighbors connected via undirected edges.
* @param vertexOrKey - Vertex or key.
* @returns Array of neighbor vertices.
* @remarks Time O(deg), Space O(deg)
*/
const neighbors = this.getNeighbors(vertexOrKey);
if (vertex) {
neighbors.forEach(neighbor => {
const neighborEdges = this._edgeMap.get(neighbor);
if (neighborEdges) {
const restEdges = neighborEdges.filter(edge => {
return !edge.endpoints.includes(vertexKey);
});
this._edgeMap.set(neighbor, restEdges);
}
});
this._edgeMap.delete(vertex);
}
return this._vertexMap.delete(vertexKey);
}
/**
* Degree of a vertex (# of incident undirected edges).
* @param vertexOrKey - Vertex or key.
* @returns Non-negative integer.
* @remarks Time O(1) avg, Space O(1)
*/
degreeOf(vertexOrKey) {
var _a;
const vertex = this._getVertex(vertexOrKey);
if (vertex) {
return ((_a = this._edgeMap.get(vertex)) === null || _a === void 0 ? void 0 : _a.length) || 0;
}
else {
return 0;
}
}
/**
* Incident undirected edges of a vertex.
* @param vertexOrKey - Vertex or key.
* @returns Array of incident edges.
* @remarks Time O(deg), Space O(deg)
*/
edgesOf(vertexOrKey) {
const vertex = this._getVertex(vertexOrKey);
if (vertex) {
return this._edgeMap.get(vertex) || [];
}
else {
return [];
}
}
/**
* Unique set of undirected edges across endpoints.
* @returns Array of edges.
* @remarks Time O(E), Space O(E)
*/
edgeSet() {
const edgeSet = new Set();
this._edgeMap.forEach(edgeMap => {
edgeMap.forEach(edge => {
edgeSet.add(edge);
});
});
return [...edgeSet];
}
getNeighbors(vertexOrKey) {
const neighbors = [];
const vertex = this._getVertex(vertexOrKey);
if (vertex) {
const neighborEdges = this.edgesOf(vertex);
for (const edge of neighborEdges) {
const neighbor = this._getVertex(edge.endpoints.filter(e => e !== vertex.key)[0]);
if (neighbor) {
neighbors.push(neighbor);
}
}
}
return neighbors;
}
/**
* Resolve an edge's two endpoints to vertex instances.
* @param edge - Edge instance.
* @returns `[v1, v2]` or `undefined` if either endpoint is missing.
* @remarks Time O(1), Space O(1)
*/
getEndsOfEdge(edge) {
if (!this.hasEdge(edge.endpoints[0], edge.endpoints[1])) {
return undefined;
}
const v1 = this._getVertex(edge.endpoints[0]);
const v2 = this._getVertex(edge.endpoints[1]);
if (v1 && v2) {
return [v1, v2];
}
else {
return undefined;
}
}
/**
* Whether the graph has no vertices and no edges.
* @remarks Time O(1), Space O(1)
*/
isEmpty() {
return this.vertexMap.size === 0 && this.edgeMap.size === 0;
}
/**
* Remove all vertices and edges.
* @remarks Time O(V + E), Space O(1)
*/
clear() {
this._vertexMap = new Map();
this._edgeMap = new Map();
}
/**
* Deep clone as the same concrete class.
* @returns A new graph of the same concrete class (`this` type).
* @remarks Time O(V + E), Space O(V + E)
*/
clone() {
return super.clone();
}
/**
* Tarjan-based bridge and articulation point detection.
* @returns `{ dfnMap, lowMap, bridges, cutVertices }`.
* @remarks Time O(V + E), Space O(V + E)
*/
tarjan() {
const dfnMap = new Map();
const lowMap = new Map();
const bridges = [];
const cutVertices = [];
let time = 0;
const dfs = (vertex, parent) => {
dfnMap.set(vertex, time);
lowMap.set(vertex, time);
time++;
const neighbors = this.getNeighbors(vertex);
let childCount = 0;
for (const neighbor of neighbors) {
if (!dfnMap.has(neighbor)) {
childCount++;
dfs(neighbor, vertex);
lowMap.set(vertex, Math.min(lowMap.get(vertex), lowMap.get(neighbor)));
if (lowMap.get(neighbor) > dfnMap.get(vertex)) {
// Found a bridge
const edge = this.getEdge(vertex, neighbor);
if (edge) {
bridges.push(edge);
}
}
if (parent !== undefined && lowMap.get(neighbor) >= dfnMap.get(vertex)) {
// Found an articulation point
cutVertices.push(vertex);
}
}
else if (neighbor !== parent) {
lowMap.set(vertex, Math.min(lowMap.get(vertex), dfnMap.get(neighbor)));
}
}
if (parent === undefined && childCount > 1) {
// Special case for root in DFS tree
cutVertices.push(vertex);
}
};
for (const vertex of this.vertexMap.values()) {
if (!dfnMap.has(vertex)) {
dfs(vertex, undefined);
}
}
return {
dfnMap,
lowMap,
bridges,
cutVertices
};
}
/**
* Get bridges discovered by `tarjan()`.
* @returns Array of edges that are bridges.
* @remarks Time O(B), Space O(1)
*/
getBridges() {
return this.tarjan().bridges;
}
/**
* Get articulation points discovered by `tarjan()`.
* @returns Array of cut vertices.
* @remarks Time O(C), Space O(1)
*/
getCutVertices() {
return this.tarjan().cutVertices;
}
/**
* DFN index map computed by `tarjan()`.
* @returns Map from vertex to DFN index.
* @remarks Time O(V), Space O(V)
*/
getDFNMap() {
return this.tarjan().dfnMap;
}
/**
* LOW link map computed by `tarjan()`.
* @returns Map from vertex to LOW value.
* @remarks Time O(V), Space O(V)
*/
getLowMap() {
return this.tarjan().lowMap;
}
/**
* Internal hook to attach an undirected edge into adjacency maps.
* @param edge - Edge instance.
* @returns `true` if both endpoints exist; otherwise `false`.
* @remarks Time O(1) avg, Space O(1)
*/
_addEdge(edge) {
for (const end of edge.endpoints) {
const endVertex = this._getVertex(end);
if (endVertex === undefined)
return false;
if (endVertex) {
const edgeMap = this._edgeMap.get(endVertex);
if (edgeMap) {
edgeMap.push(edge);
}
else {
this._edgeMap.set(endVertex, [edge]);
}
}
}
return true;
}
}
exports.UndirectedGraph = UndirectedGraph;