Skip to content

Commit

Permalink
Complete up to P59
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhijit Sarkar committed Jan 10, 2024
1 parent 61dcaaa commit 9092a6d
Show file tree
Hide file tree
Showing 15 changed files with 290 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ if (( no_test == 0 )); then
else
green='\033[1;32m'
no_color='\033[0m'
printf "Running tests in the package: %b%s%b\n" "$green" "$1" "$no_color"
printf "Running tests in packages matching: %b%s*%b\n" "$green" "$1" "$no_color"
sbt -warn "Test / testOnly $1*"
fi
fi
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@ P48 (**) Truth tables for logical expressions (3).

P54 Omitted; our tree representation will only allow well-formed trees.

P55 (**) Construct completely balanced binary trees.
[P55](src/main/scala/bintree/P55.scala) (**) Construct completely balanced binary trees.

P56 (**) Symmetric binary trees.
[P56](src/main/scala/bintree/P56.scala) (**) Symmetric binary trees.

P57 (**) Binary search trees (dictionaries).
[P57](src/main/scala/bintree/P57.scala) (**) Binary search trees (dictionaries).

P58 (**) Generate-and-test paradigm.
[P58](src/main/scala/bintree/P58.scala) (**) Generate-and-test paradigm.

P59 (**) Construct height-balanced binary trees.
[P59](src/main/scala/bintree/P59.scala) (**) Construct height-balanced binary trees.

P60 (**) Construct height-balanced binary trees with a given number of nodes.

Expand Down
45 changes: 45 additions & 0 deletions src/main/scala/bintree/P55.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package bintree

import Tree.*

// P55 (**) Construct completely balanced binary trees.
// In a completely balanced binary tree, the following property holds for
// every node: The number of nodes in its left subtree and the number of
// nodes in its right subtree are almost equal, which means their difference
// is not greater than one.
//
// Define an object named Tree. Write a function Tree.cBalanced to
// construct completely balanced binary trees for a given number of nodes.
// The function should generate all solutions. The function should take as
// parameters the number of nodes and a single value to put in all of them.
//
// scala> Tree.cBalanced(4, "x")
// res0: List(Node[String]) = List(T(x T(x . .) T(x . T(x . .))), T(x T(x . .) T(x T(x . .) .)), ...

object P55:
private def build[A](n: Int, x: A): List[(Tree[A], Int)] =
if n == 0
then Nil
else if n == 1
then List((singleton(x), 1))
else if n == 2
then
List(
(Node(x, singleton(x), Empty), 2),
(Node(x, Empty, singleton(x)), 2)
)
else
val k = (n - 1) / 2
for
(l, i) <- build(k, x)
(r, j) <- build(n - k - 1, x)
h = i + j + 1
xs =
if math.abs(i - j) == 1
then List((Node(x, r, l), h))
else Nil
y <- (Node(x, l, r), h) :: xs
yield y

def cBalanced[A](n: Int, x: A): List[Tree[A]] =
build(n, x).map(_._1)
29 changes: 29 additions & 0 deletions src/main/scala/bintree/P56.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package bintree

import Tree.*

// P56 (**) Symmetric binary trees.
// Let us call a binary tree symmetric if you can draw a vertical line
// through the root node and then the right subtree is the mirror image of
// the left subtree. Add an isSymmetric method to the Tree class to check
// whether a given binary tree is symmetric. Hint: Write an isMirrorOf
// method first to check whether one tree is the mirror image of another.
// We are only interested in the structure, not in the contents of the
// nodes.
//
// scala> Node('a', Node('b'), Node('c')).isSymmetric
// res0: Boolean = true

object P56:
private def isMirror[A](t1: Tree[A], t2: Tree[A]): Boolean =
(t1, t2) match
case (Empty, Empty) => true
case (Empty, _) => false
case (_, Empty) => false
case (Node(_, l1, r1), Node(_, l2, r2)) =>
isMirror(l1, r2) && isMirror(r1, l2)

extension [A](t: Tree[A])
def isSymmetric: Boolean = t match
case Empty => true
case Node(_, l, r) => isMirror(l, r)
52 changes: 52 additions & 0 deletions src/main/scala/bintree/P57.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package bintree

import Tree.*
import math.Ordered.orderingToOrdered

// P57 (**) Binary search trees (dictionaries).
// Write a function to add an element to a binary search tree.
//
// scala> End.addValue(2)
// res0: Node[Int] = T(2 . .)
//
// scala> res0.addValue(3)
// res1: Node[Int] = T(2 . T(3 . .))
//
// scala> res1.addValue(0)
// res2: Node[Int] = T(2 T(0 . .) T(3 . .))
//
// Hint: The abstract definition of addValue in Tree should be
// `def addValue[U >: T <% Ordered[U]](x: U): Tree[U]`. The `>: T` is
// because addValue's parameters need to be _contravariant_ in T.
// (Conceptually, we're adding nodes above existing nodes. In order for the
// subnodes to be of type T or any subtype, the upper nodes must be of type
// T or any supertype.) The `<% Ordered[U]` allows us to use the < operator
// on the values in the tree.
//
// Use that function to construct a binary tree from a list of integers.
//
// scala> Tree.fromList(List(3, 2, 5, 7, 1))
// res3: Node[Int] = T(3 T(2 T(1 . .) .) T(5 . T(7 . .)))
//
// Finally, use that function to test your solution to P56.
//
// scala> Tree.fromList(List(5, 3, 18, 1, 4, 12, 21)).isSymmetric
// res4: Boolean = true
//
// scala> Tree.fromList(List(3, 2, 5, 7, 4)).isSymmetric
// res5: Boolean = false

object P57:
extension [A](t: Tree[A])
// "covariant type A occurs in contravariant position".
// If we allow any subtype of A, then a list of cats
// may contain a dog!
// https://stackoverflow.com/a/43180701/839733
def addValue[B >: A](x: B)(using Ordering[B]): Tree[B] = t match
case Empty => singleton(x)
case Node(y, l, r) if x < y =>
val left = l.addValue(x)
Node(y, left, r)
case Node(y, l, r) =>
val right = r.addValue(x)
Node(y, l, right)
14 changes: 14 additions & 0 deletions src/main/scala/bintree/P58.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package bintree

import P56.*

// P58 (**) Generate-and-test paradigm.
// Apply the generate-and-test paradigm to construct all symmetric,
// completely balanced binary trees with a given number of nodes.
//
// scala> Tree.symmetricBalancedTrees(5, "x")
// res0: List[Node[String]] = List(T(x T(x . T(x . .)) T(x T(x . .) .)), T(x T(x T(x . .) .) T(x . T(x . .))))

object P58:
def symmetricBalancedTrees[A](n: Int, x: A): List[Tree[A]] =
P55.cBalanced(n, x).filter(_.isSymmetric)
39 changes: 39 additions & 0 deletions src/main/scala/bintree/P59.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package bintree

import Tree.*

// P59 (**) Construct height-balanced binary trees.
// In a height-balanced binary tree, the following property holds for every
// node: The height of its left subtree and the height of its right subtree
// are almost equal, which means their difference is not greater than one.
//
// Write a method Tree.hbalTrees to construct height-balanced binary trees
// for a given height with a supplied value for the nodes. The function
// should generate all solutions.
//
// scala> Tree.hbalTrees(3, "x")
// res0: List[Node[String]] = List(T(x T(x T(x . .) T(x . .)) T(x T(x . .) T(x . .))), T(x T(x T(x . .) T(x . .)) T(x T(x . .) .)), ...

object P59:
def hbalTrees[A](h: Int, x: A): List[Tree[A]] =
if h == 0
then Nil
else if h == 1
then List(singleton(x))
else if h == 2
then
List(
Node(x, singleton(x), Empty),
Node(x, Empty, singleton(x)),
Node(x, singleton(x), singleton(x))
)
else
for
a <- hbalTrees(h - 1, x)
b <- hbalTrees(h - 2, x)
c <- List(
Node(x, a, b),
Node(x, b, a),
Node(x, a, a)
)
yield c
11 changes: 11 additions & 0 deletions src/main/scala/bintree/Tree.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package bintree

enum Tree[+A]:
case Empty
case Node(value: A, left: Tree[A], right: Tree[A])

object Tree:
def empty[A]: Tree[A] = Empty

def singleton[A](x: A): Tree[A] =
Node(x, Empty, Empty)
2 changes: 1 addition & 1 deletion src/main/scala/list/P27.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ object P27:
case n :: next =>
for
xs <- P26.combinations(n, l)
xxs <- group(next, l.filterNot(x => xs.exists(_ == x)))
xxs <- group(next, l.filterNot(xs.contains))
yield xs :: xxs
29 changes: 29 additions & 0 deletions src/test/scala/bintree/P55Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package bintree

import munit.FunSuite
import Tree.*

class P55Suite extends FunSuite:
test("construct completely balanced binary trees"):
val data = List(
(0, Nil),
(1, List(singleton('x'))),
(2, List(Node('x', singleton('x'), Empty), Node('x', Empty, singleton('x')))),
(3, (List(Node('x', singleton('x'), singleton('x'))))),
(
4,
List(
Node('x', singleton('x'), Node('x', singleton('x'), Empty)),
Node('x', Node('x', singleton('x'), Empty), singleton('x')),
Node('x', singleton('x'), Node('x', Empty, singleton('x'))),
Node('x', Node('x', Empty, singleton('x')), singleton('x'))
)
)
)
data.foreach { (n, expected) =>
val obtained = P55.cBalanced(n, 'x')
assertEquals(obtained.size, expected.size)
obtained.foreach { tree =>
assert(expected.contains(tree), s"Tree: $tree")
}
}
13 changes: 13 additions & 0 deletions src/test/scala/bintree/P56Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package bintree

import munit.FunSuite
import Tree.*
import P56.*

class P56Suite extends FunSuite:
test("check whether a given binary tree is symmetric"):
val obtained = Node('a', singleton('b'), Empty).isSymmetric
assertEquals(obtained, false)

val obtained2 = Node('a', singleton('b'), singleton('c')).isSymmetric
assertEquals(obtained2, true)
16 changes: 16 additions & 0 deletions src/test/scala/bintree/P57Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package bintree

import munit.FunSuite
import Tree.*
import P56.*
import P57.*

class P57Suite extends FunSuite:
test("add an element to a BST"):
val xs = List(3, 2, 5, 7, 1)
val obtained = xs.foldLeft(Tree.empty[Int])((t, x) => t.addValue(x))
assertEquals(obtained.isSymmetric, true)

val ys = List(3, 2, 5, 7, 4)
val obtained2 = ys.foldLeft(Tree.empty[Int])((t, x) => t.addValue(x))
assertEquals(obtained2.isSymmetric, false)
16 changes: 16 additions & 0 deletions src/test/scala/bintree/P58Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package bintree

import munit.FunSuite
import Tree.*

class P58Suite extends FunSuite:
test("construct all symmetric, completely balanced binary trees with a given number of nodes"):
val obtained = P58.symmetricBalancedTrees(5, 'x')
val expected = List(
Node('x', Node('x', Empty, singleton('x')), Node('x', singleton('x'), Empty)),
Node('x', Node('x', singleton('x'), Empty), Node('x', Empty, singleton('x')))
)
assertEquals(obtained.size, expected.size)
obtained.foreach { tree =>
assert(expected.contains(tree), s"Tree: $tree")
}
17 changes: 17 additions & 0 deletions src/test/scala/bintree/P59Suite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package bintree

import munit.FunSuite
import Tree.*

class P59Suite extends FunSuite:
test("construct height-balanced binary trees"):
val obtained = P59.hbalTrees(3, 'x')
val expected = List(
Node('x', Node('x', Empty, Empty), Node('x', Empty, singleton('x'))),
Node('x', Node('x', Empty, Empty), Node('x', singleton('x'), Empty)),
Node('x', Node('x', Empty, Empty), Node('x', singleton('x'), singleton('x'))),
Node('x', Node('x', Empty, singleton('x')), Node('x', Empty, Empty))
)
expected.foreach { tree =>
assert(obtained.contains(tree), s"Tree: $tree")
}
4 changes: 2 additions & 2 deletions src/test/scala/list/P23Suite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ class P23Suite extends ScalaCheckSuite:
val k = lst.size
val n = Random.nextInt(k + 1)
val obtained = P23.randomSelect(n, lst)
assert(obtained.size == n && obtained.forall(x => lst.exists(_ == x)))
assert(obtained.size == n && obtained.forall(lst.contains))
}

property("extract n random elements from a list where n >>> list.size"):
forAllNoShrink(Gen.listOf(ListGen.genInt)) { (lst: List[Int]) =>
val k = lst.size
val obtained = P23.randomSelect(Int.MaxValue, lst)
assert(obtained.size == k && obtained.forall(x => lst.exists(_ == x)))
assert(obtained.size == k && obtained.forall(lst.contains))
}

0 comments on commit 9092a6d

Please sign in to comment.