Skip to content

Commit

Permalink
Algorithm-a-Day : Day 12 : Binary Search Tree
Browse files Browse the repository at this point in the history
- A BST. Know what this is? Can you write one right now?
  • Loading branch information
fosskers committed Aug 12, 2011
1 parent 0ce4c23 commit 1821e9e
Show file tree
Hide file tree
Showing 8 changed files with 440 additions and 0 deletions.
63 changes: 63 additions & 0 deletions day12-bs-tree/BSTREE_NOTES.txt
@@ -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.
53 changes: 53 additions & 0 deletions day12-bs-tree/bs-tree.hs
@@ -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
55 changes: 55 additions & 0 deletions day12-bs-tree/bs-unit-test.c
@@ -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");
}
69 changes: 69 additions & 0 deletions day12-bs-tree/bs_tree.py
@@ -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)

121 changes: 121 additions & 0 deletions day12-bs-tree/bstree.c
@@ -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;
}
30 changes: 30 additions & 0 deletions day12-bs-tree/bstree.h
@@ -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

0 comments on commit 1821e9e

Please sign in to comment.