Functional Programming
  • Preface
  • Getting Started
  • Sorting
    • Bubble Sort
    • Merge Sort
    • Insertion Sort
  • Stacks
    • Stack
  • Queues
    • Queue
    • Binary Heap (Priority Queue)
  • Trees
    • Binary Tree (Unbalanced)
    • Prefix Trie
  • Graphs
    • Undirected
    • Directed
    • Edge Weighted
  • Dynamic Programming
    • Knapsack
    • Longest Common Substring
    • Staircase
  • Leetcode/InterviewBit
    • 2 Sum [Easy]
    • Longest Valid Parentheses [Medium-Hard]
    • Kth Smallest Element [Medium]
    • Max Profit [2 variations: 1st Easy 2nd Hard]
    • Pretty Print 2D Matrix [Easy]
    • Max Contiguous Subarray (Kadane's) [Medium (just cause its tricky)]
    • Permutations [Medium]
    • Next Permutation [Medium]
Powered by GitBook
On this page

Was this helpful?

  1. Graphs

Directed

Algorithms

Operation

Complexity

Breadth First Search

Depth First Search

Topological Sort

Kosaraju's Strongly Connected Components

sealed abstract class DirectedGraph[A](m: Map[A, Set[A]]) {

  /** Return first cycle detected, if one exists */
  def cycle(start: A): (List[A], Option[List[A]]) = {

    def dfs(
      stack: Chain[A],
      stackContains: Set[A],
      visited: LinkedHashSet[A]
    ): (List[A], Option[List[A]]) = 
      stack.headOption.fold(
        visited.toList -> (None: Option[List[A]])
      ) { top =>
        val (visited1, notVisited) =
          m.get(top)
            .fold(Set.empty[A] -> Set.empty[A])(_.partition(visited.contains))
        visited1
          .find(stackContains.contains)
          .fold(
            notVisited.foldLeft(List.empty[A] -> (None: Option[List[A]])) {
              (_, elem) =>
                dfs(
                  stack.prepend(elem),
                  stackContains + elem,
                  visited + elem
                )
            }
          )(cycleEnd =>
            visited.toList -> Some(stack.prepend(cycleEnd).toList))

      }

    dfs(Chain.one(start), Set.empty[A], mutable.LinkedHashSet.empty)

  }

  /** Post, reverse post (topological sort), pre traversals.
   *  Not stack safe
   * */
  def dfsTraversalsUnsafe[A](start: A): (LinkedHashSet[A], List[A], LinkedHashSet[A]) = {

    def dfs(
        curr: A,
        post: LinkedHashSet[A],
        reversePost: List[A],
        pre: LinkedHashSet[A]
    ): (LinkedHashSet[A], List[A], LinkedHashSet[A]) = {
      val preWithCurr = pre + curr
      val (post1, reversePost1, pre1) = m
        .get(curr)
        .fold((post, reversePost, preWithCurr))(nexts =>
          nexts.foldLeft((post, reversePost, preWithCur)) {
            case ((post, postStack, pre), next) =>
              if (pre.contains(next)) (post, postStack, pre)
              else dfs(next, post, postStack, pre)
        })
      (post1 + curr,
       if (post.contains(curr)) reversePost1 else curr :: reversePost1,
       pre1)
    }
  
    dfs(start, LinkedHashSet.empty, List.empty, LinkedHashSet.empty)
  
  }

  /** Kosaraju's algorithm for determining strongly connected
    * components in a directed graph. A graph is strongly connected if 
    * every vertex is reachable from every other vertex
    * */
  def stronglyConnectedComponents(start: A): Set[Set[A]] = {

    def dfs(
      stack: Chain[A],
      visited: LinkedHashSet[A],
      m: Map[A, Set[A]]
    ): LinkedHashSet[A] =
      stack.uncons.fold(
        visited
      ) {
        case (top, tail) =>
          val notVisited =
            m.get(top)
              .fold(Chain.empty[A])(nexts =>
                Chain.fromSeq(nexts.filterNot(visited.contains).toList))

          dfs(
            notVisited ++ tail,
            visited + top,
            m
          )
      }

    val stack = Chain.fromSeq(
      dfs(
        Chain.one(start),
        LinkedHashSet.empty,
        m
      ).toList)

    val complimentGraph =
      m.foldLeft(Map.empty[A, Set[A]]) {
        case (m, (k, vs)) =>
          vs.foldLeft(m) { (m, v) =>
            m.get(v).fold(m + (v -> Set(k)))(ks => m.updated(v, ks + k))
          }
      }

    stack
      .foldLeft(Set.empty[Set[A]] -> LinkedHashSet.empty[A]) {
        case ((out, visited), k) =>
          if (visited.contains(k)) out -> visited
          else {
            val res = dfs(
              Chain.one(k),
              visited,
              complimentGraph
            )
            (out + (res.toSet -- visited.toSet)) -> (visited ++ res)
          }
      }
      ._1

  }

}
PreviousUndirectedNextEdge Weighted

Last updated 6 years ago

Was this helpful?

O(v+e)O(v + e)O(v+e)
O(v+e)O(v + e)O(v+e)
O(v+e)O(v + e)O(v+e)
O(v+e)O(v + e)O(v+e)