@thi.ng/adjacency
Version:
Sparse & bitwise adjacency matrices, lists and selected traversal algorithms for directed & undirected graphs
131 lines (130 loc) • 3.1 kB
JavaScript
import { __into, __invert, __toDot } from "./utils.js";
class AdjacencyList {
adjacency = [];
indegree = [];
numE = 0;
numV = 0;
constructor(edges) {
edges && __into(this, edges);
}
numEdges() {
return this.numE;
}
numVertices() {
return this.numV;
}
*vertices() {
const { adjacency } = this;
for (let i = 0, n = adjacency.length; i < n; i++) {
if (adjacency[i]) yield i;
}
}
*edges() {
const { adjacency } = this;
for (let i = 0, n = adjacency.length; i < n; i++) {
const vertex = adjacency[i];
if (!vertex) continue;
for (let j of vertex) yield [i, j];
}
}
addVertex(id) {
this.ensureVertexData(id);
}
removeVertex(id) {
const { adjacency, indegree } = this;
const vertex = adjacency[id];
if (!vertex) return false;
while (vertex.length) {
indegree[vertex.pop()]--;
this.numE--;
}
delete adjacency[id];
for (let i = 0, n = adjacency.length; i < n && indegree[id] > 0; i++) {
const vertex2 = adjacency[i];
if (!vertex2) continue;
while (vertex2.includes(id)) this.removeEdge(i, id);
}
this.numV--;
return true;
}
hasVertex(id) {
return !!this.adjacency[id];
}
addEdge(from, to) {
const vertex = this.ensureVertexData(from);
this.ensureVertexData(to);
vertex.push(to);
this.indegree[to]++;
this.numE++;
return true;
}
removeEdge(from, to) {
const vertex = this.adjacency[from];
if (vertex) {
const dest = vertex.indexOf(to);
if (dest >= 0) {
vertex.splice(dest, 1);
this.numE--;
this.indegree[to]--;
return true;
}
}
return false;
}
hasEdge(from, to) {
const vertex = this.adjacency[from];
return vertex ? vertex.includes(to) : false;
}
degree(id, type = "out") {
let degree = 0;
const vertex = this.adjacency[id];
if (vertex) {
if (type !== "in") degree += vertex.length;
if (type !== "out") degree += this.indegree[id];
}
return degree;
}
neighbors(id) {
return [...this.adjacency[id] || []];
}
invert() {
return __invert(new AdjacencyList(), this.edges());
}
toDot(ids) {
return __toDot(this.edges(), false, ids);
}
toString() {
const { adjacency } = this;
const res = [];
for (let i = 0, n = adjacency.length; i < n; i++) {
if (adjacency[i]) {
res.push(
`${i}: [${[...adjacency[i]].sort((a, b) => a - b).join(", ")}]`
);
}
}
return res.join("\n");
}
ensureVertexData(id) {
const vertex = this.adjacency[id];
if (vertex) return vertex;
this.numV++;
this.indegree[id] = 0;
return this.adjacency[id] = [];
}
}
const defAdjList = (edges) => new AdjacencyList(edges);
const adjListFromAdjacency = (src) => {
const res = new AdjacencyList();
for (let i = 0, n = src.length; i < n; i++) {
const v = src[i];
if (!v) continue;
for (let w of v) res.addEdge(i, w);
}
return res;
};
export {
AdjacencyList,
adjListFromAdjacency,
defAdjList
};