Как определить, является ли направленный граф циклическим?

Как мы можем определить, является ли направленный граф циклическим? Я думал, ширину первого поиска, но я не уверен. Есть идеи?

6 ответов


обычно вместо этого используется поиск глубины. Я не знаю, применим ли BFS легко.

на DFS, связующее дерево построено в порядке посещения. Если посещается предок узла в дереве (т. е. создается задний край), то мы обнаруживаем цикл.

см.http://www.cs.nyu.edu/courses/summer04/G22.1170-001/6a-Graphs-More.pdf для более детального объяснения.


Что вам действительно нужно, я считаю, является топологический алгоритм сортировки, как описано здесь:

http://en.wikipedia.org/wiki/Topological_sorting

Если направленный граф имеет цикл, то алгоритм завершится ошибкой.

комментарии / ответы, которые я видел до сих пор, похоже, не хватает того факта, что в направлено график может быть более одного способа добраться от узла X до узла Y без каких-либо (направленных) циклы на графике.


используйте DFS для поиска, если какой-либо путь является циклическим

class Node<T> { T value; List<Node<T>> adjacent;  }

class Graph<T>{

    List<Node<T>> nodes;

   public boolean isCyclicRec()
   {

      for (Node<T> node : nodes)
      {
            Set<Node<T>> initPath = new HashSet<>();
            if (isCyclicRec(node, initPath))
            {
              return true;
            }
      }
      return false;
   }

   private boolean isCyclicRec(Node<T> currNode, Set<Node<T>> path)
   {
      if (path.contains(currNode))
      {
        return true;
      }
      else
      {
        path.add(currNode);
        for (Node<T> node : currNode.adjacent)
        {
            if (isCyclicRec(node, path))
            {
                return true;
            }
            else
            {
                path.remove(node);
            }
        }
      }
      return false;
  }

подход:1
как насчет уровня без назначения для обнаружения цикла. например: рассмотрим график ниже. A->(B,C) B->D D->(E,F) E,F->(G) E - >D при выполнении DFS начните назначать уровень no узлу, который вы посещаете (root A=0). уровень no узла = родитель+1. Итак, a=0, B=1, D=2, F=3, G=4, тогда рекурсия достигает D, поэтому E=3. Не отмечайте уровень для G (G уже уровень не назначен, который больше, чем E) теперь E также имеет край D. Поэтому выравнивание скажет, что D должен получить Уровень № 4. Но уже имеет" нижний уровень", назначенный ему из 2. Таким образом, каждый раз, когда вы пытаетесь назначить номер уровня узлу при выполнении DFS, который уже имеет более низкий номер уровня, вы знаете, что направленный граф имеет цикл..

approach2:
использовать 3 цвета. белый, серый, черный. покрасьте только белые узлы, белые узлы в серый цвет, когда вы идете вниз по DFS, покрасьте серые узлы в черный, когда рекурсия разворачивается (все дочерние элементы обрабатываются). если еще не все дети обработаны, и вы нажмете серый узел, который ездить на велосипеде. например: все белые, чтобы начать выше прямого графика. цвета A, B, D,F, G окрашены в бело-серый цвет. G-лист, поэтому все дети обрабатывают цвет от серого до Черного. рекурсия разворачивается до F (все обработанные дети) окрашивают ее в черный цвет. теперь вы достигаете D, D имеет необработанных детей, поэтому цвет e серый, G уже окрашен в черный цвет, поэтому не спускайтесь дальше. E также имеет ребро К D, поэтому при обработке D (D все еще серый) вы находите ребро обратно к D(серый узел), обнаруживается цикл.


тестирование топологической сортировки по данному графику приведет вас к решению. Если алгоритм для topsort, i.e ребра всегда должны быть направлены в одну сторону, это означает, что график содержит циклы.


еще одним простым решением был бы подход пометки и развертки. В принципе, для каждого узла в дереве вы помечаете его как "посещенный", а затем переходите к его детям. Если вы когда-нибудь увидите узел с установленным флагом "visted", вы знаете, что есть цикл.

Если изменение графика для включения "посещенного" бита невозможно, вместо этого можно использовать набор указателей узлов. Чтобы пометить узел как посещенный, вы помещаете указатель на него в набор. Если курсор уже находится в наборе, есть цикл.