Skip to content

Commit

Permalink
Refactor some binary tree problems
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhijit Sarkar committed Jan 13, 2025
1 parent a503125 commit 9225dcb
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 80 deletions.
13 changes: 6 additions & 7 deletions bintree/src/P61A.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import Tree.*

object P61A:
extension [A](t: Tree[A])
def leafList: List[A] =
def loop(tree: Tree[A], leaves: List[A]): List[A] =
tree match
case Empty => leaves
case Node(value, Empty, Empty) => value :: leaves
case Node(_, left, right) => loop(left, loop(right, leaves))
private def loop(leaves: List[A]): List[A] =
t match
case Empty => leaves
case Node(value, Empty, Empty) => value :: leaves
case Node(_, left, right) => left.loop(right.loop(leaves))

loop(t, Nil)
def leafList: List[A] = loop(Nil)
13 changes: 6 additions & 7 deletions bintree/src/P62.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import Tree.*

object P62:
extension [A](t: Tree[A])
def internalList: List[A] =
def loop(tree: Tree[A], leaves: List[A]): List[A] =
tree match
case Empty => leaves
case Node(_, Empty, Empty) => leaves
case Node(value, left, right) => loop(left, value :: loop(right, leaves))
private def loop(leaves: List[A]): List[A] =
t match
case Empty => leaves
case Node(_, Empty, Empty) => leaves
case Node(value, left, right) => left.loop(value :: right.loop(leaves))

loop(t, Nil)
def internalList: List[A] = loop(Nil)
14 changes: 7 additions & 7 deletions bintree/src/P62B.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import Tree.*

object P62B:
extension [A](t: Tree[A])
def atLevel(level: Int): List[A] =
def loop(tree: Tree[A], currLevel: Int, acc: List[A]): List[A] =
tree match
case Node(value, _, _) if currLevel == level => value :: acc
case Node(_, left, right) => loop(left, currLevel + 1, loop(right, currLevel + 1, acc))
case Empty => acc
private def loop(level: Int, currLevel: Int, acc: List[A]): List[A] =
t match
case Node(value, _, _) if currLevel == level => value :: acc
case Node(_, left, right) =>
left.loop(level, currLevel + 1, right.loop(level, currLevel + 1, acc))
case Empty => acc

loop(t, 1, Nil)
def atLevel(level: Int): List[A] = loop(level, 1, Nil)
21 changes: 10 additions & 11 deletions bintree/src/P64.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@ object P64:
type AnnotatedTree[A] = Tree[(A, Pos)]

extension [A](t: Tree[A])
def layoutBinaryTree: AnnotatedTree[A] =
def loop[A](pos: Int, depth: Int, tree: Tree[A]): (AnnotatedTree[A], Int) =
tree match
case Empty => (Empty, 0)
case Node(value, l, r) =>
val (left, lSize) = loop(pos, depth + 1, l)
val pos1 = lSize + pos + 1
val (right, rSize) = loop(pos1, depth + 1, r)
val node = Node((value, (pos1, depth)), left, right)
(node, lSize + rSize + 1)
private def loop(pos: Int, depth: Int): (AnnotatedTree[A], Int) =
t match
case Empty => (Empty, 0)
case Node(value, l, r) =>
val (left, lSize) = l.loop(pos, depth + 1)
val pos1 = lSize + pos + 1
val (right, rSize) = r.loop(pos1, depth + 1)
val node = Node((value, (pos1, depth)), left, right)
(node, lSize + rSize + 1)

loop(0, 1, t)._1
def layoutBinaryTree: AnnotatedTree[A] = loop(0, 1)._1
25 changes: 12 additions & 13 deletions bintree/src/P65.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,16 @@ object P65:
case Empty => -1
case Node(_, left, right) => 1 + math.max(left.height, right.height)

def layoutBinaryTree2: AnnotatedTree[A] =
def loop[A](pos: Int, depth: Int, height: Int, tree: Tree[A]): (AnnotatedTree[A], Int) =
tree match
case Empty => (Empty, 0)
case Node(value, l, r) =>
val d1 = depth + 1
val h1 = height / 2
val (left, lPos) = loop(pos - height, d1, h1, l)
val pos1 = if lPos > 0 then lPos + height else math.max(pos, 1)
val (right, _) = loop(pos1 + height, d1, h1, l)
val node = Node((value, (pos1, depth)), left, right)
(node, pos1)
private def loop(pos: Int, depth: Int, height: Int): (AnnotatedTree[A], Int) =
t match
case Empty => (Empty, 0)
case Node(value, l, r) =>
val d1 = depth + 1
val h1 = height / 2
val (left, lPos) = l.loop(pos - height, d1, h1)
val pos1 = if lPos > 0 then lPos + height else math.max(pos, 1)
val (right, _) = r.loop(pos1 + height, d1, h1)
val node = Node((value, (pos1, depth)), left, right)
(node, pos1)

loop(1, 1, t.height * 2, t)._1
def layoutBinaryTree2: AnnotatedTree[A] = loop(1, 1, t.height * 2)._1
22 changes: 22 additions & 0 deletions bintree/src/P66.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package bintree

// P66 (***) Layout a binary tree (3).
// Yet another layout strategy is shown in the illustration opposite.
// The method yields a very compact layout while maintaining a certain symmetry in every node.
// Find out the rules and write the corresponding method.
// Hint: Consider the horizontal distance between a node and its successor nodes.
// How tight can you pack together two subtrees to construct the combined binary tree?
// Use the same conventions as in problem P64 and P65.
//
// scala> Node('a', Node('b', End, Node('c')), Node('d')).layoutBinaryTree3
// res0: PositionedNode[Char] = T[2,1]('a T[1,2]('b . T[2,3]('c . .)) T[3,2]('d . .))
/*
ANSWER: ???
*/
object P66:
type Pos = (Int, Int)
type AnnotatedTree[A] = Tree[(A, Pos)]

extension [A](t: Tree[A])

def layoutBinaryTree3: AnnotatedTree[A] = ???
29 changes: 11 additions & 18 deletions bintree/test/src/P64Spec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import scala.language.implicitConversions
import P64.layoutBinaryTree

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers.shouldBe

class P64Spec extends AnyFunSpec:
it("layout a binary tree (1)"):
Expand All @@ -13,24 +14,16 @@ class P64Spec extends AnyFunSpec:
'a', 'h', None, None, None, 's',
None, None, 'g', None, 'q', None, 'e'
)

val ys: Array[Option[(Char, (Int, Int))]] = (
('n', (8, 1)),
('k', (6, 2)), ('u', (12, 2)),
('c', (2, 3)), ('m', (7, 3)), ('p', (9, 3)), None,
('a', (1, 4)), ('h', (5, 4)), None, None, None, ('s', (11, 4)),
None, None, ('g', (4, 5)), None, ('q', (10, 5)), None,
('e', (3, 6))
)
// format: on
Tree
.fromArray(xs)
.layoutBinaryTree
.levelOrder
.zipAll(xs, None, None)
.forall:
case (None, None) => true
case (x, y) =>
x.zip(y) match
case Some((a, b)) if a._1 == b =>
// format: off
Set(
('n', (8, 1)), ('k', (6, 2)), ('u', (12, 2)), ('c', (2, 3)),
('m', (7, 3)), ('p', (9, 3)), ('a', (1, 4)), ('m', (7, 3)),
('h', (5, 4)), ('s', (11, 4)), ('g', (4, 5)), ('q', (10, 5)),
('e', (3, 6))
)
// format: on
.contains(a)
case _ => false
.layoutBinaryTree shouldBe (Tree.fromArray(ys))
26 changes: 9 additions & 17 deletions bintree/test/src/P65Spec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import scala.language.implicitConversions
import P65.layoutBinaryTree2

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers.shouldBe

class P65Spec extends AnyFunSpec:
it("layout a binary tree (2)"):
Expand All @@ -12,24 +13,15 @@ class P65Spec extends AnyFunSpec:
'n', 'k', 'u', 'c', 'm', 'p',
None, 'a', 'e', None, None,
None, 'q', None, None, 'd', 'g'
)
val ys: Array[Option[(Char, (Int, Int))]] = (
('n', (15, 1)),
('k', (7, 2)), ('u', (23, 2)),
('c', (3, 3)), ('m', (11, 3)), ('p', (19, 3)), None,
('a', (1, 4)), ('e', (5, 4)), None, None, None, ('q', (21, 4)),
None, None, ('d', (4, 5)), ('g', (6, 5))
)
// format: on
Tree
.fromArray(xs)
.layoutBinaryTree2
.levelOrder
.zipAll(xs, None, None)
.forall:
case (None, None) => true
case (x, y) =>
x.zip(y) match
case Some((a, b)) if a._1 == b =>
// format: off
Set(
('n', (15, 1)), ('k', (7, 2)), ('u', (23, 2)), ('c', (3, 3)),
('m', (11, 3)), ('p', (19, 3)), ('a', (1, 4)), ('e', (5, 4)),
('q', (21, 4)), ('d', (4, 5)), ('g', (6, 5)),
)
// format: on
.contains(a)
case _ => false
.layoutBinaryTree2 shouldBe (Tree.fromArray(ys))
29 changes: 29 additions & 0 deletions bintree/test/src/P66Spec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package bintree

import scala.language.implicitConversions
import P66.layoutBinaryTree3

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers.shouldBe
import org.scalatest.Ignore

@Ignore
class P66Spec extends AnyFunSpec:
it("layout a binary tree (3)"):
// format: off
val xs: Array[Option[Char]] = (
'n', 'k', 'u', 'c', 'm', 'p',
None, 'a', 'e', None, None,
None, 'q', None, None, 'd', 'g'
)
val ys: Array[Option[(Char, (Int, Int))]] = (
('n', (5, 1)),
('k', (3, 2)), ('u', (7, 2)),
('c', (2, 3)), ('m', (4, 3)), ('p', (6, 3)), None,
('a', (1, 4)), ('e', (3, 4)), None, None, None, ('q', (7, 4)),
None, None, ('d', (2, 5)), ('g', (4, 5))
)
// format: on
Tree
.fromArray(xs)
.layoutBinaryTree3 shouldBe (Tree.fromArray(ys))

0 comments on commit 9225dcb

Please sign in to comment.