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. Trees

Binary Tree (Unbalanced)

Performance

Operation

Complexity

Find

Delete

Insert

import cats.data.Chain
import cats.Order

sealed trait BinaryTree[+A]
case class Node[+A](l: BinaryTree[A], v: A, r: BinaryTree[A])
  extends BinaryTree[A]
case object Leaf extends BinaryTree[Nothing]

object BinaryTree {

  def insert[A: Order](t: BinaryTree[A], v: A): BinaryTree[A] = t match {
    case Leaf => Node(Leaf, v, Leaf)
    case n @ Node(l, x, r) =>
      if (v < x) Node(insert(l, v), x, r)
      else if (v > x) Node(l, x, insert(r, v))
      else n
  }

  @tailrec
  def rootedAt[A: Order](t: BinaryTree[A], v: A): BinaryTree[A] = t match {
    case Leaf => Leaf
    case n @ Node(l, x, r) =>
      if (v == x) n
      else if (v > x) rootedAt(r, v)
      else rootedAt(l, v)
  }

  def find[A: Order](t: BinaryTree[A], v: A): Option[A] =
    rootedAt(t, v) match {
      case Leaf          => None
      case Node(_, x, _) => Some(x)
    }

  def delete[A: Order](t: BinaryTree[A], v: A): BinaryTree[A] = t match {
    case Leaf =>
    case n @ Node(l, x, r) =>
      if (l == Leaf) r
      else if (r == Leaf) l
      else {
        if (v < x) Node(delete(l, v), x, r)
        else if (v > x) Node(l, x, delete(r, v))
        else deleteRoot(n)
      }
  }

  @tailrec
  def leftistElem[A](t: BinaryTree[A]): Option[A] = t match {
    case Leaf             => None
    case Node(Leaf, x, _) => Some(x)
    case Node(l, _, _)    => leftistElem(l)
  }

  def deleteRoot[A: Order](t: BinaryTree[A]): BinaryTree[A] = t match {
    case Node(Leaf, _, r) => r
    case Node(l, _, Leaf) => l
    case Node(l, _, r) =>
      val le = leftistElem(r).get
      Node(l, le, delete(r, le))
  }

  def successor[A: Order](t: BinaryTree[A], v: A): Option[A] =
    rootedAt(t, v) match {
      case Leaf          => None
      case Node(_, _, r) => leftistElem(r)
    }

  def predecessor[A: Order](t: BinaryTree[A], v: A): Option[A] =
    rootedAt(t, v) match {
      case Leaf => None
      case Node(l, _, _) =>
        @tailrec
        def go(t: BinaryTree[A]): Option[A] = t match {
          case Leaf             => None
          case Node(_, x, Leaf) => Some(x)
          case Node(_, _, r)    => go(r)
        }
        go(l)
    }

  def inorder[A: Order](t: BinaryTree[A]): Chain[A] = t match {
    case Leaf          => Chain.empty
    case Node(l, x, r) => inorder(l) ++ Chain(x) ++ inorder(r)
  }

  def preorder[A: Order](t: BinaryTree[A]): Chain[A] = t match {
    case Leaf          => Chain.empty
    case Node(l, x, r) => Chain(x) ++ preorder(l) ++ preorder(r)

  }

  def postorder[A: Order](t: BinaryTree[A]): Chain[A] = t match {
    case Leaf          => Chain.empty
    case Node(l, x, r) => postorder(l) ++ postorder(r) ++ Chain(x)
  }
}

PreviousTreesNextPrefix Trie

Last updated 6 years ago

Was this helpful?

NOTE: is constant time append and prepend ordered data structure

O(log(n)O(log(n)O(log(n)
O(log(n)O(log(n)O(log(n)
O(log(n)O(log(n)O(log(n)
Chain