I have written two programs to find out a loopy path in a directed graphs.
The first version is a pure functional recursive solution, but its complexity is exponential. The second version following it achieves a linear complexity, but it doesn't look perfect either.
def GetACycle(start: String, maps: Map[String, List[String]]): List[String] = {
def explore(node: String, visits: List[String], steps: Int): List[String] = {
println(List.fill(steps)("\t").mkString + node)
if (visits.contains(node)) (visits.+:(node)).reverse
else {
if (maps(node).isEmpty) Nil
else {
val id = maps(node).indexWhere(x => !explore(x, visits.+:(node), steps + 1).isEmpty)
if (id.!=(-1))
explore(maps(node)(id), visits.+:(node), steps + 1)
else
Nil
}
}
}
explore(start, List(), 0)
}
The second version uses mutable variables, the "visits" and "path", though it achieves a linear complexity in terms of number of visited nodes.
Would it be possible to achieve such a linear complexity in this situation while using a pure functional recursion without any mutable variables?
def GetACycle2(start: String, maps: Map[String, List[String]]): List[String] = {
val nodes = maps.:\(Set[String]())((item, set) =>
item._2.:\(set)(((i, set) => set.+(i))).+(item._1))
val pairs = nodes.toList.zip(List.fill(nodes.size)(false))
var visits = pairs.toMap
var path = List[String]()
def explore(node: String, steps: Int): Boolean = {
println(List.fill(steps)("\t").mkString + node)
path = path.+:(node)
if (visits(node)) { visits = visits.updated(node, true); true }
else {
visits = visits.updated(node, true)
if (maps(node).isEmpty) false
else {
maps(node).exists( x => explore(x, steps+1))
}
}
}
explore(start, 0)
path
}
path = path.+:(node)
is very bad. Add values to the front of a list, and reverse it when you are finished. \$\endgroup\$