Algorithmathon Day 9: Trees Base
Created: 23 July 2013 Modified:
The goal for today is to setup the basics for trees. The basic object for trees is the node. A node is a collection of references/pointers to other nodes. Lets write the tests and code to satisfy this requirement. I will be concentrating on the BinarySearchTreeNode object. You will find a TreeNode and a BinaryTreeNode in the code on GitHub.
binary_search_tree_node_spec.rb
require 'spec_helper'
describe BinarySearchTreeNode do
describe "Class" do
it "should have method 'greater'" do
BinarySearchTreeNode . method_defined? ( :greater ). should be_true
end
it "should have method 'lesser'" do
BinarySearchTreeNode . method_defined? ( :lesser ). should be_true
end
it "should have method 'data'" do
BinarySearchTreeNode . method_defined? ( :data ). should be_true
end
it "should have method 'less_than?'" do
BinarySearchTreeNode . method_defined? ( :less_than? ). should be_true
end
it "should have method 'greater_than?'" do
BinarySearchTreeNode . method_defined? ( :greater_than? ). should be_true
end
end
describe "Instance" do
before ( :each ) do
@node = BinarySearchTreeNode . new ( 5 )
end
it "should not be nil" do
@node . should_not be_nil
end
it "should not throw an exeption because greater, lesser and parent are nil" do
expect { parent = BinarySearchTreeNode . new ( 5 )}. to_not raise_error
end
it "should throw an exeption because lesser is more than greater" do
lesser = BinarySearchTreeNode . new ( 10 )
greater = BinarySearchTreeNode . new ( 5 )
expect { parent = BinarySearchTreeNode . new ( 7 , lesser , greater )}. to raise_error
end
it "should throw an exeption because lesser is not nil and greater is" do
lesser = BinarySearchTreeNode . new ( 10 )
expect { parent = BinarySearchTreeNode . new ( 7 , lesser ) }. to raise_error
end
it "should not throw an exeption because lesser is less than greater" do
lesser = BinarySearchTreeNode . new ( 5 )
greater = BinarySearchTreeNode . new ( 10 )
expect { parent = BinarySearchTreeNode . new ( 7 , lesser , greater )}. to_not raise_error
end
it "should not throw an exeption because lesser is nil and greater is not" do
greater = BinarySearchTreeNode . new ( 10 )
expect { parent = BinarySearchTreeNode . new ( 7 , nil , greater )}. to_not raise_error
end
it "should not throw an exeption because lesser and greater are nil" do
expect { parent = BinarySearchTreeNode . new ( 7 )}. to_not raise_error
end
it "should throw an exeption because lesser and greater are equal" do
lesser = BinarySearchTreeNode . new ( 10 )
greater = BinarySearchTreeNode . new ( 10 )
expect { parent = BinarySearchTreeNode . new ( 7 , lesser , greater )}. to raise_error
end
it "should throw an exeption because lesser and parent are equal" do
lesser = BinarySearchTreeNode . new ( 7 )
greater = BinarySearchTreeNode . new ( 10 )
expect { parent = BinarySearchTreeNode . new ( 7 , lesser , greater )}. to raise_error
end
it "should throw an exeption because greater and parent are equal" do
lesser = BinarySearchTreeNode . new ( 3 )
greater = BinarySearchTreeNode . new ( 7 )
expect { parent = BinarySearchTreeNode . new ( 7 , lesser , greater )}. to raise_error
end
it "should return true when lesser.lesser_than?(greater) is called" do
lesser = BinarySearchTreeNode . new ( 3 )
greater = BinarySearchTreeNode . new ( 7 )
lesser . less_than? ( greater ). should be_true
end
it "should return false when greater.greater_than?(lesser) is called" do
lesser = BinarySearchTreeNode . new ( 3 )
greater = BinarySearchTreeNode . new ( 7 )
greater . greater_than? ( lesser ). should be_true
end
end
end
As can be seen we have added several tests that enforce the attributes that make a
Binary Search Tree . With the code below to satisfy the tests we can move on to building a Binary Search Tree.
tree_node.rb
class BinarySearchTreeNode
attr_accessor :lesser , :greater
attr_reader :data
def initialize ( data , lesser = nil , greater = nil )
@data = data
self . lesser = lesser
self . greater = greater
if ( ! self . lesser . nil? && self . greater . nil? ) || ( ! self . lesser . nil? && ( self . lesser . data > self . greater . data ))
throw "The lesser exceeds the greater"
end
if ! self . lesser . nil? && ! self . data . nil? && self . lesser . data == self . data
throw "The lesser is equal to the parent"
end
if ! self . greater . nil? && ! self . data . nil? && self . greater . data == self . data
throw "The greater is equal to the parent"
end
if ! self . lesser . nil? && ! self . greater . nil? && self . lesser . data == self . greater . data
throw "The lesser is equal to the parent"
end
end
def less_than? ( target_node )
if target_node . nil?
raise "The target_node is nil!"
else
if @data < target_node . data
true
else
false
end
end
end
def greater_than? ( target_node )
if target_node . nil?
raise "The target_node is nil!"
else
if @data > target_node . data
true
else
false
end
end
end
end
What do we know about a binary search tree? We know that it needs a starting node know as the “root”. If we are going to be able to write algorithms against a tree we need to be able to build a tree. This means we need a “root” attribute and an “add” method. We should add several tests to specify the functionality we need. These tests come quite naturally if you use the Test Driven Development
methodology.
binary_search_tree_spec.rb
require 'spec_helper'
describe BinarySearchTree do
describe "Class" do
it "should have method 'root'" do
BinarySearchTree . method_defined? ( :root ). should be_true
end
it "should have method 'add'" do
BinarySearchTree . method_defined? ( :add ). should be_true
end
end
describe "Instance" do
before ( :each ) do
@tree = BinarySearchTree . new ()
end
it "should have node(5) as the root when passed into constructor" do
node = BinarySearchTreeNode . new ( 5 )
tree = BinarySearchTree . new ( node )
tree . root . should_not be_nil
tree . root . data . should == 5
end
it "should have node(6) as the root when add(node(5)) is called" do
node = BinarySearchTreeNode . new ( 5 )
tree = BinarySearchTree . new ()
tree . add ( node )
tree . root . should_not be_nil
tree . root . data . should == 5
end
it "should have add(node(5)) as the lesser of the root" do
node = BinarySearchTreeNode . new ( 5 )
tree = BinarySearchTree . new ( BinarySearchTreeNode . new ( 10 ))
tree . add ( node )
tree . root . lesser . should_not be_nil
tree . root . lesser . data . should == 5
end
it "should have add(node(15)) as the greater of the root" do
node = BinarySearchTreeNode . new ( 15 )
tree = BinarySearchTree . new ( BinarySearchTreeNode . new ( 10 ))
tree . add ( node )
tree . root . greater . should_not be_nil
tree . root . greater . data . should == 15
end
it "should have add(node(10)) raise an error" do
node = BinarySearchTreeNode . new ( 10 )
tree = BinarySearchTree . new ( BinarySearchTreeNode . new ( 10 ))
expect { tree . add ( node ) }. to raise_error
end
it "should have add(node(50)) as the lesser of the root.lesser" do
node1 = BinarySearchTreeNode . new ( 75 )
node2 = BinarySearchTreeNode . new ( 50 )
tree = BinarySearchTree . new ( BinarySearchTreeNode . new ( 100 ))
tree . add ( node1 )
tree . add ( node2 )
tree . root . lesser . should_not be_nil
tree . root . lesser . lesser . should_not be_nil
tree . root . lesser . lesser . data . should == 50
end
it "should have add(node(85)) as the greater of the root.lesser" do
node1 = BinarySearchTreeNode . new ( 75 )
node2 = BinarySearchTreeNode . new ( 85 )
tree = BinarySearchTree . new ( BinarySearchTreeNode . new ( 100 ))
tree . add ( node1 )
tree . add ( node2 )
tree . root . lesser . should_not be_nil
tree . root . lesser . greater . should_not be_nil
tree . root . lesser . greater . data . should == 85
end
it "should have add(node(150)) as the greater of the root" do
node1 = BinarySearchTreeNode . new ( 150 )
tree = BinarySearchTree . new ( BinarySearchTreeNode . new ( 100 ))
tree . add ( node1 )
tree . root . greater . should_not be_nil
tree . root . greater . data . should == 150
end
it "should return 125 when it recieves the root.greater.lesser.data call" do
node1 = BinarySearchTreeNode . new ( 150 )
node2 = BinarySearchTreeNode . new ( 75 )
node3 = BinarySearchTreeNode . new ( 125 )
tree = BinarySearchTree . new ( BinarySearchTreeNode . new ( 100 ))
tree . add ( node1 )
tree . add ( node2 )
tree . add ( node3 )
tree . root . greater . should_not be_nil
tree . root . greater . lesser . should_not be_nil
tree . root . greater . lesser . data . should == 125
end
end
end
binary_search_tree.rb
class BinarySearchTree
attr_reader :root
def initialize ( root = nil )
@root = root
end
def add ( new_node )
if new_node . nil?
new_node
else
if @root . nil?
@root = new_node
else
add_by_recursion ( @root , new_node )
end
end
end
private
def add_by_recursion ( current_node , new_node )
if current_node . nil?
current_node
else
if current_node . greater_than? ( new_node )
if current_node . lesser . nil?
current_node . lesser = new_node
else
add_by_recursion ( current_node . lesser , new_node )
end
elsif current_node . less_than? ( new_node )
if current_node . greater . nil?
current_node . greater = new_node
else
add_by_recursion ( current_node . greater , new_node )
end
else
raise "Node matches existing node" + new_node . data . to_s
end
end
end
end
We now have a Binary Search Tree with enough basic functionality to make it useful. Tomorrow we will look into depth first searches.
tags: Algorithm - Algorithmathon - RSpec - Ruby - Trees