Testing whether a path in a directed graph does not cause cycles

52 views Asked by At

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.

0

There are 0 answers