Finding all cycles in a directed graph

前端 未结 17 2168
渐次进展
渐次进展 2020-11-22 05:47

How can I find (iterate over) ALL the cycles in a directed graph from/to a given node?

For example, I want something like this:

A->B->A
A->B         


        
相关标签:
17条回答
  • 2020-11-22 06:08

    I was given this as an interview question once, I suspect this has happened to you and you are coming here for help. Break the problem into three questions and it becomes easier.

    1. how do you determine the next valid route
    2. how do you determine if a point has been used
    3. how do you avoid crossing over the same point again

    Problem 1) Use the iterator pattern to provide a way of iterating route results. A good place to put the logic to get the next route is probably the "moveNext" of your iterator. To find a valid route, it depends on your data structure. For me it was a sql table full of valid route possibilities so I had to build a query to get the valid destinations given a source.

    Problem 2) Push each node as you find them into a collection as you get them, this means that you can see if you are "doubling back" over a point very easily by interrogating the collection you are building on the fly.

    Problem 3) If at any point you see you are doubling back, you can pop things off the collection and "back up". Then from that point try to "move forward" again.

    Hack: if you are using Sql Server 2008 there is are some new "hierarchy" things you can use to quickly solve this if you structure your data in a tree.

    0 讨论(0)
  • 2020-11-22 06:10

    To clarify:

    1. Strongly Connected Components will find all subgraphs that have at least one cycle in them, not all possible cycles in the graph. e.g. if you take all strongly connected components and collapse/group/merge each one of them into one node (i.e. a node per component), you'll get a tree with no cycles (a DAG actually). Each component (which is basically a subgraph with at least one cycle in it) can contain many more possible cycles internally, so SCC will NOT find all possible cycles, it will find all possible groups that have at least one cycle, and if you group them, then the graph will not have cycles.

    2. to find all simple cycles in a graph, as others mentioned, Johnson's algorithm is a candidate.

    0 讨论(0)
  • 2020-11-22 06:11

    Start at node X and check for all child nodes (parent and child nodes are equivalent if undirected). Mark those child nodes as being children of X. From any such child node A, mark it's children of being children of A, X', where X' is marked as being 2 steps away.). If you later hit X and mark it as being a child of X'', that means X is in a 3 node cycle. Backtracking to it's parent is easy (as-is, the algorithm has no support for this so you'd find whichever parent has X').

    Note: If graph is undirected or has any bidirectional edges, this algorithm gets more complicated, assuming you don't want to traverse the same edge twice for a cycle.

    0 讨论(0)
  • 2020-11-22 06:11

    I stumbled over the following algorithm which seems to be more efficient than Johnson's algorithm (at least for larger graphs). I am however not sure about its performance compared to Tarjan's algorithm.
    Additionally, I only checked it out for triangles so far. If interested, please see "Arboricity and Subgraph Listing Algorithms" by Norishige Chiba and Takao Nishizeki (http://dx.doi.org/10.1137/0214017)

    0 讨论(0)
  • 2020-11-22 06:11

    Javascript solution using disjoint set linked lists. Can be upgraded to disjoint set forests for faster run times.

    var input = '5\nYYNNN\nYYYNN\nNYYNN\nNNNYN\nNNNNY'
    console.log(input);
    //above solution should be 3 because the components are
    //{0,1,2}, because {0,1} and {1,2} therefore {0,1,2}
    //{3}
    //{4}
    
    //MIT license, authored by Ling Qing Meng
    
    //'4\nYYNN\nYYYN\nNYYN\nNNNY'
    
    //Read Input, preformatting
    var reformat = input.split(/\n/);
    var N = reformat[0];
    var adjMatrix = [];
    for (var i = 1; i < reformat.length; i++) {
        adjMatrix.push(reformat[i]);
    }
    
    //for (each person x from 1 to N) CREATE-SET(x)
    var sets = [];
    for (var i = 0; i < N; i++) {
        var s = new LinkedList();
        s.add(i);
        sets.push(s);
    }
    
    //populate friend potentials using combinatorics, then filters
    var people =  [];
    var friends = [];
    for (var i = 0; i < N; i++) {
        people.push(i);
    }
    var potentialFriends = k_combinations(people,2);
    for (var i = 0; i < potentialFriends.length; i++){
        if (isFriend(adjMatrix,potentialFriends[i]) === 'Y'){
            friends.push(potentialFriends[i]);
        }
    }
    
    
    //for (each pair of friends (x y) ) if (FIND-SET(x) != FIND-SET(y)) MERGE-SETS(x, y)
    for (var i = 0; i < friends.length; i++) {
        var x = friends[i][0];
        var y = friends[i][1];
        if (FindSet(x) != FindSet(y)) {
            sets.push(MergeSet(x,y));
        }
    }
    
    
    for (var i = 0; i < sets.length; i++) {
        //sets[i].traverse();
    }
    console.log('How many distinct connected components?',sets.length);
    
    
    
    //Linked List data structures neccesary for above to work
    function Node(){
        this.data = null;
        this.next = null;
    }
    
    function LinkedList(){
        this.head = null;
        this.tail = null;
        this.size = 0;
    
        // Add node to the end
        this.add = function(data){
            var node = new Node();
            node.data = data;
            if (this.head == null){
                this.head = node;
                this.tail = node;
            } else {
                this.tail.next = node;
                this.tail = node;
            }
            this.size++;
        };
    
    
        this.contains = function(data) {
            if (this.head.data === data) 
                return this;
            var next = this.head.next;
            while (next !== null) {
                if (next.data === data) {
                    return this;
                }
                next = next.next;
            }
            return null;
        };
    
        this.traverse = function() {
            var current = this.head;
            var toPrint = '';
            while (current !== null) {
                //callback.call(this, current); put callback as an argument to top function
                toPrint += current.data.toString() + ' ';
                current = current.next; 
            }
            console.log('list data: ',toPrint);
        }
    
        this.merge = function(list) {
            var current = this.head;
            var next = current.next;
            while (next !== null) {
                current = next;
                next = next.next;
            }
            current.next = list.head;
            this.size += list.size;
            return this;
        };
    
        this.reverse = function() {
          if (this.head == null) 
            return;
          if (this.head.next == null) 
            return;
    
          var currentNode = this.head;
          var nextNode = this.head.next;
          var prevNode = this.head;
          this.head.next = null;
          while (nextNode != null) {
            currentNode = nextNode;
            nextNode = currentNode.next;
            currentNode.next = prevNode;
            prevNode = currentNode;
          }
          this.head = currentNode;
          return this;
        }
    
    
    }
    
    
    /**
     * GENERAL HELPER FUNCTIONS
     */
    
    function FindSet(x) {
        for (var i = 0; i < sets.length; i++){
            if (sets[i].contains(x) != null) {
                return sets[i].contains(x);
            }
        }
        return null;
    }
    
    function MergeSet(x,y) {
        var listA,listB;
        for (var i = 0; i < sets.length; i++){
            if (sets[i].contains(x) != null) {
                listA = sets[i].contains(x);
                sets.splice(i,1);
            }
        }
        for (var i = 0; i < sets.length; i++) {
            if (sets[i].contains(y) != null) {
                listB = sets[i].contains(y);
                sets.splice(i,1);
            }
        }
        var res = MergeLists(listA,listB);
        return res;
    
    }
    
    
    function MergeLists(listA, listB) {
        var listC = new LinkedList();
        listA.merge(listB);
        listC = listA;
        return listC;
    }
    
    //access matrix by i,j -> returns 'Y' or 'N'
    function isFriend(matrix, pair){
        return matrix[pair[0]].charAt(pair[1]);
    }
    
    function k_combinations(set, k) {
        var i, j, combs, head, tailcombs;
        if (k > set.length || k <= 0) {
            return [];
        }
        if (k == set.length) {
            return [set];
        }
        if (k == 1) {
            combs = [];
            for (i = 0; i < set.length; i++) {
                combs.push([set[i]]);
            }
            return combs;
        }
        // Assert {1 < k < set.length}
        combs = [];
        for (i = 0; i < set.length - k + 1; i++) {
            head = set.slice(i, i+1);
            tailcombs = k_combinations(set.slice(i + 1), k - 1);
            for (j = 0; j < tailcombs.length; j++) {
                combs.push(head.concat(tailcombs[j]));
            }
        }
        return combs;
    }
    
    0 讨论(0)
  • 2020-11-22 06:15

    Regarding your question about the Permutation Cycle, read more here: https://www.codechef.com/problems/PCYCLE

    You can try this code (enter the size and the digits number):

    # include<cstdio>
    using namespace std;
    
    int main()
    {
        int n;
        scanf("%d",&n);
    
        int num[1000];
        int visited[1000]={0};
        int vindex[2000];
        for(int i=1;i<=n;i++)
            scanf("%d",&num[i]);
    
        int t_visited=0;
        int cycles=0;
        int start=0, index;
    
        while(t_visited < n)
        {
            for(int i=1;i<=n;i++)
            {
                if(visited[i]==0)
                {
                    vindex[start]=i;
                    visited[i]=1;
                    t_visited++;
                    index=start;
                    break;
                }
            }
            while(true)
            {
                index++;
                vindex[index]=num[vindex[index-1]];
    
                if(vindex[index]==vindex[start])
                    break;
                visited[vindex[index]]=1;
                t_visited++;
            }
            vindex[++index]=0;
            start=index+1;
            cycles++;
        }
    
        printf("%d\n",cycles,vindex[0]);
    
        for(int i=0;i<(n+2*cycles);i++)
        {
            if(vindex[i]==0)
                printf("\n");
            else
                printf("%d ",vindex[i]);
        }
    }
    
    0 讨论(0)
提交回复
热议问题