C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
120: Implementing a Binary Search Tree in C#
I've noticed a recurring pattern when developers first dive into tree structures: they assume that a Binary Search Tree (BST) is simply a binary tree where the data happens to be sorted. It sounds like a semantic nuance, but it's actually a fundamental misunderstanding of how the data structure functions.
It's not just "sorted"βthe Invariant is everything
If you just take a sorted list of numbers and shove them into a binary tree structure without following the BST rules, you don't have a search tree; you have a very expensive linked list. For example, if you insert the numbers 10, 20, and 30 in that order into a naive tree without the "Left < Root < Right" invariant, you'll end up with a long chain leaning entirely to the right. Your search time drops from $O(\log n)$ to $O(n)$. You've lost the entire point of the structure.
The "magic" of the BST isn't that the tree is sorted, but that every single node maintains a strict contract: every node in its left subtree must be smaller, and every node in its right subtree must be larger. This contract allows us to discard half the remaining tree with every single comparison. I always tell my juniors to think of it like a high-stakes game of "Higher or Lower."
Structuring the Node and the Recursive Insert
To build this in C#, we need a node class that can hold a value and references to its two potential children. I prefer using a generic type T here, but since we need to compare values, we have to constrain T to IComparable. Otherwise, the compiler will have no idea how to determine if one "Product" or "Employee" is "less than" another.
public class BSTNode<T> where T : IComparable<T>
{
public T Value { get; set; }
public BSTNode<T> Left { get; set; }
public BSTNode<T> Right { get; set; }
public BSTNode(T value)
{
Value = value;
}
}
Now, for the insertion. The trick is to use recursion. We start at the root; if the new value is smaller than the current node, we go left. If it's larger, we go right. We keep doing this until we find an empty slot (a null child). I like to handle the root separately to keep the recursive method clean.
public class BinarySearchTree<T> where T : IComparable<T>
{
private BSTNode<T> _root;
public void Insert(T value)
{
_root = InsertRecursive(_root, value);
}
private BSTNode<T> InsertRecursive(BSTNode<T> current, T value)
{
if (current == null)
{
return new BSTNode<T>(value);
}
int comparison = value.CompareTo(current.Value);
if (comparison < 0)
{
current.Left = InsertRecursive(current.Left, value);
}
else if (comparison > 0)
{
current.Right = InsertRecursive(current.Right, value);
}
// If comparison == 0, the value already exists.
// In a standard BST, we usually just ignore duplicates.
return current;
}
}
Leveraging the Structure for $O(\log n)$ Lookups
Searching is where the payoff happens. Because we maintained that strict invariant during insertion, we don't have to check every node. We just navigate the path. If we're looking for "Employee ID 502" and the current node is "Employee ID 700", we know with absolute certainty that 502 cannot possibly be in the right subtree. We ignore that entire half of the data structure.
public bool Contains(T value)
{
return SearchRecursive(_root, value);
}
private bool SearchRecursive(BSTNode<T> current, T value)
{
if (current == null) return false;
int comparison = value.CompareTo(current.Value);
if (comparison == 0) return true;
return comparison < 0
? SearchRecursive(current.Left, value)
: SearchRecursive(current.Right, value);
}
One quick tip: while this basic implementation is great for learning, in a real-world production system, you'd likely use a "Balanced" BST (like an AVL tree or a Red-Black tree). Why? Because if your data arrives already sorted, our basic BST becomes that "expensive linked list" I mentioned earlier. Balancing ensures the tree stays bushy, keeping your performance consistent.
π Practical Task
Build a High-Score Leaderboard using a BST
You are tasked with creating a simple high-score tracking system for a game. Instead of using a List and sorting it every time a new score is added, you will implement a Binary Search Tree to store the scores.
Requirements:
- Create a class
ScoreNodethat holds an integerScoreand a stringPlayerName. - Implement a
ScoreTreeclass that allows you toAddScore(int score, string name). - The tree should be ordered by the
Score(descending or ascending is fine, as long as it is consistent). - Implement a method
FindPlayerByScore(int score)that returns the name of the player who achieved that score. If the score isn't in the tree, return "No player found". - Challenge: Implement a method
PrintAllScores()that performs an In-Order Traversal to print all scores in the leaderboard from lowest to highest.
There are no comments for now.