What is the best way to detect cycles in a directed graph and identify the nodes involved in the cycle?

I have an array of objects in JavaScript, and I am attempting to create a directed graph. How can I determine if the graph contains a cycle, and if so, what elements are part of that cycle? The graph is not strongly connected, and nodes can be isolated like "f".

 
array = {};
//operations   parents
    array[a] = [b,c]
    array[b] = [d,c]
    array[e] = [a,b]
    array[d] = [e]
    array[f] = []

https://i.sstatic.net/nl3tY.png

I am looking to identify cycles between operations within the graph, such as the cycle from e-d-b-e here. How can I achieve this using JavaScript?

Answer №1

Presented below is a Breadth-First Search (BFS) solution designed to identify one cycle, if any exists, with an emphasis on finding the shortest possible cycle(s).

function getCycle(graph) {
    // Make a copy of the graph, ensuring all node references are strings
    graph = Object.assign(...Object.keys(graph).map( node =>
                ({ [node]: graph[node].map(String) }) 
    ));

    let queue = Object.keys(graph).map( node => [node] );
    while (queue.length) {
        const batch = [];
        for (const path of queue) {
            const parents = graph[path[0]] || [];
            for (const node of parents) {
                if (node === path[path.length-1]) return [node, ...path];
                batch.push([node, ...path]);
            }
        }
        queue = batch;
    }
}

// Example using string node references
var graph = {
    a: ['b', 'c'],
    b: ['d', 'c'],
    e: ['a', 'b'],
    d: ['e']
};
var result = getCycle(graph);
console.log(result);

// Example using numeric node references
var graph = {
    0: [4],
    1: [4,0],
    2: [0,1],
    3: [1],
    4: [3]
};
var result = getCycle(graph);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Note: When defining your graph with number references, it's necessary to convert them to strings due to object property types always being strings. While using == may seem like an option, it results in mixed string and number output. It's recommended to convert all node references to strings initially.

Answer №2

When utilizing Depth-First Search, a cycle in the graph is identified if an edge leads to a previously discovered ancestor of the current node.

Answer №3

Upon reviewing the feedback on @zenwraigts' solution, it appears that the issue at hand is related to Strongly Connected Components. For a helpful guide, check out this tutorial here, along with JavaScript implementations available here and here.

How does this relate to SCC?

When all vertices form a strongly connected component for a cycle, the task shifts towards identifying all SCCs and displaying them.

Answer №4

When analyzing the graph, it is crucial to traverse through all nodes while maintaining a visited array to keep track of visited nodes. If you encounter a node that has already been visited, then there is a cycle present in the directed graph.

For reference and ease of understanding, provided below is a runnable code snippet which can serve as guidance for developing your algorithm.

I have made some enhancements to my previous answer by iterating through all nodes, even covering unreachable paths. Thank you everyone for your contributions :)

array = {};

/*
Assuming 
a = 0
b=  1
c = 2
d = 3
e = 4
*/

// Constructing an adjacency list of directed nodes based on the given diagram
array[0] = [4];
array[1] = [4,0];
array[2] = [0,1];
array[4] = [3];
array[3] = [1];
visited = {};
visited[0] = 0;
visited[1] = 0;
visited[2] = 0;
visited[3] = 0;
visited[4] = 0;

list_for_cycle_nodes = [];

for(var node = 0; node<5; node++) {
   if (dfs(node)) {
    for (var index = 0; index < list_for_cycle_nodes.length; index++) {
      console.log(list_for_cycle_nodes[index]+" ");
    }
    console.log('There is a cycle');
  } else {
    console.log('Yipee, there is no cycle');
  } 
}

function dfs(s) {
  if(visited[s] == 1) {
    return true;
  }
  list_for_cycle_nodes.push(s);
  visited[s] = 1;
  var flag = false;
  if(array[s].length <= 0) {
    return false;
  }
  for(var index = 0; index<array[s].length; index++) {
    flag = flag | dfs(array[s][index]);
    if(flag) {
      return true;
    }
  }
  return flag;
}

I trust this information proves beneficial in your analysis!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Perform a task upon clicking the JavaScript menu

Implementing dropdown menu items using a text link with JavaScript and CSS. You can view a demo here. I am looking to trigger an action when each menu item is clicked. Currently, they are not behaving as expected. HTML: <span class="inline-dropdown- ...

Employing a break statement after the default case within a switch statement even when the default is not

According to a tutorial from w3schools that discusses switch statements, it is advised: If the default case is not the last case in the switch block, it is important to remember to end it with a break statement. However, the same tutorial also explains ...

Guide to Sending JSON Data using ASP.NET and jQuery

I'm trying to figure out how to return JSON data in my code. JavaScript $(function () { $.ajax({ type: "POST", url: "Default.aspx/GetProducts", data: "{}", contentType: "application/j ...

Apply a class to each consecutive element following the current one until reaching a child element with a

My goal is to apply a "bg-info" class using jQuery to all rows (tr) that come after odd rows with a child element of "test". The "bg-info" class should be removed when a row with a child element of "test" is encountered, and then re-applied when the next o ...

The execution of my JavaScript code does not pause for the completion of an ajax request

I'm currently working on a register validation code in JavaScript. One of the key aspects checked by the validation function is whether the email address provided by the user already exists in the database. To handle this, I'm implementing ajax, ...

Elements that are disabled will not be sent when submitting a form

I am trying to disable certain elements and enable others when submitting a form in order to submit the values. However, I am encountering an issue where I cannot find the values in the action class even though the elements are being enabled. Due to using ...

Does Notepad++ only paste the first line of copied code when using the replace feature?

I frequently utilize the replace feature in Notepad++ to change code across multiple files. However, I've encountered an issue where when I try to paste code with multiple lines into the replace textbox of Notepad++, only the first line gets pasted. T ...

JavaScript is failing to run when placed in an external script file

Currently, I'm in the process of developing an ASP.NET MVC website where I require a tag editor that resembles the one seen on Stack Overflow. After researching how to implement autocomplete using jQuery UI, I encountered an issue: the script fails to ...

"Using ng-include with ng-show doesn't seem to be functioning properly

I am facing an issue with my Angular app where the template is getting too large. I would like to split it and utilize the ng-include directive, but I am struggling to get it to work properly. current state of template.html <div class="edit-ob ...

AngularJS Error: The method serviceName.functionName() is not a valid function

I am trying to implement a function that will go back when the cancel button is clicked. Here is the view code: <div ng-controller="goodCtrl"> <button class="btn" ng-click="cancel()">Cancel</button> </div> And here is the Jav ...

For every iteration, verify the presence of the image

I am currently working on a foreach loop to iterate over the data returned by an ajax call. Within this loop, I am checking if each record has an associated image. Code for Checking Image Existence: function checkImageExists(url, callback) { var img ...

Addon for Firefox: Image Upload

I am looking for a way to streamline the process of uploading an image to a website through a Firefox Addon. While I know it is possible to use createElement('canvas'), convert Image data to base64, and XHR POST the data, I would prefer to lever ...

What causes an error when attempting ++[] but produces 1 with ++[[]][0]?

Can you explain the difference between the following two expressions? It appears that incrementing [] is equivalent to incrementing [[]][0] since the first element of this outer array is []. console.log(++[]); console.log(++[[]][0]); ...

Sending an Ajax call to a PHP script without including any data in the POST

Having recently started delving into javascript, I've encountered an issue with performing an ajax POST to php. I'm attempting to send javascript variables over to php through an ajax POST, but it seems to be malfunctioning. The ajax post goes ...

Combining res.render and redirect in Express.js for efficient rendering and redirection

I am currently developing a web application that involves setting up routes for user authentication. The Challenge: After a successful registration, I want to redirect the user to /login page while also including render options. However, when I use render ...

Angular 10 and Typescript: Variables assigned within the change event become undefined

In my code, I initialize an Algolia input and set an onchange event to it. This initialization takes place in a service. algolia_data; random_var; this.http.post<any>('APIENDPOINT', formData).subscribe(data => { instance = places({ ...

The process of generating a querystring from a form using jQuery is not functioning as expected

I am attempting to send an AJAX request, but I am facing an issue where the query string I am trying to construct turns out to be empty. <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>This project dem ...

Verifying if the checkbox has been unselected

I'm currently attempting to send post requests to the database based on whether a checkbox is checked or unchecked. My goal is to add or delete data in the database using these post requests. I've successfully implemented the logic to add data us ...

Trigger the change of an element upon hovering over another element

Is there a way to manipulate an element when another element is being hovered over, with the two elements structured like this: <div id="parent_element"> <div id="class-open-1"></div> <div id="class-close-1"></div> < ...

Using async/await keywords in React Native while iterating through an array and calling an API does not result in successful resolution

When attempting to request data from my API in this manner: export default ({ navigation }) => { // Call getMovieImages with the id of the likedMovies from the state.likedMovies const { getMovieImages, state:{ likedMovies }} = useContext(MovieContext); ...