Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Algorithm-a-Day : Day 12 : Binary Search Tree
- A BST. Know what this is? Can you write one right now?
- Loading branch information
Showing
8 changed files
with
440 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
Algorithm-a-Day : Day ?? : Binary Search Tree | ||
|
||
ABOUT BINARY SEARCH TREE | ||
------------------------ | ||
A handy tree structure built for fast look-ups! | ||
Our versions here today aren't self-balancing, meaning that if your root | ||
sucks you could end up with a very lopsided tree. | ||
Many built-in data structures in modern languages are implemented with trees, | ||
so it's importaant to know what they are and how they work. | ||
This included knowing how the following things are done: | ||
|
||
-- Recursion. | ||
-- Queues. | ||
-- Depth-first searches. | ||
-- Breadth-first searches. | ||
(But for a binary search tree we don't have to worry about those types | ||
of searches) | ||
|
||
ABOUT PRINTING TREE STRUCTURES | ||
------------------------------ | ||
This causes so much unnecessary suffering, so I created a slick recursive | ||
way to print out tree structures opening out to the right. Each leaf gets | ||
its own line, and everything is indented properly. It's quite easy to read. | ||
Check out both the py_test.py file and the C unit test to see what | ||
I'm talking about. | ||
|
||
C | ||
- | ||
Here we had to introduce some new structs. Allocating memory for them is | ||
standard run-of-the-mill stuff. Don't forget to check for NULL pointers | ||
everywhere possible. I'll be there with an "I told you so" when a Segfault | ||
cracks your computer in half. | ||
Insertion and checking for containment is all done with recursion. These | ||
things can be done iteratively too, but it can get silly. | ||
Printing here, unlike the Python and Haskell versions, is done without | ||
any string creation, and instead prints right in the functions itself. | ||
There is a **unit test** file, so make sure to check that out too. | ||
|
||
PYTHON | ||
------ | ||
Shorter and sweeter than the C version, but all around the same. | ||
Just as we've had in other Python versions of algorithms thus far, we get | ||
to specify 'special functions' that allow us to use keywords on our Tree. | ||
print(tree) | ||
if 5 in tree: ... | ||
etc. | ||
For printing, here we're gradually building up a list of strings of the | ||
leaves, then concatinating it all at the end in the __str__ function. | ||
Remember that in Python, str.join() is always better than str + str. | ||
|
||
HASKELL | ||
------- | ||
God functional programming rocks. Here we start by defining instances | ||
of typeclasses for the Tree type, allowing us to (via Functor) map functions | ||
across our Tree, and (via Foldable) use folds across it as well. | ||
While it may look a little different, the function showTree produces | ||
the same result as the print functions in the C and Python versions. Once | ||
again, a victory for recursion. showTree is called by 'show', as is standard. | ||
Everything else is essentially the same, but with the Haskell twist. | ||
Even so, its over half as short as the C version (and that's with all our | ||
instance definitions included!). | ||
In conclusion, Haskell continues to impress me with its power of | ||
expressiveness. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
-- Binary Search Tree | ||
|
||
import qualified Data.Foldable as F | ||
import Data.Monoid | ||
|
||
data Tree a = EmptyTree | Leaf a (Tree a) (Tree a) deriving (Read, Eq) | ||
|
||
-- Makes our tree map(p)able. | ||
instance Functor Tree where | ||
fmap f EmptyTree = EmptyTree | ||
fmap f (Leaf x left right) = Leaf (f x) (fmap f left) (fmap f right) | ||
|
||
-- Makes our tree foldable. | ||
instance F.Foldable Tree where | ||
foldMap f EmptyTree = mempty | ||
foldMap f (Leaf x left right) = F.foldMap f left `mappend` | ||
f x `mappend` | ||
F.foldMap f right | ||
|
||
instance Show a => Show (Tree a) where | ||
show EmptyTree = "Leafless Tree" | ||
show leaf = showTree leaf 0 | ||
|
||
showTree :: Show a => Tree a -> Int -> String | ||
showTree EmptyTree _ = "" | ||
showTree (Leaf x left right) ind = showTree right ind' ++ | ||
replicate ind ' ' ++ | ||
show x ++ | ||
"\n" ++ | ||
showTree left ind' | ||
where ind' = ind + 2 | ||
|
||
newTree :: a -> Tree a | ||
newTree x = Leaf x EmptyTree EmptyTree | ||
|
||
treeInsert :: Ord a => a -> Tree a -> Tree a | ||
treeInsert x EmptyTree = newTree x | ||
treeInsert x orig@(Leaf a left right) | ||
| x == a = orig --Leaf x left right | ||
| x < a = Leaf a (treeInsert x left) right | ||
| x > a = Leaf a left (treeInsert x right) | ||
|
||
treeElem :: Ord a => a -> Tree a -> Bool | ||
treeElem _ EmptyTree = False | ||
treeElem x (Leaf a left right) | ||
| x == a = True | ||
| x < a = treeElem x left | ||
| x > a = treeElem x right | ||
|
||
fromList :: Ord a => [a] -> Tree a | ||
-- Creates a tree from a given list. | ||
fromList [] = EmptyTree | ||
fromList xs = foldl (\acc x -> treeInsert x acc) EmptyTree xs |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include "bstree.h" | ||
#include "intlist.h" | ||
|
||
void test_contains(Tree *, int); | ||
|
||
int main() { | ||
Tree *tree = NULL; | ||
IntList *list = NULL; | ||
int nums[12] = {10, 15, 5, 3, 14, 7, 2, 11, 9, 0, 16, 15}; | ||
|
||
// NULL Tree. | ||
printf("Attempting to make and print a NULL Tree...\n"); | ||
tree = new_tree_from_list(list); | ||
print_tree(tree); | ||
|
||
// Few elements at a time. | ||
printf("Adding a few elements at a time...\n"); | ||
tree = new_tree(); | ||
insert(tree, 5); | ||
printf("One leaf...\n"); | ||
print_tree(tree); | ||
insert(tree, 3); | ||
printf("Two leaves...\n"); | ||
print_tree(tree); | ||
insert(tree, 7); | ||
printf("Three leaves...\n"); | ||
print_tree(tree); | ||
|
||
// From a list. | ||
printf("Making a Tree from an IntList...\n"); | ||
list = wrap_array(nums, 12); | ||
tree = new_tree_from_list(list); | ||
print_tree(tree); | ||
|
||
// Testing contains. | ||
test_contains(tree, 1); | ||
test_contains(tree, 16); | ||
test_contains(tree, 15); | ||
test_contains(tree, 5); | ||
test_contains(tree, 7); | ||
test_contains(tree, 0); | ||
test_contains(tree, 6); | ||
|
||
return 0; | ||
} | ||
|
||
void test_contains(Tree *tree, int item) { | ||
printf("Is %d in the tree?\n", item); | ||
if(contains(tree, item)) | ||
printf("Yes.\n"); | ||
else | ||
printf("No.\n"); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# A binary search tree in Python. | ||
|
||
class BSTree(): | ||
'''A binary search tree.''' | ||
def __init__(self, items=None): | ||
self.root = None | ||
if items: # Can be initialized with a list. | ||
for item in items: | ||
self.insert(item) | ||
|
||
def __str__(self): | ||
'''A pretty picture of a tree. | ||
May this be the definitive way to print trees, ever. EVER. | ||
Don't need a special case for empty trees, either :) | ||
''' | ||
leaves = [] | ||
self._str(leaves, self.root, 0) | ||
return ''.join(leaves).rstrip() | ||
|
||
def _str(self, leaves, leaf, indent): | ||
'''Never have I high-fived myself so hard. | ||
This generates a tree string expanding out to the right via recursion. | ||
The indent of each leaf on its line its carried through the recursive | ||
process, increasing for each layer.''' | ||
if leaf: | ||
self._str(leaves, leaf.right, indent + 2) # Arbitrary pad of 2. | ||
leaves.append(''.join((' ' * indent, str(leaf), '\n'))) | ||
self._str(leaves, leaf.left, indent + 2) | ||
|
||
def __contains__(self, item): | ||
'''Determines if a value is in the tree.''' | ||
return self._contains(self.root, item) | ||
|
||
def _contains(self, leaf, item): | ||
'''Uses recursion to test for containment.''' | ||
if not leaf: | ||
return False | ||
if item == leaf.data: | ||
result = True | ||
elif item < leaf.data: | ||
result = self._contains(leaf.left, item) | ||
else: | ||
result = self._contains(leaf.right, item) | ||
return result | ||
|
||
def insert(self, item): | ||
'''Insert a value into the tree.''' | ||
self.root = self._insert(self.root, item) | ||
|
||
def _insert(self, leaf, item): | ||
'''Inserts a new Leaf via recursion.''' | ||
if not leaf: | ||
return Leaf(item) | ||
if item < leaf.data: | ||
leaf.left = self._insert(leaf.left, item) | ||
else: | ||
leaf.right = self._insert(leaf.right, item) | ||
return leaf | ||
|
||
class Leaf(): | ||
'''Or 'Node', if you wish.''' | ||
def __init__(self, data): | ||
self.data = data | ||
self.left = None | ||
self.right = None | ||
|
||
def __str__(self): | ||
return str(self.data) | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// A Binary Search Tree in C. | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include "bstree.h" | ||
#include "intlist.h" | ||
|
||
Tree * new_tree() { | ||
// Creates a new, empty Tree. | ||
Tree *tree = NULL; | ||
|
||
tree = malloc(sizeof(Tree)); | ||
|
||
if(tree != NULL) | ||
tree->root = NULL; | ||
|
||
return tree; | ||
} | ||
|
||
Tree * new_tree_from_list(IntList *list) { | ||
// Given an IntList, creates a Tree from it. | ||
Tree *tree = NULL; | ||
int count; | ||
|
||
if(list != NULL) { | ||
tree = new_tree(); | ||
|
||
if(tree != NULL) { | ||
// Insert every item in 'list' into the Tree. | ||
for(count = 0; count < list->size; count++) | ||
insert(tree, list->list[count]); | ||
} | ||
} | ||
|
||
return tree; | ||
} | ||
|
||
int contains(Tree *tree, int item) { | ||
// Determines if 'item' is in the given Tree. | ||
int result = 0; | ||
|
||
if(tree != NULL) | ||
result = _contains(tree->root, item); | ||
|
||
return result; | ||
} | ||
|
||
int _contains(Leaf *leaf, int item) { | ||
// Uses recursion to test for containment. | ||
int result; | ||
|
||
if(leaf == NULL) | ||
return 0; | ||
|
||
if(item == leaf->data) | ||
result = 1; | ||
else if(item < leaf->data) | ||
result = _contains(leaf->left, item); | ||
else | ||
result = _contains(leaf->right, item); | ||
|
||
return result; | ||
} | ||
|
||
void insert(Tree *tree, int item) { | ||
// Inserts an item into the given Tree. | ||
if(tree != NULL) { | ||
tree->root = _insert(tree->root, item); | ||
} | ||
} | ||
|
||
Leaf * _insert(Leaf *leaf, int item) { | ||
// Inserts the new item via recursion. | ||
if(leaf == NULL) | ||
return new_leaf(item); | ||
|
||
if(item < leaf->data) | ||
leaf->left = _insert(leaf->left, item); | ||
else | ||
leaf->right = _insert(leaf->right, item); | ||
|
||
return leaf; | ||
} | ||
|
||
void print_tree(Tree *tree) { | ||
// Figured out a great way to print trees. | ||
if(tree != NULL) | ||
_print_tree(tree->root, 0); | ||
} | ||
|
||
void _print_tree(Leaf *leaf, int indent) { | ||
// Prints the tree opening to the right via recursion. | ||
if(leaf != NULL) { | ||
_print_tree(leaf->right, indent + 2); | ||
print_indent(indent); | ||
printf("%d\n", leaf->data); | ||
_print_tree(leaf->left, indent + 2); | ||
} | ||
} | ||
|
||
void print_indent(int indent) { | ||
// Prints a bunch of whitespace. | ||
int count; | ||
|
||
for(count = 0; count < indent; count++) | ||
printf(" "); | ||
} | ||
|
||
Leaf * new_leaf(int item) { | ||
Leaf *leaf = NULL; | ||
|
||
leaf = malloc(sizeof(Leaf)); | ||
|
||
if(leaf != NULL) { | ||
leaf->data = item; | ||
leaf->left = NULL; | ||
leaf->right = NULL; | ||
} | ||
|
||
return leaf; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#include "intlist.h" | ||
|
||
#ifndef BSTREE_H | ||
#define BSTREE_H | ||
|
||
typedef struct LEAF { | ||
int data; | ||
struct LEAF *left; | ||
struct LEAF *right; | ||
} Leaf; | ||
|
||
typedef struct TREE { | ||
Leaf *root; | ||
} Tree; | ||
|
||
// Tree Functions | ||
Tree * new_tree(); | ||
Tree * new_tree_from_list(IntList *); | ||
int contains(Tree *, int); | ||
int _contains(Leaf *, int); | ||
void insert(Tree *, int); | ||
Leaf * _insert(Leaf *, int); | ||
void print_tree(Tree *); | ||
void _print_tree(Leaf *, int); | ||
void print_indent(int); | ||
|
||
// Leaf Functions | ||
Leaf * new_leaf(int); | ||
|
||
#endif |
Oops, something went wrong.