简体   繁体   English

使用 dfs 在无向图中循环

[英]Cycle in an undirected graph using dfs

Please tell me why this code fails to analyse if there is a cycle in the un-directed graph?请告诉我为什么这段代码无法分析无向图中是否存在循环? The code is below.代码如下。 This is the PT07Y question on spoj.这是 spoj 上的 PT07Y 问题。 What i am doing is taking a node and doing the dfs.我正在做的是获取一个节点并执行 dfs。 While performing dfs if I visit the same node then I say there is a cycle.在执行 dfs 时,如果我访问同一个节点,那么我说有一个循环。 After performing dfs from a node, I make the visited array false and perform for next node until I get a cycle or all nodes are visited.从一个节点执行 dfs 后,我将访问的数组设置为 false 并为下一个节点执行,直到我得到一个循环或访问所有节点。

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
bool vis[10001];

int dfs(int n,vector <int> v[],int a)
{
    vis[n]=true;
    for(int i=0;i<v[n].size();i++)
    {
        if(v[n][i]==a)
            return 1;
        if(vis[v[n][i]]==false)
        {
            dfs(v[n][i],v,a);
        }
    }
    return 0;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int n,m,i,a,b,cc;
    cin>>n>>m;
    vector <int> v[n+1];
    memset(vis,false,n+1);
    for(i=0;i<m;i++)
    {
        cin>>a>>b;
        v[a].push_back(b);
    }
    for(i=1;i<=n;i++)
    {
        if(dfs(i,v,i))
        {
            cout<<"NO"<<endl;
            return 0;
        }
        memset(vis,false,sizeof(vis));
    }
    cc=-1;
    for(i=1;i<=n;i++)
    {
        if(vis[i]==false)
        {
            cc++;
            if(cc>0)
            {
                cout<<"NO"<<endl;
                return 0;
            }  
            int m= dfs(i,v,-1);
        }
    }
    cout<<"YES"<<endl;
    return 0;
}

Problem 1问题1

The graph is specified to be undirected, but the input format will only specify each edge once (for one of the directions).该图被指定为无向的,但输入格式将只指定每条边一次(对于其中一个方向)。 So when you read from the input a line like 1 2 , you would have to extend the adjacency list of 1 by 2 and extend the adjacency list of 2 by 1 .因此,当您从输入中读取像1 2这样的行时,您必须将1的邻接列表扩展2 ,并将2的邻接列表扩展1 However, in your program you only store the first direction.但是,在您的程序中,您只存储第一个方向。 Therefore your representation of the graph will be incomplete.因此,您对图表的表示将是不完整的。

To fix this, extend your input processing to store both directions:要解决此问题,请扩展您的输入处理以存储两个方向:

  for (i = 0; i < m; i++) {
    cin >> a >> b;
    v[a].push_back(b);
    v[b].push_back(a);
  }

Problem 2问题2

Your dfs method behaves incorrectly:您的dfs方法行为不正确:

The dfs method returns 1 if it finds the target vertex. dfs方法在找到目标顶点时返回1 However, this return code is not propagated along the recursion.但是,此返回代码不会沿递归传播。 So when you make the recursive call dfs(v[n][i], v, a);所以当你进行递归调用时dfs(v[n][i], v, a); inside your dfs method, and that call returns 1 , this result is ignored by the current dfs call.在您的dfs方法中,并且该调用返回1 ,当前dfs调用将忽略此结果。 If the current dfs call does not find the target vertex in the current adjacency list, it will then complete the for loop and return 0 .如果当前的dfs调用没有在当前的邻接表中找到目标顶点,那么它将完成for循环并返回0

To fix this, you would have to make sure that if the recursive dfs call returns 1 , the for loop is aborted and the 1 is also returned by the current dfs call, eg:要解决此问题,您必须确保如果递归dfs调用返回1 ,则for循环中止并且当前dfs调用也返回1 ,例如:

if (dfs(v[n][i], v, a, n) == 1) {
  return 1;
}

With this fix applied, another problem is revealed: When the dfs method discovers the target vertex in the list of adjacent vertices, it does not rule out the edge from where the current vertex was reached.应用此修复程序后,会出现另一个问题:当dfs方法在相邻顶点列表中发现目标顶点时,它不排除到达当前顶点的边。 So if you have a graph 1 <-> 2 and you start dfs at 1 with target vertex 1 , you would examine the list of adjacent vertices, containing 2 .因此,如果您有一个图1 <-> 2并且您从1开始dfs ,目标顶点为1 ,您将检查包含2的相邻顶点列表。 Traversing the edge from 1 to 2 , you would examine the list of adjacent vertices, containing 1 .12遍历边,您将检查包含1的相邻顶点列表。 Your method would then report that the target vertex has been found, but this is not valid because it has only been found along the edge from where you came.然后,您的方法将报告已找到目标顶点,但这是无效的,因为它只在您来的地方的边上找到。

To fix this, you could track the parent vertex for each dfs call.要解决此问题,您可以跟踪每个dfs调用的父顶点。 Initially, no parent is set (ie -1 ).最初,没有设置父级(即-1 )。 When you go from a to b , you would pass a as the parent.当您从a转到b时,您会将a作为父级传递。 At b , you would then ignore the parent a in the adjacency list of b :b处,您将忽略b a

So your dfs would look eg like this:所以你的dfs看起来像这样:

int dfs(int n, vector<int> v[], int a, int parent) {
  vis[n] = true;
  for (int i = 0; i < v[n].size(); i++) {
    if (v[n][i] == parent) {
      continue;
    }
    if (v[n][i] == a)
      return 1;
    if (vis[v[n][i]] == false) {
      if (dfs(v[n][i], v, a, n) == 1) {
        return 1;
      }
    }
  }
  return 0;
}

Problem 3问题3

The performance of your implementation is not optimal (which causes a time-out on SPOJ).您的实施性能不是最佳的(这会导致 SPOJ 超时)。 You can improve this by not clearing the vis array after each cycle check in your main function.您可以通过在每次循环检查main函数后不清除vis数组来改进这一点。 Instead, reuse the vis array and only perform the cycle check on vertices that have not been visited yet:相反,重用vis数组并只对尚未访问过的顶点执行循环检查:

  for (i = 1; i <= n; i++) {
    if (vis[i] == false) {
      if (dfs(i, v, i, -1)) {
        cout << "NO" << endl;
        return 0;
      }
    }
  }
  memset(vis, false, sizeof(vis));

This is enough to get your code accepted by SPOJ.这足以让 SPOJ 接受您的代码。

However, you could further optimize the connectivity check in your code: Instead of clearing the vis array after the cycle check and performing another series of dfs for each vertex until a second component is discovered, you could reuse the vis array and simply check if there are any unvisited vertices after the cycle checks.但是,您可以进一步优化代码中的连通性检查:不是在循环检查后清除vis数组并为每个顶点执行另一系列dfs直到发现第二个组件,您可以重用vis数组并简单地检查是否存在是循环检查后任何未访问的顶点。 This would allow you to omit the second dfs series completely.这将允许您完全省略第二个dfs系列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM