fix: bug fixed #58, DirectedGraph.getCycles returns an inexisting cycle. feat: hasValue method for HashMap.

This commit is contained in:
Revone 2023-12-12 09:57:30 +08:00
parent f0927e8b0a
commit e113a99b26
5 changed files with 78 additions and 12 deletions

View file

@ -1086,15 +1086,33 @@ export abstract class AbstractGraph<
}
const cycles: Map<number, VO[]> = new Map();
if (needCycles) {
let SCCs: Map<number, VO[]> = new Map();
if (SCCs.size < 1) {
SCCs = getSCCs();
}
SCCs.forEach((SCC, low) => {
if (SCC.length > 1) {
cycles.set(low, SCC);
if (needCycles) {
const visitedMap: Map<VO, boolean> = new Map();
const stack: VO[] = [];
const findCyclesDFS = (cur: VO, parent: VO | undefined) => {
visitedMap.set(cur, true);
stack.push(cur);
const neighbors = this.getNeighbors(cur);
for (const neighbor of neighbors) {
if (!visitedMap.get(neighbor)) {
findCyclesDFS(neighbor, cur);
} else if (stack.includes(neighbor) && neighbor !== parent) {
const cycleStartIndex = stack.indexOf(neighbor);
const cycle = stack.slice(cycleStartIndex);
const cycleLow = Math.min(...cycle.map(v => dfnMap.get(v) || Infinity));
cycles.set(cycleLow, cycle);
}
}
stack.pop();
};
vertexMap.forEach(v => {
if (!visitedMap.get(v)) {
findCyclesDFS(v, undefined);
}
});
}

View file

@ -411,6 +411,13 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
}
}
hasValue(value: V): boolean {
for (const [, elementValue] of this) {
if (elementValue === value) return true;
}
return false;
}
setMany(entries: Iterable<[K, V]>): void {
for (const entry of entries) {
const [key, value] = entry;

View file

@ -188,7 +188,7 @@ export class Queue<E = any> extends IterableElementBase<E> {
* @param {E} value - The value parameter represents the value that you want to add to the queue.
*/
enqueue(value: E) {
this.push(value);
return this.push(value);
}
/**

View file

@ -588,7 +588,7 @@ describe('cycles, strongly connected components, bridges, articular points in Di
const cutVertexes = graph.getCutVertexes();
const dfnMap = graph.getDFNMap();
const lowMap = graph.getLowMap();
expect(cycles.size).toBe(1);
expect(cycles.size).toBe(2);
expect(scCs.size).toBe(5);
expect(bridges.length).toBe(4);
expect(cutVertexes.length).toBe(4);
@ -673,3 +673,24 @@ describe('DirectedGraph iterative Methods', () => {
})
});
describe('DirectedGraph getCycles', () => {
test('should getCycles return correct result', () => {
const graph = new DirectedGraph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.addVertex('E');
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.addEdge('B', 'D');
graph.addEdge('C', 'D');
graph.addEdge('D', 'E');
graph.addEdge('E', 'B');
const cycles = graph.getCycles();
expect(cycles.size).toBe(1);
console.log(Array.from(cycles.values()));
})
})

View file

@ -237,7 +237,7 @@ describe('cycles, strongly connected components, bridges, articular points in Un
const cutVertexes = graph.getCutVertexes();
const dfnMap = graph.getDFNMap();
const lowMap = graph.getLowMap();
expect(cycles.size).toBe(1);
expect(cycles.size).toBe(2);
expect(scCs.size).toBe(5);
expect(bridges.length).toBe(4);
expect(cutVertexes.length).toBe(4);
@ -260,4 +260,24 @@ it("Should return Infinity if dest is not found", () => {
const minCost01 = graph.getMinCostBetween(0, 1, true);
expect(minCost01).toBe(1);
});
});
describe('UndirectedGraph getCycles', () => {
test('should getCycles return correct result', () => {
const graph = new UndirectedGraph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.addVertex('E');
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.addEdge('B', 'D');
graph.addEdge('C', 'D');
graph.addEdge('D', 'E');
graph.addEdge('E', 'B');
const cycles = graph.getCycles();
expect(cycles.size).toBe(2);
console.log(Array.from(cycles.values()));
})
})