How to convert an iterative function that checks for cycles in an adjacency matrix, to a simpler, recursive function in C?

半城伤御伤魂 提交于 2020-06-16 18:41:48

问题


Having trouble grasping recursive thinking, and a lot of what I've seen on Stack overflow so far I don't understand.

I'm trying to detect a cycle in a directed graph, given a 2D array adjacency matrix where a value of true for graph[i][j] indicates an edge from i to j.

Have created a check_cycles function which checks for a path from j to i, given graph[i][j] and have hard coded the size of the graph to simplify the issue.

I am getting a return value of true as expected with this code, but as you can see, now I've hard coded a lot of for loops, and this would be impractical should the size or values passed to the function be changed.

How would I find a recursive solution to this? What would be the case in which the function should stop running?

Right now I'm using a library that allows for a bool return value on functions, but it could as well return void.

#include <stdio.h>
#include <cs50.h>


//hard coding the size of the graph
int size = 5;

//set up an adjecency matrix
bool graph[5][5];

//functions
bool check_cycles(int index1, int index2);

int main(void)
{

  //setting the graph values to false
  for (int i = 0; i < size; i++)
  {
    for (int j = 0; j < size; j++)
    {
      graph[i][j] = false;
    }
  }

  //hard coding a cycle into the graph
  graph[0][1] = true;
  graph[1][2] = true;
  graph[2][3] = true;
  graph[3][0] = true;

//check for cycles
  printf(check_cycles(2,3)? "Cycle detected\n" : "No cycles\n");

}



bool check_cycles(int index1, int index2)
{  
  for (int i = 0; i < size; i++)
  { 
    //check for adjacent edge
    if (graph[index2][i] == true)
    {
      //check if edge points to initial node
      if (graph[i][index1] == true)
      {
        return true;
      }
      else 
      {
        for (int j = 0; j < size; j++)
        {
          if (graph[i][j] == true)
          {
            if (graph[j][index1] == true)
            {
              return true;
            }
            else  
            {
              for (int k = 0; k < size; k++)
              {
                if (graph[j][k] == true)
                {
                  if (graph[k][index1] == true)
                  {
                    return true;
                  }
                }
              }
            }
          }
        }
      }
    }
  }
return false;
}

回答1:


This idea of hardcoding may seem like a nice way to start, but as you've noticed, a fundamentally different strategy is going to be necessary to solve problems like this.

Cycle detection can be done naively running a depth-first search starting from every node. Before beginning, create an empty set to keep track of visited nodes. When you visit a node, first look it up in the set. If it's already there, you've found a cycle; otherwise, mark it visited and search all of its adjacent neighbors. If you don't find any cycles for a node, mark it unvisited before exiting its call frame to avoid false positives if there are multiple paths to it.

You can do this iteratively using a stack and a loop. pop the stack to visit a node and push its neighbors onto the stack. The fundamental approach is the same in that you're using lookups on a visited set to detect cycles.

Once you have the basic implementation, you may wish to optimize.

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

bool is_cyclic_(int curr, int n, bool **graph, bool **visited) {
    if ((*visited)[curr]) {
        return true;
    }

    (*visited)[curr] = true;

    for (int i = 0; i < n; i++) {
        if (graph[curr][i] && is_cyclic_(i, n, graph, visited)) {
            return true;
        }
    }

    (*visited)[curr] = false;
    return false;
}

bool is_cyclic(int n, bool **graph) {
    bool *visited = calloc(n, sizeof(*visited));

    for (int i = 0; i < n; i++) {        
        if (is_cyclic_(i, n, graph, &visited)) {
            free(visited);
            return true;
        }
    }

    free(visited);
    return false;
}

int main() {
    int n = 5;
    bool graph[][5] = {
        {0,0,0,1,0},
        {0,0,0,0,0},
        {0,0,0,0,1},
        {0,0,1,0,0},
        {1,0,0,0,0},
    };
    bool **pgraph = malloc(sizeof(*pgraph) * n);

    for (int i = 0; i < n; i++) {
        pgraph[i] = malloc(sizeof(pgraph[i]) * n);

        for (int j = 0; j < n; j++) {
            pgraph[i][j] = graph[i][j];
        }
    }

    printf("%d\n", is_cyclic(n, pgraph));

    for (int i = 0; i < n; i++) {
        free(pgraph[i]);
    }

    free(pgraph);
    return 0;
}



回答2:


Thank you! I figured this out eventually -- I was overcomplicating the problem. My cycle function is, in effect, searching for a path, from node a to b, given that the edge b to a is already set to true.

I was then able to use recursion as follows, size was a global variable --

bool check_path(int index2, int index1)
{
    if (index1 == index2)
    {
        return true;
    }
    for (int i = 0; i < size; i++)
    {
            if (graph[index2][i] == true)
        {
            if (check_path(i, index1))
            {
                return true;
            }
        }
    }
    return false;
}


来源:https://stackoverflow.com/questions/61303200/how-to-convert-an-iterative-function-that-checks-for-cycles-in-an-adjacency-matr

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!