mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-09-20 07:43:04 +08:00
Add Eulerian Path.
This commit is contained in:
parent
808a1e713f
commit
e5a0b4ba0d
@ -74,7 +74,7 @@
|
|||||||
* [Topological Sorting](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/topological-sorting) - DFS method
|
* [Topological Sorting](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/topological-sorting) - DFS method
|
||||||
* [Articulation Points](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/articulation-points) - Tarjan's algorithm (DFS based)
|
* [Articulation Points](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/articulation-points) - Tarjan's algorithm (DFS based)
|
||||||
* [Bridges](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bridges) - DFS based algorithm
|
* [Bridges](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bridges) - DFS based algorithm
|
||||||
* [Eulerian Path and Eulerian Circuit](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/eulerian-path)
|
* [Eulerian Path and Eulerian Circuit](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/eulerian-path) - Fleury's algorithm
|
||||||
* Strongly Connected Component algorithm
|
* Strongly Connected Component algorithm
|
||||||
* Shortest Path Faster Algorithm (SPFA)
|
* Shortest Path Faster Algorithm (SPFA)
|
||||||
* **Uncategorized**
|
* **Uncategorized**
|
||||||
|
@ -21,7 +21,7 @@ describe('articulationPoints', () => {
|
|||||||
.addEdge(edgeBC)
|
.addEdge(edgeBC)
|
||||||
.addEdge(edgeCD);
|
.addEdge(edgeCD);
|
||||||
|
|
||||||
const articulationPointsSet = articulationPoints(graph);
|
const articulationPointsSet = Object.values(articulationPoints(graph));
|
||||||
|
|
||||||
expect(articulationPointsSet.length).toBe(2);
|
expect(articulationPointsSet.length).toBe(2);
|
||||||
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
||||||
@ -47,7 +47,7 @@ describe('articulationPoints', () => {
|
|||||||
.addEdge(edgeBC)
|
.addEdge(edgeBC)
|
||||||
.addEdge(edgeCD);
|
.addEdge(edgeCD);
|
||||||
|
|
||||||
const articulationPointsSet = articulationPoints(graph);
|
const articulationPointsSet = Object.values(articulationPoints(graph));
|
||||||
|
|
||||||
expect(articulationPointsSet.length).toBe(1);
|
expect(articulationPointsSet.length).toBe(1);
|
||||||
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
||||||
@ -75,7 +75,7 @@ describe('articulationPoints', () => {
|
|||||||
.addEdge(edgeBC)
|
.addEdge(edgeBC)
|
||||||
.addEdge(edgeCD);
|
.addEdge(edgeCD);
|
||||||
|
|
||||||
const articulationPointsSet = articulationPoints(graph);
|
const articulationPointsSet = Object.values(articulationPoints(graph));
|
||||||
|
|
||||||
expect(articulationPointsSet.length).toBe(1);
|
expect(articulationPointsSet.length).toBe(1);
|
||||||
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
||||||
@ -114,7 +114,7 @@ describe('articulationPoints', () => {
|
|||||||
.addEdge(edgeGF)
|
.addEdge(edgeGF)
|
||||||
.addEdge(edgeFH);
|
.addEdge(edgeFH);
|
||||||
|
|
||||||
const articulationPointsSet = articulationPoints(graph);
|
const articulationPointsSet = Object.values(articulationPoints(graph));
|
||||||
|
|
||||||
expect(articulationPointsSet.length).toBe(4);
|
expect(articulationPointsSet.length).toBe(4);
|
||||||
expect(articulationPointsSet[0].getKey()).toBe(vertexF.getKey());
|
expect(articulationPointsSet[0].getKey()).toBe(vertexF.getKey());
|
||||||
@ -156,7 +156,7 @@ describe('articulationPoints', () => {
|
|||||||
.addEdge(edgeGF)
|
.addEdge(edgeGF)
|
||||||
.addEdge(edgeFH);
|
.addEdge(edgeFH);
|
||||||
|
|
||||||
const articulationPointsSet = articulationPoints(graph);
|
const articulationPointsSet = Object.values(articulationPoints(graph));
|
||||||
|
|
||||||
expect(articulationPointsSet.length).toBe(4);
|
expect(articulationPointsSet.length).toBe(4);
|
||||||
expect(articulationPointsSet[0].getKey()).toBe(vertexF.getKey());
|
expect(articulationPointsSet[0].getKey()).toBe(vertexF.getKey());
|
||||||
@ -187,7 +187,7 @@ describe('articulationPoints', () => {
|
|||||||
.addEdge(edgeCD)
|
.addEdge(edgeCD)
|
||||||
.addEdge(edgeDE);
|
.addEdge(edgeDE);
|
||||||
|
|
||||||
const articulationPointsSet = articulationPoints(graph);
|
const articulationPointsSet = Object.values(articulationPoints(graph));
|
||||||
|
|
||||||
expect(articulationPointsSet.length).toBe(2);
|
expect(articulationPointsSet.length).toBe(2);
|
||||||
expect(articulationPointsSet[0].getKey()).toBe(vertexD.getKey());
|
expect(articulationPointsSet[0].getKey()).toBe(vertexD.getKey());
|
||||||
@ -224,7 +224,7 @@ describe('articulationPoints', () => {
|
|||||||
.addEdge(edgeEG)
|
.addEdge(edgeEG)
|
||||||
.addEdge(edgeFG);
|
.addEdge(edgeFG);
|
||||||
|
|
||||||
const articulationPointsSet = articulationPoints(graph);
|
const articulationPointsSet = Object.values(articulationPoints(graph));
|
||||||
|
|
||||||
expect(articulationPointsSet.length).toBe(1);
|
expect(articulationPointsSet.length).toBe(1);
|
||||||
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
expect(articulationPointsSet[0].getKey()).toBe(vertexC.getKey());
|
||||||
|
@ -17,7 +17,7 @@ class VisitMetadata {
|
|||||||
* Tarjan's algorithm for finding articulation points in graph.
|
* Tarjan's algorithm for finding articulation points in graph.
|
||||||
*
|
*
|
||||||
* @param {Graph} graph
|
* @param {Graph} graph
|
||||||
* @return {GraphVertex[]}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
export default function articulationPoints(graph) {
|
export default function articulationPoints(graph) {
|
||||||
// Set of vertices we've already visited during DFS.
|
// Set of vertices we've already visited during DFS.
|
||||||
@ -109,5 +109,5 @@ export default function articulationPoints(graph) {
|
|||||||
// Do Depth First Search traversal over submitted graph.
|
// Do Depth First Search traversal over submitted graph.
|
||||||
depthFirstSearch(graph, startVertex, dfsCallbacks);
|
depthFirstSearch(graph, startVertex, dfsCallbacks);
|
||||||
|
|
||||||
return Object.values(articulationPointsSet);
|
return articulationPointsSet;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ describe('graphBridges', () => {
|
|||||||
.addEdge(edgeBC)
|
.addEdge(edgeBC)
|
||||||
.addEdge(edgeCD);
|
.addEdge(edgeCD);
|
||||||
|
|
||||||
const bridges = graphBridges(graph);
|
const bridges = Object.values(graphBridges(graph));
|
||||||
|
|
||||||
expect(bridges.length).toBe(3);
|
expect(bridges.length).toBe(3);
|
||||||
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
|
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
|
||||||
@ -48,7 +48,7 @@ describe('graphBridges', () => {
|
|||||||
.addEdge(edgeBC)
|
.addEdge(edgeBC)
|
||||||
.addEdge(edgeCD);
|
.addEdge(edgeCD);
|
||||||
|
|
||||||
const bridges = graphBridges(graph);
|
const bridges = Object.values(graphBridges(graph));
|
||||||
|
|
||||||
expect(bridges.length).toBe(1);
|
expect(bridges.length).toBe(1);
|
||||||
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
|
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
|
||||||
@ -87,7 +87,7 @@ describe('graphBridges', () => {
|
|||||||
.addEdge(edgeGF)
|
.addEdge(edgeGF)
|
||||||
.addEdge(edgeFH);
|
.addEdge(edgeFH);
|
||||||
|
|
||||||
const bridges = graphBridges(graph);
|
const bridges = Object.values(graphBridges(graph));
|
||||||
|
|
||||||
expect(bridges.length).toBe(3);
|
expect(bridges.length).toBe(3);
|
||||||
expect(bridges[0].getKey()).toBe(edgeFH.getKey());
|
expect(bridges[0].getKey()).toBe(edgeFH.getKey());
|
||||||
@ -128,7 +128,7 @@ describe('graphBridges', () => {
|
|||||||
.addEdge(edgeGF)
|
.addEdge(edgeGF)
|
||||||
.addEdge(edgeFH);
|
.addEdge(edgeFH);
|
||||||
|
|
||||||
const bridges = graphBridges(graph);
|
const bridges = Object.values(graphBridges(graph));
|
||||||
|
|
||||||
expect(bridges.length).toBe(3);
|
expect(bridges.length).toBe(3);
|
||||||
expect(bridges[0].getKey()).toBe(edgeFH.getKey());
|
expect(bridges[0].getKey()).toBe(edgeFH.getKey());
|
||||||
@ -158,7 +158,7 @@ describe('graphBridges', () => {
|
|||||||
.addEdge(edgeCD)
|
.addEdge(edgeCD)
|
||||||
.addEdge(edgeDE);
|
.addEdge(edgeDE);
|
||||||
|
|
||||||
const bridges = graphBridges(graph);
|
const bridges = Object.values(graphBridges(graph));
|
||||||
|
|
||||||
expect(bridges.length).toBe(2);
|
expect(bridges.length).toBe(2);
|
||||||
expect(bridges[0].getKey()).toBe(edgeDE.getKey());
|
expect(bridges[0].getKey()).toBe(edgeDE.getKey());
|
||||||
@ -195,7 +195,7 @@ describe('graphBridges', () => {
|
|||||||
.addEdge(edgeEG)
|
.addEdge(edgeEG)
|
||||||
.addEdge(edgeFG);
|
.addEdge(edgeFG);
|
||||||
|
|
||||||
const bridges = graphBridges(graph);
|
const bridges = Object.values(graphBridges(graph));
|
||||||
|
|
||||||
expect(bridges.length).toBe(1);
|
expect(bridges.length).toBe(1);
|
||||||
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
|
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
|
||||||
|
@ -12,7 +12,7 @@ class VisitMetadata {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Graph} graph
|
* @param {Graph} graph
|
||||||
* @return {GraphVertex[]}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
export default function graphBridges(graph) {
|
export default function graphBridges(graph) {
|
||||||
// Set of vertices we've already visited during DFS.
|
// Set of vertices we've already visited during DFS.
|
||||||
@ -91,5 +91,5 @@ export default function graphBridges(graph) {
|
|||||||
// Do Depth First Search traversal over submitted graph.
|
// Do Depth First Search traversal over submitted graph.
|
||||||
depthFirstSearch(graph, startVertex, dfsCallbacks);
|
depthFirstSearch(graph, startVertex, dfsCallbacks);
|
||||||
|
|
||||||
return Object.values(bridges);
|
return bridges;
|
||||||
}
|
}
|
||||||
|
139
src/algorithms/graph/eulerian-path/__test__/eulerianPath.test.js
Normal file
139
src/algorithms/graph/eulerian-path/__test__/eulerianPath.test.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import GraphVertex from '../../../../data-structures/graph/GraphVertex';
|
||||||
|
import GraphEdge from '../../../../data-structures/graph/GraphEdge';
|
||||||
|
import Graph from '../../../../data-structures/graph/Graph';
|
||||||
|
import eulerianPath from '../eulerianPath';
|
||||||
|
|
||||||
|
describe('eulerianPath', () => {
|
||||||
|
it('should throw an error when graph is not Eulerian', () => {
|
||||||
|
function findEulerianPathInNotEulerianGraph() {
|
||||||
|
const vertexA = new GraphVertex('A');
|
||||||
|
const vertexB = new GraphVertex('B');
|
||||||
|
const vertexC = new GraphVertex('C');
|
||||||
|
const vertexD = new GraphVertex('D');
|
||||||
|
const vertexE = new GraphVertex('E');
|
||||||
|
|
||||||
|
const edgeAB = new GraphEdge(vertexA, vertexB);
|
||||||
|
const edgeAC = new GraphEdge(vertexA, vertexC);
|
||||||
|
const edgeBC = new GraphEdge(vertexB, vertexC);
|
||||||
|
const edgeBD = new GraphEdge(vertexB, vertexD);
|
||||||
|
const edgeCE = new GraphEdge(vertexC, vertexE);
|
||||||
|
|
||||||
|
const graph = new Graph();
|
||||||
|
|
||||||
|
graph
|
||||||
|
.addEdge(edgeAB)
|
||||||
|
.addEdge(edgeAC)
|
||||||
|
.addEdge(edgeBC)
|
||||||
|
.addEdge(edgeBD)
|
||||||
|
.addEdge(edgeCE);
|
||||||
|
|
||||||
|
eulerianPath(graph);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(findEulerianPathInNotEulerianGraph).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find Eulerian Circuit in graph', () => {
|
||||||
|
const vertexA = new GraphVertex('A');
|
||||||
|
const vertexB = new GraphVertex('B');
|
||||||
|
const vertexC = new GraphVertex('C');
|
||||||
|
const vertexD = new GraphVertex('D');
|
||||||
|
const vertexE = new GraphVertex('E');
|
||||||
|
const vertexF = new GraphVertex('F');
|
||||||
|
const vertexG = new GraphVertex('G');
|
||||||
|
|
||||||
|
const edgeAB = new GraphEdge(vertexA, vertexB);
|
||||||
|
const edgeAE = new GraphEdge(vertexA, vertexE);
|
||||||
|
const edgeAF = new GraphEdge(vertexA, vertexF);
|
||||||
|
const edgeAG = new GraphEdge(vertexA, vertexG);
|
||||||
|
const edgeGF = new GraphEdge(vertexG, vertexF);
|
||||||
|
const edgeBE = new GraphEdge(vertexB, vertexE);
|
||||||
|
const edgeEB = new GraphEdge(vertexE, vertexB);
|
||||||
|
const edgeBC = new GraphEdge(vertexB, vertexC);
|
||||||
|
const edgeED = new GraphEdge(vertexE, vertexD);
|
||||||
|
const edgeCD = new GraphEdge(vertexC, vertexD);
|
||||||
|
|
||||||
|
const graph = new Graph();
|
||||||
|
|
||||||
|
graph
|
||||||
|
.addEdge(edgeAB)
|
||||||
|
.addEdge(edgeAE)
|
||||||
|
.addEdge(edgeAF)
|
||||||
|
.addEdge(edgeAG)
|
||||||
|
.addEdge(edgeGF)
|
||||||
|
.addEdge(edgeBE)
|
||||||
|
.addEdge(edgeEB)
|
||||||
|
.addEdge(edgeBC)
|
||||||
|
.addEdge(edgeED)
|
||||||
|
.addEdge(edgeCD);
|
||||||
|
|
||||||
|
const graphEdgesCount = graph.getAllEdges().length;
|
||||||
|
|
||||||
|
const eulerianPathSet = eulerianPath(graph);
|
||||||
|
|
||||||
|
expect(eulerianPathSet.length).toBe(graphEdgesCount + 1);
|
||||||
|
|
||||||
|
expect(eulerianPathSet[0].getKey()).toBe(vertexA.getKey());
|
||||||
|
expect(eulerianPathSet[1].getKey()).toBe(vertexB.getKey());
|
||||||
|
expect(eulerianPathSet[2].getKey()).toBe(vertexE.getKey());
|
||||||
|
expect(eulerianPathSet[3].getKey()).toBe(vertexB.getKey());
|
||||||
|
expect(eulerianPathSet[4].getKey()).toBe(vertexC.getKey());
|
||||||
|
expect(eulerianPathSet[5].getKey()).toBe(vertexD.getKey());
|
||||||
|
expect(eulerianPathSet[6].getKey()).toBe(vertexE.getKey());
|
||||||
|
expect(eulerianPathSet[7].getKey()).toBe(vertexA.getKey());
|
||||||
|
expect(eulerianPathSet[8].getKey()).toBe(vertexF.getKey());
|
||||||
|
expect(eulerianPathSet[9].getKey()).toBe(vertexG.getKey());
|
||||||
|
expect(eulerianPathSet[10].getKey()).toBe(vertexA.getKey());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find Eulerian Path in graph', () => {
|
||||||
|
const vertexA = new GraphVertex('A');
|
||||||
|
const vertexB = new GraphVertex('B');
|
||||||
|
const vertexC = new GraphVertex('C');
|
||||||
|
const vertexD = new GraphVertex('D');
|
||||||
|
const vertexE = new GraphVertex('E');
|
||||||
|
const vertexF = new GraphVertex('F');
|
||||||
|
const vertexG = new GraphVertex('G');
|
||||||
|
const vertexH = new GraphVertex('H');
|
||||||
|
|
||||||
|
const edgeAB = new GraphEdge(vertexA, vertexB);
|
||||||
|
const edgeAC = new GraphEdge(vertexA, vertexC);
|
||||||
|
const edgeBD = new GraphEdge(vertexB, vertexD);
|
||||||
|
const edgeDC = new GraphEdge(vertexD, vertexC);
|
||||||
|
const edgeCE = new GraphEdge(vertexC, vertexE);
|
||||||
|
const edgeEF = new GraphEdge(vertexE, vertexF);
|
||||||
|
const edgeFH = new GraphEdge(vertexF, vertexH);
|
||||||
|
const edgeFG = new GraphEdge(vertexF, vertexG);
|
||||||
|
const edgeHG = new GraphEdge(vertexH, vertexG);
|
||||||
|
|
||||||
|
const graph = new Graph();
|
||||||
|
|
||||||
|
graph
|
||||||
|
.addEdge(edgeAB)
|
||||||
|
.addEdge(edgeAC)
|
||||||
|
.addEdge(edgeBD)
|
||||||
|
.addEdge(edgeDC)
|
||||||
|
.addEdge(edgeCE)
|
||||||
|
.addEdge(edgeEF)
|
||||||
|
.addEdge(edgeFH)
|
||||||
|
.addEdge(edgeFG)
|
||||||
|
.addEdge(edgeHG);
|
||||||
|
|
||||||
|
const graphEdgesCount = graph.getAllEdges().length;
|
||||||
|
|
||||||
|
const eulerianPathSet = eulerianPath(graph);
|
||||||
|
|
||||||
|
expect(eulerianPathSet.length).toBe(graphEdgesCount + 1);
|
||||||
|
|
||||||
|
expect(eulerianPathSet[0].getKey()).toBe(vertexC.getKey());
|
||||||
|
expect(eulerianPathSet[1].getKey()).toBe(vertexA.getKey());
|
||||||
|
expect(eulerianPathSet[2].getKey()).toBe(vertexB.getKey());
|
||||||
|
expect(eulerianPathSet[3].getKey()).toBe(vertexD.getKey());
|
||||||
|
expect(eulerianPathSet[4].getKey()).toBe(vertexC.getKey());
|
||||||
|
expect(eulerianPathSet[5].getKey()).toBe(vertexE.getKey());
|
||||||
|
expect(eulerianPathSet[6].getKey()).toBe(vertexF.getKey());
|
||||||
|
expect(eulerianPathSet[7].getKey()).toBe(vertexH.getKey());
|
||||||
|
expect(eulerianPathSet[8].getKey()).toBe(vertexG.getKey());
|
||||||
|
expect(eulerianPathSet[9].getKey()).toBe(vertexF.getKey());
|
||||||
|
});
|
||||||
|
});
|
101
src/algorithms/graph/eulerian-path/eulerianPath.js
Normal file
101
src/algorithms/graph/eulerian-path/eulerianPath.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import graphBridges from '../bridges/graphBridges';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fleury's algorithm of finding Eulerian Path (visit all graph edges exactly once).
|
||||||
|
*
|
||||||
|
* @param {Graph} graph
|
||||||
|
* @return {GraphVertex[]}
|
||||||
|
*/
|
||||||
|
export default function eulerianPath(graph) {
|
||||||
|
const eulerianPathVertices = [];
|
||||||
|
|
||||||
|
// Set that contains all vertices with even rank (number of neighbors).
|
||||||
|
const evenRankVertices = {};
|
||||||
|
|
||||||
|
// Set that contains all vertices with odd rank (number of neighbors).
|
||||||
|
const oddRankVertices = {};
|
||||||
|
|
||||||
|
// Set of all not visited edges.
|
||||||
|
const notVisitedEdges = {};
|
||||||
|
graph.getAllEdges().forEach((vertex) => {
|
||||||
|
notVisitedEdges[vertex.getKey()] = vertex;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Detect whether graph contains Eulerian Circuit or Eulerian Path or none of them.
|
||||||
|
/** @params {GraphVertex} vertex */
|
||||||
|
graph.getAllVertices().forEach((vertex) => {
|
||||||
|
if (vertex.getDegree() % 2) {
|
||||||
|
oddRankVertices[vertex.getKey()] = vertex;
|
||||||
|
} else {
|
||||||
|
evenRankVertices[vertex.getKey()] = vertex;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check whether we're dealing with Eulerian Circuit or Eulerian Path only.
|
||||||
|
// Graph would be an Eulerian Circuit in case if all its vertices has even degree.
|
||||||
|
// If not all vertices have even degree then graph must contain only two odd-degree
|
||||||
|
// vertices in order to have Euler Path.
|
||||||
|
const isCircuit = !Object.values(oddRankVertices).length;
|
||||||
|
|
||||||
|
if (!isCircuit && Object.values(oddRankVertices).length !== 2) {
|
||||||
|
throw new Error('Eulerian path must contain two odd-ranked vertices');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick start vertex for traversal.
|
||||||
|
let startVertex = null;
|
||||||
|
|
||||||
|
if (isCircuit) {
|
||||||
|
// For Eulerian Circuit it doesn't matter from what vertex to start thus we'll just
|
||||||
|
// peek a first node.
|
||||||
|
const evenVertexKey = Object.keys(evenRankVertices)[0];
|
||||||
|
startVertex = evenRankVertices[evenVertexKey];
|
||||||
|
} else {
|
||||||
|
// For Eulerian Path we need to start from one of two odd-degree vertices.
|
||||||
|
const oddVertexKey = Object.keys(oddRankVertices)[0];
|
||||||
|
startVertex = oddRankVertices[oddVertexKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start traversing the graph.
|
||||||
|
let currentVertex = startVertex;
|
||||||
|
while (Object.values(notVisitedEdges).length) {
|
||||||
|
// Add current vertex to Eulerian path.
|
||||||
|
eulerianPathVertices.push(currentVertex);
|
||||||
|
|
||||||
|
// Detect all bridges in graph.
|
||||||
|
// We need to do it in order to not delete bridges if there are other edges
|
||||||
|
// exists for deletion.
|
||||||
|
const bridges = graphBridges(graph);
|
||||||
|
|
||||||
|
// Peek the next edge to delete from graph.
|
||||||
|
const currentEdges = currentVertex.getEdges();
|
||||||
|
/** @var {GraphEdge} edgeToDelete */
|
||||||
|
let edgeToDelete = null;
|
||||||
|
if (currentEdges.length === 1) {
|
||||||
|
// If there is only one edge left we need to peek it.
|
||||||
|
[edgeToDelete] = currentEdges;
|
||||||
|
} else {
|
||||||
|
// If there are many edges left then we need to peek any of those except bridges.
|
||||||
|
[edgeToDelete] = currentEdges.filter(edge => !bridges[edge.getKey()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect next current vertex.
|
||||||
|
if (currentVertex.getKey() === edgeToDelete.startVertex.getKey()) {
|
||||||
|
currentVertex = edgeToDelete.endVertex;
|
||||||
|
} else {
|
||||||
|
currentVertex = edgeToDelete.startVertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete edge from not visited edges set.
|
||||||
|
delete notVisitedEdges[edgeToDelete.getKey()];
|
||||||
|
|
||||||
|
// If last edge were deleted then add finish vertex to Eulerian Path.
|
||||||
|
if (Object.values(notVisitedEdges).length === 0) {
|
||||||
|
eulerianPathVertices.push(currentVertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the edge from graph.
|
||||||
|
graph.deleteEdge(edgeToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eulerianPathVertices;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user