0

How do I write the below code using immutables instead of var ?

Question:

There is a bi-directional graph with n vertices, where each vertex is labeled from 0 to n - 1 (inclusive). The edges in the graph are represented as a 2D integer array edges, where each edges[i] = [ui, vi] denotes a bi-directional edge between vertex ui and vertex vi. Every vertex pair is connected by at most one edge, and no vertex has an edge to itself.

You want to determine if there is a valid path that exists from vertex start to vertex end.

Given edges and the integers n, start, and end, return true if there is a valid path from start to end, or false otherwise.

Example 1:

Input: n = 3, edges = [[0,1],[1,2],[2,0]], start = 0, end = 2 Output: true Explanation: There are two paths from vertex 0 to vertex 2:

  • 0 → 1 → 2
  • 0 → 2
  • List item

Solution

package com.example

object Solution {

  var visited: Seq[Int] = Nil

  def validPath(n: Int, edges: Array[Array[Int]], start: Int, end: Int) : Boolean ={

    if(edges.length == 0)
      return true
    val finalMap = edges.foldLeft(Map.empty[Int, Seq[Int]]) { case(result, edge) =>
      val keyVal = result.getOrElse(edge(0) , Nil) :+ edge(1)
      val updatedMap = (result + (edge(0)-> keyVal ))
      val keyVal1 = updatedMap.getOrElse(edge(1) , Nil) :+ edge(0)
      (updatedMap + (edge(1)-> keyVal1 ))
    }
    helper(finalMap , end, start)
  }

  def helper(map:Map[Int, Seq[Int]],  end: Int, start: Int): Boolean = {

    println(visited)
    if(visited.contains(start)) {
      false
    }
    else {
     val resultList =  map.get(start)
      resultList match {
        case Some(l) =>  if (l.contains(end)) {
          true
        } else {
          l.foldLeft(false) {(a , b) =>
            visited = (visited :+ start).distinct
            a || helper(map, end, b)
          }
        }
        case None => false
      }
    }o
  }

  def main(args: Array[String]): Unit = {
//    val input =   Array(Array(3,12),Array(26,84),Array(10,43),Array(68,47),Array(33,10),Array(87,35),Array(41,96),Array(70,92),Array(38,31),Array(88,59),Array(7,30),Array(89,26),Array(95,25),Array(66,28),Array(14,24),Array(86,11),Array(83,65),Array(14,4),Array(67,7),Array(89,45),Array(52,73),Array(47,85),Array(86,53),Array(68,81),Array(43,68),Array(87,78),Array(94,49),Array(70,21),Array(11,82),Array(60,93),Array(22,32),Array(69,99),Array(7,1),Array(41,46),Array(73,94),Array(98,52),Array(68,0),Array(69,89),Array(37,72),Array(25,50),Array(72,78),Array(96,60),Array(73,95),Array(7,69),Array(97,19),Array(46,75),Array(8,38),Array(19,36),Array(64,41),Array(61,78),Array(97,14),Array(54,28),Array(6,18),Array(25,32),Array(34,77),Array(58,60),Array(17,63),Array(98,87),Array(13,76),Array(58,53),Array(81,74),Array(29,6),Array(37,5),Array(65,63),Array(89,56),Array(61,18),Array(23,34),Array(76,29),Array(73,76),Array(11,63),Array(98,0),Array(54,14),Array(63,7),Array(87,32),Array(79,57),Array(72,0),Array(94,16),Array(85,16),Array(12,91),Array(14,17),Array(30,45),Array(42,41),Array(82,69),Array(24,28),Array(31,59),Array(11,88),Array(41,89),Array(48,12),Array(92,76),Array(84,64),Array(19,64),Array(21,32),Array(30,19),Array(47,43),Array(45,27),Array(31,17),Array(53,36),Array(88,3),Array(83,7),Array(27,48),Array(13,6),Array(14,40),Array(90,28),Array(80,85),Array(29,79),Array(10,50),Array(56,86),Array(82,88),Array(11,99),Array(37,55),Array(62,2),Array(55,92),Array(51,53),Array(9,40),Array(65,97),Array(25,57),Array(7,96),Array(86,1),Array(39,93),Array(45,86),Array(40,90),Array(58,75),Array(99,86),Array(82,45),Array(5,81),Array(89,91),Array(15,83),Array(93,38),Array(3,93),Array(71,28),Array(11,97),Array(74,47),Array(64,96),Array(88,96),Array(4,99),Array(88,26),Array(0,55),Array(36,75),Array(26,24),Array(84,88),Array(58,40),Array(77,72),Array(58,48),Array(50,92),Array(62,68),Array(70,49),Array(41,71),Array(68,6),Array(64,91),Array(50,81),Array(35,44),Array(91,48),Array(21,37),Array(62,98),Array(64,26),Array(63,51),Array(77,55),Array(25,13),Array(60,41),Array(87,79),Array(75,17),Array(61,95),Array(30,82),Array(47,79),Array(28,7),Array(92,95),Array(91,59),Array(94,85),Array(24,65),Array(91,31),Array(3,9),Array(59,58),Array(70,43),Array(95,13),Array(30,96),Array(51,9),Array(16,70),Array(29,94),Array(37,22),Array(35,79),Array(14,90),Array(75,9),Array(2,57),Array(81,80),Array(61,87),Array(69,88),Array(98,79),Array(18,70),Array(82,19),Array(36,27),Array(49,62),Array(67,75),Array(62,77),Array(83,96),Array(92,37),Array(95,22),Array(46,97),Array(35,0),Array(44,79),Array(82,89),Array(68,94),Array(96,31),Array(92,34),Array(25,0),Array(46,36),Array(38,84),Array(21,0),Array(0,80),Array(72,44),Array(56,97),Array(86,26),Array(94,57),Array(25,6),Array(81,13),Array(66,63),Array(57,5),Array(72,49),Array(46,86),Array(95,16),Array(95,37),Array(14,89),Array(44,22),Array(60,39),Array(37,47),Array(58,86),Array(89,96),Array(38,83),Array(51,91),Array(72,70),Array(14,82),Array(60,30),Array(58,39),Array(57,22),Array(95,70),Array(44,76),Array(5,68),Array(15,69),Array(33,61),Array(81,32),Array(21,68),Array(73,20),Array(22,72),Array(83,8),Array(15,54),Array(93,42),Array(68,95),Array(55,72),Array(33,92),Array(5,49),Array(17,96),Array(44,77),Array(24,53),Array(2,98),Array(33,81),Array(32,43),Array(20,16),Array(67,84),Array(98,35),Array(58,11),Array(72,5),Array(3,59),Array(78,79),Array(6,0),Array(26,71),Array(96,97),Array(18,92),Array(1,36),Array(78,0),Array(63,15),Array(20,43),Array(32,73),Array(37,76),Array(73,16),Array(76,23),Array(50,44),Array(68,2),Array(14,86),Array(69,65),Array(95,98),Array(53,64),Array(6,76),Array(7,11),Array(14,84),Array(62,50),Array(83,58),Array(78,92),Array(37,0),Array(13,55),Array(12,86),Array(11,59),Array(41,86),Array(27,26),Array(94,43),Array(20,78),Array(0,73),Array(58,90),Array(69,36),Array(62,34),Array(65,26),Array(32,85))
   val input = Array(Array(0,4))
    val result  = Solution.validPath(5, input,0,4)
    println(result)

  }



}

Krishhna
  • 21
  • 2

3 Answers3

1

This works, I think (very limited testing), and has no var or mutable collection.

It doesn't do a depth-first-search, as your question is tagged, but from the problem description that doesn't appear to be a requirement.

import scala.collection.immutable.IntMap

def validPath(edgCnt: Int
             ,edges: Array[Array[Int]]
             ,start: Int, end: Int): Boolean = {
  val nxtSet = 
    edges.head
         .indices
         .map(x => IntMap.from(edges.groupBy(_(x)).map{
                           case (k,v) => k -> v.flatten.toSet}))
         .reduce(_.unionWith(_, (k,a,b) => (a++b) - k))

  def loop(from:Set[Int], to:Set[Int], open:Set[Int]):Boolean =
    from.exists(to) || to.nonEmpty &&
      loop(to, from.flatMap(nxtSet).filter(open), open diff to)

  loop(Set(start), Set(end), Set.range(0,edgCnt) - start)
}

I'm using IntMap here only because it has the handy unionWith() method. Don't know why that isn't available on other collections.

jwvh
  • 50,871
  • 7
  • 38
  • 64
0

I personally prefer to model graphs as a Map[Node, NonEmptyList[Node]] where key-values pairs represent the edges.

With that representation searching for the path is very simple:

import cats.data.NonEmptyList

type Graph[A] = Map[A, NonEmptyList[A]]

object findPath {
  private sealed trait SearchState
  private final case object Initial extends SearchState
  private final case object FoundStart extends SearchState
  private final case object FoundEnd extends SearchState
  

  def apply[A](graph: Graph[A])(start: A, end: A): Boolean = {
    def newState(currentState: SearchState, nextElem: A): SearchState =
      (currentState, nextElem) match {
        case (Initial, `start`) => FoundStart
        case (FoundStart, `end`) => FoundEnd
        case _ => currentState
      }
    
    @annotation.tailrec
    def loop(remainingSteps: List[(SearchState, A, Set[A])]): Boolean =
      remainingSteps match {
        case (currentState, nextElem, visited) :: tail =>
          newState(currentState, nextElem) match {
            case FoundEnd =>
              true
            
            case newState =>
              val newVisited = visited + nextElem
              val newSteps =
                graph
                  .get(key = nextElem)
                  .fold(ifEmpty = List.empty[A])(_.toList)
                  .collect {
                    case a if (!newVisited.contains(a)) =>
                      (newState, a, newVisited)
                  }
            
              loop(newSteps reverse_::: tail)
          }
          
        
        case Nil =>
          false
      }
    
    loop(
      remainingSteps = graph.keysIterator.map(a => (Initial, a, Set.empty[A])).toList
    )
  }
}

How to create such Map from the current input is left as an exercise for the reader


You can see the code running here.

0

Here is an immutable solution that also satisfies applying bfs

object Solution {
  def validPath(n: Int, edges: Array[Array[Int]], start: Int, end: Int): Boolean = {
    if(start == end) true
    else if(edges.isEmpty) false
    else bfs(build(edges), start, end)
  }


  import scala.collection.immutable.Queue
  def bfs(map: Map[Int, List[Int]], start: Int, end: Int): Boolean = {
    def go(queue: Queue[Int], seen: Set[Int]): (Queue[Int], Set[Int]) = {
      if (queue.isEmpty) (queue, seen)
      else {
        val (node, remQueue) = queue.dequeue
        val updateSet = seen + node
        val (q, s) = map(node).foldLeft(remQueue, updateSet) {
          case ((q, s), n) =>
            val q2 = if (!s.contains(n)) q.enqueue(n) else q
            (q2, s)
        }
        go(q, s)
      }
    }
    val (_, seen) = go(Queue.empty[Int].enqueue(start), Set.empty[Int])

    if(seen.contains(start) && seen.contains(end)) true else false
  }


  def build(edges: Array[Array[Int]]): Map[Int, List[Int]] = {
    edges.foldLeft(Map[Int, List[Int]]()) { (acc, edge) => 
      appendMap(appendMap(acc, edge(1), edge(0)), edge(0), edge(1))
    }
}


  def appendMap(map: Map[Int, List[Int]], k: Int, v: Int): Map[Int, List[Int]] = {
    map.get(k) match {
      case Some(list) => 
        val r = list :+ v
        map + (k -> r)
      case None => map + (k -> List(v))
    }
  }
             
}

It pass on leetcode too :)