Elements of Programming Interviews in Scala (Chapter 9)

Problem 9.1 - Balanced Binary Tree

Given a binary tree, figure out if it is balanced.

 
object App {
  def main(args: Array[String]): Unit = {
    test()
  }

  def isBalanced(tree: Node): Boolean = {
    math.abs(maxDepth(tree.left) - maxDepth(tree.right)) <= 1
  }

  def maxDepth(tree: Option[Node]): Int = {
    tree match {
      case None => -1 
      case Some(tree) => 1 + math.max(maxDepth(tree.left),maxDepth(tree.right))
    } 
  }
 
  def test(): Unit = {
    /*
     * 0     r 
     *      / \
     * 1 l .   . r
     */     
    val l = Node(None, None) 
    val r = Node(None, None)
    val tree = Node(Some(l),Some(r))
    assert(maxDepth(Some(tree)) == 1) 

    /**
     * 0         r
     *          /
     * 1    ll . 
     *        /
     * 2   l .        
     */
    val ll = Node(Some(l), None)
    val tree2 = Node(Some(ll), None) 
    assert(maxDepth(Some(tree2)) == 2)

    /**
     * 0      r 
     *       / \ 
     * 1  l .   . rrr
     *           \ 
     * 2          . rr
     *           / \
     * 3      l .   . r
     */ 
    val rr = Node(Some(l), Some(r))
    val rrr = Node(None, Some(rr))
    val tree3 = Node(Some(l),Some(rrr))
    assert(maxDepth(Some(tree3)) == 3)

    /**
     * 0        r 
     *         / \ 
     * 1  ll .   . rrr
     *       /     \ 
     * 2  l .       . rr
     *             / \
     * 3        l .   . r
     */ 
    val tree4 = Node(Some(ll), Some(rrr))
    assert(maxDepth(Some(tree4)) == 3)

    assert(isBalanced(tree))
    assert(isBalanced(tree2) == false)
    assert(isBalanced(tree3) == false)
    assert(isBalanced(tree4))
  }
}

sealed trait Tree
case class Node(var left: Option[Node], var right: Option[Node]) extends Tree

Problem 9.2 - k balanced trees

Design an algorithm that finds nodes in a binary tree that meet the following condition: (1) The node itself is not k-balanced. (2) All its descendants are k-balanced.

object App {
  def main(args: Array[String]): Unit = {
    test()
  }

  def isKBalanced(tree: Option[Node], k: Int): Boolean = {
    tree match {
      case None => true 
      case Some(t) => math.abs(countNodes(t.left) - countNodes(t.right)) <= k 
    }
  }

  def allDescendantsKBalanced(tree: Option[Node], k: Int): Boolean = {
    tree match {
      case None => true 
      case Some(t) => 
        isKBalanced(t.left, k) && 
        isKBalanced(t.right, k) && 
        allDescendantsKBalanced(t.left, k) && 
        allDescendantsKBalanced(t.right,k)
    }
  }

  def countNodes(tree: Option[Node]): Int = {
    tree match {
      case None => 0  
      case Some(t) => 1 + countNodes(t.left) + countNodes(t.right) 
    }
  }

  def searchForNode(tree: Option[Node], k: Int): Unit = {
    tree match {
      case None => 
      case Some(t) => {
        if(!isKBalanced(tree, k) && allDescendantsKBalanced(tree, k)){
          println(s"Found node matching condition: ${t.label}")
        } else {
          searchForNode(t.left, k)
          searchForNode(t.right, k)
        }
      }  
    }
  }
 
  def test(): Unit = {
    /*
     * 0     r 
     *      / \
     * 1 l .   . r
     */     
    val l = Node(None, None,"l") 
    val r = Node(None, None,"r")
    val tree = Node(Some(l),Some(r),"tree")
    assert(countNodes(Some(tree)) == 3)
    assert(isKBalanced(Some(tree),1))
    /**
     * 0         r
     *          /
     * 1    ll . 
     *        /
     * 2   l .        
     */
    val ll = Node(Some(l), None, "ll")
    val tree2 = Node(Some(ll), None, "tree2") 
    assert(countNodes(Some(tree2)) == 3)
    assert(isKBalanced(Some(tree2),1) == false)
    /**
     * 0      r 
     *       / \ 
     * 1  l .   . rrr
     *           \ 
     * 2          . rr
     *           / \
     * 3      l .   . r
     */ 
    val rr = Node(Some(l), Some(r), "rr")
    val rrr = Node(None, Some(rr), "rrr")
    val tree3 = Node(Some(l),Some(rrr), "tree3")
    assert(countNodes(Some(tree3)) == 6)
    assert(isKBalanced(Some(tree3),1) == false)
    assert(isKBalanced(Some(rrr),3))
    assert(isKBalanced(Some(rrr),1) == false)
    assert(isKBalanced(Some(rr),1))

    /**
     * 0        r 
     *         / \ 
     * 1   ll .   . rrr
     *       /     \ 
     * 2  l .       . rr
     *             / \
     * 3        l .   . r
     */ 
    val tree4 = Node(Some(ll), Some(rrr), "tree4")
    assert(countNodes(Some(tree4)) == 7)
    assert(isKBalanced(Some(tree4),2))
    assert(isKBalanced(Some(tree4),1) == false)
    assert(allDescendantsKBalanced(Some(r), 1) == true)
    /**
     * 0         r 
     *          / \ 
     * 1   lll .   . rrr
     *        /     \ 
     * 2  l2 .       . rr
     *      / \     / \
     * 3   l   r l .   . r
     */ 
    val l2 = Node(Some(l), Some(r), "l2")
    val lll = Node(Some(l2), None, "lll")
    val tree5 = Node(Some(lll), Some(rrr), "tree5")
    assert(allDescendantsKBalanced(Some(rr),1))
    assert(allDescendantsKBalanced(Some(rrr),1))
    assert(allDescendantsKBalanced(Some(lll),1))
    assert(allDescendantsKBalanced(Some(tree5),1) == false) 

    searchForNode(Some(tree5), 1)
  }
}

sealed trait Tree
case class Node(var left: Option[Node], var right: Option[Node], label: String) extends Tree
Did you like that post? You can suscribe to the RSS feed .