-
-
Save dz-s/71e01e9bf814cce732b0b3377e504034 to your computer and use it in GitHub Desktop.
Scala binary tree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 | |
* | |
* *********************** | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment