This is a different question from what I have found so far related to cycles in graph as they relate to finding cycles in general but I need to find a cycle in a specific situation.
Consider the following graph:
n1: [n2]
n2: [n3]
n3: [n4]
n4: [n5, n2]
n5: []
This function should only return true
when testing for the path from n4
to n2
.
In my situation I don't consider a cycle other paths because they are "safe" to render in the UI. However, if I would try to render the association between in n4
to n2
I would recreate the whole path recursively.
So far I came up with the following:
/**
* @param {string} target The target node
* @param {Record<string, string[]>} g The graph
* @param {string} key The current key
* @returns {boolean} true when going from `key` to `target` would create a cycle.
*/
hasCycle(target, g, key) {
if (target === key) {
return true;
}
const neighbors: string[] = [];
Object.keys(g).forEach(prop => {
if (g[prop].includes(key)) {
neighbors.push(prop);
}
});
for (const neighbor of neighbors) {
if (neighbor === key) {
return true;
}
if (hasCycle(target, g, neighbor)) {
return true;
}
}
return false;
}
This works in most cases except for the following:
n1: [n2]
n2: [n3]
n3: [n2]
When I test hasCycle('n2', {...}, 'n3')
this function returns true
as the cycle exist but for the purpose of the UI I am building I am not consider it a cycle just yet (I would if I would be testing for n3
-> n2
).
Edit, to better illustrate the problem. Imagine rendering in the UI a JSON schema as a data structure. The schema has properties that can be either scalars or other schemes. These schemes can reference each other in properties which at some point will cause a cycle (when you represent it as a graph). The reason I am not considering n2
-> n3
a cycle for the purpose of the visualization is because it is safe for me to render the schema that is defined as n3
under a n2
property but if I would expand a property of n3
schema that has a reference to n2
would cause a recursive rendering of the whole tree.
- schema 1
- prop1 (scalar)
- prop2 (ref to schema 2)
- schema 2
- prop3 (ref to schema 3)
- schema 3
- prop4 (ref to schema 2)
In the UI you need to expand complex properties (referenced schemes) to see their properties. When the user expands prop4
it would recreate the whole schema 2
-> prop3
-> schema 3
-> prop4
in a cycle which would end with throwing an error. The function I am trying to find is to tell me that prop4
cannot be expanded.