问题
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