/** * D Holbrook * * Code Club: PO1 * * (*) Define a binary tree data structure and related fundamental operations. * * Use whichever language features are the best fit (this will depend on the language you have selected). The following operations should be supported: * * Constructors * (bitree data left right) - Should return a binary tree containing data and the left and right children. * Accessors * (bitree-data t) - Should return the data contained by the tree. * (bitree-left t) - Should return the left child of the tree. * (bitree-right t) - Should return the right child of the tree. * Predicates * (bitree-leaf? t) - Should return true if the tree is a leaf (has null left and right children), false otherwise */ trait Tree[+A] { import scala.annotation.tailrec def value: Option[A] = this match { case n: Node[A] => Some(n.v) case l: Leaf[A] => Some(l.v) case Empty => None } def left: Option[Tree[A]] = this match { case n: Node[A] => Some(n.l) case l: Leaf[A] => None case Empty => None } def right: Option[Tree[A]] = this match { case n: Node[A] => Some(n.r) case l: Leaf[A] => None case Empty => None } /** * Represents a deferred evaluation of a node value */ private case class Eval[A](v: A) extends Tree[A] /** * represents common functionality of all traversal order folds */ @tailrec private def foldLoop[A, B](a: List[Tree[A]], z: B)(f: (B, A) => B)(o: (Node[A], List[Tree[A]]) => List[Tree[A]]): B = a match { case (n: Node[A]) :: tl => foldLoop(o(n, tl), z)(f)(o) // never directly evaluate nodes, function o will create new accumulator case (l: Leaf[A]) :: tl => foldLoop(tl, f(z, l.v))(f)(o) // always evaluate Leaf case (e: Eval[A]) :: tl => foldLoop(tl, f(z, e.v))(f)(o) // always evaluate Eval case Empty :: tl => foldLoop(tl, z)(f)(o) // ignore Empty case _ => z // will be Nil (empty list) } /** * fold with preorder traversal (root, left, right) * Tail Recursive Optimized * * F * / \ * B G * / \ \ * A D I * / \ / * C E H * * head evaluate accumulator * ---- -------- ----------- * | (F) * F | () | F::B::G::() * F | (F) | (B,G) * B | () | B::A::D::(G) * B | (B) | (A,D,G) * A | (A) | (D,G) * D | () | D::C::E::(G) * D | (D) | (C,E,G) * C | (C) | (E,G) * E | (E) | (G) * G | () | G::I::() * G | (G) | (I) * I | () | I::H::() * I | (I) | (H) * H | (H) | () * * result * F, B, A, D, C, E, G, I, H */ def foldPreorder[B](z: B)(f: (B, A) => B): B = { foldLoop(List(this), z)(f) { (n, tl) => Eval(n.v) :: n.l :: n.r :: tl } } /** * fold with inorder traversal (left, root, right) * tail recursive optimized * * F * / \ * B G * / \ \ * A D I * / \ / * C E H * * head evaluate accumulator * ---- -------- ----------- * | (F) * F | () | B::F::G::() * B | () | A::B::D::(F,G) * A | (A) | (B,D,F,G) * B | (B) | (D,F,G) * D | () | C::D::E::(F,G) * C | (C) | (D,E,F,G) * D | (D) | (E,F,G) * E | (E) | (F,G) * F | (F) | (G) * G | () | G::I::() * G | (G) | (I) * I | () | H::I::() * H | (H) | H * I | (I) | () * * result * A,B,C,D,E,F,G,H,I */ def foldInorder[B](z: B)(f: (B, A) => B): B = { foldLoop(List(this), z)(f) { (n, tl) => n.l :: Eval(n.v) :: n.r :: tl } } /** * fold with postorder traversal (left, right, root) * tail recursive optimized * * F * / \ * B G * / \ \ * A D I * / \ / * C E H * * head evaluate accumulator * ---- -------- ----------- * | (F) * F | () | B::G::F::() * B | () | A::D::(B,G,F) * A | (A) | (D,B,G,F) * D | () | C::E::D::(B,G,F) * C | (C) | (E,D,B,G,F) * E | (E) | (D,B,G,F) * D | (D) | (B,G,F) * B | (B) | (G,F) * G | () | I::G::(F) * I | () | H::I::(G,F) * H | (H) | (I,G,F) * I | (I) | (G,F) * G | (G) | (F) * F | (F) | () * * result * A,C,E,D,B,H,I,G,F */ def foldPostorder[B](z: B)(f: (B, A) => B): B = { foldLoop(List(this), z)(f) { (n, tl) => n.l :: n.r :: Eval(n.v) :: tl } } /** * fold with levelorder traversal * tail recursive optimized * * F * / \ * B G * / \ \ * A D I * / \ / * C E H * * head evaluate accumulator * ---- -------- ----------- * | (F) * F | () | (F::()) ::: (B,G) * F | (F) | (B,G) * B | () | (B::(G)) ::: (A,D) * B | (B) | (G,A,D) * G | () | (G::(A,D)) ::: (I) * G | (G) | (A,D,I) * A | (A) | (D,I) * D | () | (D::(I)) ::: (C,E) * D | (D) | (I,C,E) * I | () | (I::(C,E)) ::: (H) * I | (I) | (C,E,H) * C | (C) | (E,H) * E | (E) | (H) * H | (H) | () * * result * F, B, G, A, D, I, C, E, H */ def foldLevelorder[B](z: B)(f: (B, A) => B): B = { foldLoop(List(this), z)(f) { (n, tl) => (Eval(n.v) :: tl) ::: List(n.l, n.r) } } /** * calls foldInorder */ def fold[B](z: B)(f: (B, A) => B): B = foldInorder(z)(f) /** * P02 * (*) Count the number of nodes in a binary tree. */ def size: Int = fold(0) { (sum, v) => sum + 1 } /** * P03 * (*) Determine the height of a binary tree. * Definition: The height of a tree is the length of the path from the root to the deepest node in the tree. A (rooted) tree with only one node (the root) has a height of zero. */ def height: Int = { def loop(t: Tree[A]): Int = t match { case l: Leaf[A] => 1 case n: Node[A] => Seq(loop(n.left.get), loop(n.right.get)).max + 1 case _ => 0 } loop(this) - 1 } /** * P04 * (*) Count the number of leaves in a binary tree. */ def leafCount: Int = { @tailrec def loop(t: List[Tree[A]], z: Int): Int = t match { case (l: Leaf[A]) :: tl => loop(tl, z + 1) case (n: Node[A]) :: tl => loop(n.left.get :: n.right.get :: tl, z) case _ :: tl => loop(tl, z) case _ => z } loop(List(this), 0) } def toSeq: Seq[A] = fold(List[A]()) { (l, v) => v :: l } reverse def toSeqPreorder: Seq[A] = foldPreorder(List[A]()) { (l, v) => v :: l } reverse def toSeqInorder: Seq[A] = foldInorder(List[A]()) { (l, v) => v :: l } reverse def toSeqPostorder: Seq[A] = foldPostorder(List[A]()) { (l, v) => v :: l } reverse def toSeqLevelorder: Seq[A] = foldLevelorder(List[A]()) { (l, v) => v :: l } reverse /** * P05 * (**) Find the last element in a binary tree using pre/in/post/level order traversals. * Note: This is S-99 problem P01 converted from lists to binary trees. */ def lastPreorder = toSeqPreorder.last def lastInorder = toSeqInorder.last def lastPostorder = toSeqPostorder.last def lastLevelorder = toSeqLevelorder.last /** * P06 * (**) Find the last but one element in a binary tree using pre/in/post/level order traversals. * Note: This is S-99 problem P02 converted from lists to binary trees. */ def penultimatePreorder = toSeqPreorder.dropRight(1).last def penultimateInorder = toSeqInorder.dropRight(1).last def penultimatePostorder = toSeqPostorder.dropRight(1).last def penultimateLevelorder = toSeqLevelorder.dropRight(1).last /** * P07 * (**) Find the Nth element in a binary tree using pre/in/post/level order traversals. * By convention, the first element in the tree is element 0. * */ def nthPreorder(n: Int) = toSeqPreorder(n) def nthInorder(n: Int) = toSeqInorder(n) def nthPostorder(n: Int) = toSeqPostorder(n) def nthLevelorder(n: Int) = toSeqLevelorder(n) } case class Node[A](v: A, l: Tree[A], r: Tree[A]) extends Tree[A] case class Leaf[A](v: A) extends Tree[A] case object Empty extends Tree[Nothing] object Run extends App { val t: Tree[Symbol] = Node('F, Node('B, Leaf('A), Node('D, Leaf('C), Leaf('E))), Node('G, Empty, Node('I, Leaf('H), Empty))) println("tree: " + t) //print the value of b node navigating from root for { b <- t.left value <- b.value } println("B node: " + value) //print the value of e node navigating from root for { b <- t.left d <- b.right value <- d.value } println("D node: " + value) //no println() is executed for empty node chain for { b <- t.left d <- b.right e <- d.right x <- e.right value <- x.value } println("X node SHOUL NOT PRINT!: " + value) println("as seq: " + t.toSeq) println("count: " + t.size) assert(t.size == 9) println("height: " + t.height) assert(t.height == 3) println("leaft count: " + t.leafCount) assert(t.leafCount == 4) println("as seqPreorder: " + t.toSeqPreorder) println("as seqInorder: " + t.toSeqInorder) println("as seqPostorder: " + t.toSeqPostorder) println("as seqLevelorder: " + t.toSeqLevelorder) println("last preorder :" + t.lastPreorder) println("last inorder :" + t.lastInorder) println("last postorder :" + t.lastPostorder) println("last levelorder: " + t.lastLevelorder) println("nth preorder 5 : " + t.nthPreorder(5)) println("nth inorder 5 : " + t.nthInorder(5)) println("nth postorder 5 : " + t.nthPostorder(5)) println("nth levelorder 5 : " + t.nthLevelorder(5)) // /** * **** output ********* * * tree: Node('F,Node('B,Leaf('A),Node('D,Leaf('C),Leaf('E))),Node('G,Empty,Node('I,Leaf('H),Empty))) * B node: 'B * D node: 'D * as seq: List('A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I) * count: 9 * height: 3 * leaft count: 4 * as seqPreorder: List('F, 'B, 'A, 'D, 'C, 'E, 'G, 'I, 'H) * as seqInorder: List('A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I) * as seqPostorder: List('A, 'C, 'E, 'D, 'B, 'H, 'I, 'G, 'F) * as seqLevelorder: List('F, 'B, 'G, 'A, 'D, 'I, 'C, 'E, 'H) * last preorder :'H * last inorder :'I * last postorder :'F * last levelorder: List('F, 'B, 'G, 'A, 'D, 'I, 'C, 'E, 'H) * nth preorder 5 : 'C * nth inorder 5 : 'E * nth postorder 5 : 'B * nth levelorder 5 : 'D * * *********************** */ }