Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
44: Implementing a Binary Search Tree
Imagine you're working in a massive physical archive of historical documents. If you just threw every paper into a giant pile, finding one specific record from 1922 would take you all day. But if you had a system where you stood at a central desk and the archivist told you, "Everything dated before 1950 is in the left wing of the building, and everything after is in the right wing," you've just cut your search area in half instantly. Once you enter the left wing, you hit another desk that tells you, "Before 1920? Left. After 1920? Right."
That's exactly how a Binary Search Tree (BST) works. It's not just a way to store data; it's a way to organize data so that every time you make a move, you discard half of the remaining possibilities. In Scala, we can implement this beautifully using recursion and algebraic data types.
The Anatomy of a Tree
I've seen a lot of developers try to implement trees in Scala using mutable pointers and while-loops, coming from a Java or C++ background. Don't do that here. The "Scala way" is to define the tree as a recursive data structure. We use a sealed trait to define what a BST is, and then provide two specific cases: an empty tree (the leaf) and a node containing a value and two sub-trees.
sealed trait BST[+A]
case object Empty extends BST[Nothing]
case class Node[A](value: A, left: BST[A], right: BST[A]) extends BST[A]
By using a sealed trait, we're telling the compiler that these are the only two possible shapes a BST can take. This makes our pattern matching exhaustive and safe.
Winding the Path for Insertions
Inserting a value into a BST is like following the directions in that archive. If the tree is empty, you've found your spot. If it's not, you compare your value to the current node's value. Smaller? Go left. Larger? Go right. Since we're staying functional, we don't "modify" the tree; we return a new tree that shares most of its structure with the old one.
def insert[A](tree: BST[A], x: A)(implicit ord: Ordering[A]): BST[A] = {
tree match {
case Empty =>
Node(x, Empty, Empty)
case Node(v, l, r) =>
if (ord.lt(x, v))
Node(v, insert(l, x), r) // Rebuild node with updated left branch
else if (ord.gt(x, v))
Node(v, l, insert(r, x)) // Rebuild node with updated right branch
else
Node(v, l, r) // Value already exists, just return the tree as is
}
}
Notice the Ordering[A] implicit. I use this because we can't assume A is a number; it could be a String or a custom User object. The Ordering trait gives us a standardized way to handle comparisons.
The Efficiency of the Search
Searching is where the BST really shines. Because we maintained the "left is smaller, right is larger" rule during insertion, we don't have to look at every single element. We just dive down the branches.
def contains[A](tree: BST[A], x: A)(implicit ord: Ordering[A]): Boolean = {
tree match {
case Empty => false
case Node(v, l, r) =>
if (v == x) true
else if (ord.lt(x, v)) contains(l, x)
else contains(r, x)
}
}
If your tree is "balanced" (meaning it doesn't just look like one long line), this search happens in logarithmic time. In plain English: if you have a million items, you can find your target in about 20 steps. That's a massive win over a standard list.
📋 Practical Task
Implementing a High-Score Registry BST
You are building a leaderboard for a retro arcade game. You need to implement a Binary Search Tree that stores Score objects. A Score consists of a playerName (String) and a points (Int) value.
Your task:
- Define a
case class Score(playerName: String, points: Int). - Implement a custom
Ordering[Score]that compares scores based on thepointsvalue. - Using the
BSTtrait andinsert/containslogic from the lesson, create a program that:- Inserts three scores: ("Alice", 1200), ("Bob", 800), and ("Charlie", 1500).
- Checks if a score of 800 exists in the tree.
- Checks if a score of 1000 exists in the tree.
There are no comments for now.