2

I wrote a function in Scala to find out and return a loopy path in a directed graph. The program is as followed, one of the arguments is a graph presented in an adjacent list, and the other is a start node. It returns a pair including a loopy path by a list of nodes.

I wonder there are more elegant ways to do so. Please share your thoughts if you would like to. Thanks.

  def GetACycle(start: String, maps: Map[String, List[String]]): (Boolean, List[String]) = {
    def explore(node: String, visits: List[String]): (Boolean, List[String]) = {
      if (visits.contains(node)) (true, (visits.+:(node)).reverse)
      else {
        if (maps(node).isEmpty) (false, List())
        else {
          val id = maps(node).indexWhere(x => explore(x, visits.+:(node))._1)
          if (id.!=(-1))
            explore(maps(node)(id), visits.+:(node))
          else
            (false, List())
        }
      }
    }
    explore(start, List())
  }

I felt I had to use the indexWhere in this situation, but I suppose it would have other ways to do that.

Qi Qi
  • 279
  • 1
  • 2
  • 7
  • 3
    I think http://codereview.stackexchange.com/ is a better place for such kind of questions. – 4e6 Dec 10 '12 at 05:25
  • I just posted a more generic FP immutable answer on a related StackOverflow question: http://stackoverflow.com/a/36144158/501113 – chaotic3quilibrium Mar 22 '16 at 00:42

1 Answers1

1

You should use an array to check if you have already visited a node and not visits.contains(node), it would give you the answer in constant time instead of linear time.

The overall complexity of your algorithm is exponential. For instance, if you run your algorithm on this graph:

0 -> 1, 2, ..., n
1 -> 2, ..., n
...

where there are n nodes and there are edges from i to j iff i<j then the node i will be explored 2^i times.

Again you can solve this problem using an array (one array for all nodes) to ensure that each node is explored at most one time.

Thomash
  • 6,339
  • 1
  • 30
  • 50
  • Thanks, @Thomash. Using array is a good suggestion. But this algorithm won't go exponential because it runs in depth first search and whenever it comes across a loopy path it will immediately return it and exit, it won't explore others. The .indexWhere(p=>Boolean) will stop once its predicate is true. – Qi Qi Dec 10 '12 at 14:04
  • If you don't believe me when I say it is exponential, test your function on my example with n = 100. – Thomash Dec 10 '12 at 15:33
  • I think the program travels a graph using depth first search(DFS), isn't it? DFS's complexity is O(n), reference to http://en.wikipedia.org/wiki/Depth-first_search. – Qi Qi Dec 10 '12 at 20:13
  • have you tested your code with my example ? DFS requires that you stop when you reach an already visited node and you don't do that. – Thomash Dec 10 '12 at 20:56
  • I haven't run out the example. But in the program above, it does stop exploring and returns when it comes across a visited node, isn't it? As shown in the line, "if (visits.contains(node)) (true, (visits.+:(node)).reverse)". – Qi Qi Dec 11 '12 at 01:04
  • It does stop exploring only when it comes across visited *on this branch*. If the node have been visited on another branch, you don't stop. – Thomash Dec 11 '12 at 07:40
  • I think you are right about this. But I don't want to create a global variable to mark which node has been visited, which is not a functional programming way. Do you have any ideas about it? Thanks. – Qi Qi Dec 11 '12 at 14:39
  • If you don't want an array because "it's not functional", you can use a set that you pass to your recursive function. – Thomash Dec 11 '12 at 14:51
  • Scala has an array data structure, and it's facile. I think I have to leave the visited set defined outside the explore recursive function, so that it would be globally available to all exploring processes. Thank you, Thomash. – Qi Qi Dec 11 '12 at 16:31
  • Hi, Thomash, this is a good discussion. I did a test and printed recursively visited nodes in a hierarchical levels. The complexity (number of visited nodes) of the program above turns out to be c*2^n, c is a constant, and the n is the length of the first discovered loopy path. :) – Qi Qi Dec 11 '12 at 19:38
  • By the way, I have another question post here, http://codereview.stackexchange.com/questions/19504/scala-how-to-break-this-curse-by-a-pure-functional-recursive-function, for an extensive discussion. – Qi Qi Dec 11 '12 at 21:53