Please send questions to
st10@humboldt.edu .
/************************************************************/
/* implementation of .h file adapted from */
/* http://www.cs.colorado.edu/~main/chapter10/bt_class.h */
/* */
/* Main and Savitch, "Data Structures and Other Objects */
/* using C++", 2nd edition, Addison-Wesley, Ch.10,Ch.11 */
/************************************************************/
//-----------------------------------------------------------
// File: complete_tree.template
// Name: Sharon Tuttle
// (implementing an adaptation of a .h file adapted from
// Savitch and Main)
// last modified: 4-12-05
//
// Template Class: complete_tree<Item> (a complete binary tree
// where each node contains an Item)
// See complete_tree.h for documentation.
//
// Because this is a template class, it is included in
// the header file, and not compiled separately.
//
// INVARIANT for the complete_tree class:
// 1. The items in the complete tree are stored in a
// dynamic array, where the root is in index 0, depth
// 1 would be in indices 1-2, depth 2 would be in indices
// 3-6, ,,, depth n would (2 exp n)-1, ... (2 exp (n+1))-2.
// 2. The current capacity of the complete_tree is
// stored in capacity; the current number of items is
// stored in used.
// 3. Each non-empty complete_tree instance always has a
// "current node" --- the member variable current_index
// contains the index of this node.
//--------------------------------------------------------------
#include <cassert> // Provides assert
#include <algorithm> // Provides copy function
#include <iomanip> // Provides setw
#include <iostream> // Provides cout
#include <cstdlib> // Provides size_t
using namespace std;
/********************************************************/
/* CONSTRUCTORS and DESTRUCTOR */
/********************************************************/
// constructor
//
// postcondition: creates an empty complete tree instance
// (with no nodes).
//
template <typename Item>
complete_tree<Item>::complete_tree( )
{
nodes = new Item[1]; // initial complete tree has room
// for one node
capacity = 1;
used = 0;
current_index = -1; // there's no current until there's
// a node...
}
// copy constructor
//
// Library facilities used: algorithm
template <typename Item>
complete_tree<Item>::complete_tree(const complete_tree<Item>& source)
{
nodes = new Item[source.capacity];
capacity = source.capacity;
used = source.used;
// (copy is from the algorithm library)
copy(source.nodes, source.nodes + used, nodes);
// what should current_index for new copy contain?
// If copied an empty tree, current_index is -1;
// otherwise, we'll start it at the root, I think.
if (used == 0)
{
current_index = -1;
}
else
{
current_index = 0;
}
}
// destructor
//
template <typename Item>
complete_tree<Item>::~complete_tree()
{
delete [ ] nodes;
}
/*************************************************************/
/* ACCESSORS and other constant member functions (observers) */
/*************************************************************/
// get_size
//
// postcondition: returns the number of nodes in the
// complete_tree.
//
template <typename Item>
size_t complete_tree<Item>::get_size( ) const
{
return used;
}
// is_empty
//
// postcondition: returns true if the complete_tree is
// empty, returns false otherwise.
//
template <typename Item>
bool complete_tree<Item>::is_empty( ) const
{
return (used == 0);
}
// retrieve
//
// precondition: size( ) > 0
// postcondition: returns (non-destructively) the data
// from the "current node", BUT the complete_tree is
// unchanged.
//
template <typename Item>
Item complete_tree<Item>::retrieve( ) const
{
assert(get_size( ) > 0);
return nodes[current_index];
}
// is_root
//
// postcondition: returns true if size( ) > 0 and
// and the "current node" is the root.
//
template <typename Item>
bool complete_tree<Item>::is_root( ) const
{
return( (get_size( ) > 0) && (current_index == 0) );
}
// is_leaf
//
// postcondition: returns true if size( ) > 0 and
// the "current node" is a leaf (has no children)
//
template <typename Item>
bool complete_tree<Item>::is_leaf( ) const
{
return( (get_size( ) > 0) &&
(! has_left_child( ) ) &&
(! has_right_child( ) ) );
}
// has_parent
//
// postcondition: returns true if size( ) > 0 and
// the "current node" has a parent.
//
template <typename Item>
bool complete_tree<Item>::has_parent( ) const
{
return ( (get_size( ) > 0) &&
// if current isn't root, it must have a parent!
(current_index != 0) );
}
// has_left_child
//
// postcondition: returns true if size( ) > 0 and
// the "current node" has a left child.
//
template <typename Item>
bool complete_tree<Item>::has_left_child( ) const
{
return ( (get_size( ) > 0) &&
// see if where left child SHOULD be is
// currently filled
( get_left_index( ) < used ) );
}
// has_right_child
//
// postcondition: returns true if size( ) > 0 and
// the "current node" has a right child.
//
template <typename Item>
bool complete_tree<Item>::has_right_child( ) const
{
return ( (get_size( ) > 0) &&
// see if where right child SHOULD be is
// currently filled
( get_right_index( ) < used ) );
}
// get_parent_value
//
// precondition: has_parent( ) == true
// postcondition: returns the value of "current
// node"'s parent
//
template <typename Item>
Item complete_tree<Item>::get_parent_value( ) const
{
assert( has_parent( ) == true );
return nodes[ get_parent_index( ) ];
}
// get_left_value
//
// precondition: has_left_child( ) == true
// postcondition: returns the value of the
// "current node"'s left child
//
template <typename Item>
Item complete_tree<Item>::get_left_value( ) const
{
assert( has_left_child( ) == true );
return nodes[ get_left_index( ) ];
}
// get_right_value
//
// precondition: has_right_child( ) == true
// postcondition: returns the value of the
// "current node"'s right child
//
template <typename Item>
Item complete_tree<Item>::get_right_value( ) const
{
assert( has_right_child( ) == true );
return nodes[ get_right_index( ) ];
}
/****************************************************/
/* MODIFIERS and other modifying member functions */
/****************************************************/
// add
//
// postconditions: a node has been added to the proper
// (next moving right) position at the lowest level
// to maintain this as a complete binary tree, with
// the given entry as its value.
// IF the tree was EMPTY before, then current node
// is now the root. Otherwise, current node
// is unchanged.
//
// Library facilities used: algorithm
//
template <typename Item>
void complete_tree<Item>::add(const Item& entry)
{
Item *larger_nodes;
if (used == capacity)
{
// want to add a new level to tree --- and in a
// complete binary tree, that new level might
// eventually contain one more than the current
// capacity
capacity += (capacity+1);
larger_nodes = new Item[capacity];
copy(nodes, nodes + used, larger_nodes); // from algorithm
// library
delete [ ] nodes;
nodes = larger_nodes;
}
// if tree is currently empty, then adding this node
// should elso set "current index" to the newly-added
// root.
if ( is_empty( ) )
{
current_index = 0;
}
// "next" node to be filled in the complete tree is
// the next to the right on the lowest level ---
// the node at index used!
nodes[used] = entry;
used++;
}
// remove
//
// preconditions: size( ) > 0
// postconditions: the rightmost node on the lowest level
// of the tree has been removed, and its value is
// returned.
// IF the tree is now empty, then there is no longer
// a "current node"
// IF "current node" WAS the node to be removed, then
// "current node" is now that removed node's parent.
// OTHERWISE, "current_node" is unchanged.
//
template <typename Item>
Item complete_tree<Item>::remove( )
{
Item removed_value;
assert( get_size( ) > 0 );
removed_value = nodes[used-1];
used--;
// does current_index need to be changed?
if ( used == 0 )
{
// tree is now empty, so there is no longer
// a "current node"
current_index = -1;
}
else if ( used == current_index )
{
// just removed "current node" --- make its
// parent the new "current node"
// (and note tree cannot be empty, if reached here)
current_index = get_parent_index( );
}
return removed_value;
}
// change
//
// precondition: size( ) > 0
// postcondition: the data at the "current node" has
// been changed to the new entry
//
template <typename Item>
void complete_tree<Item>::change(const Item& entry)
{
assert( get_size( ) > 0 );
nodes[current_index] = entry;
}
// clear_tree
//
// postconditions: the tree is empty (and so there is
// no "current node")
//
template <typename Item>
void complete_tree<Item>::clear_tree( )
{
delete [ ] nodes;
nodes = new Item[1];
capacity = 1;
used = 0;
current_index = -1;
}
// shift_to_root
//
// precondition: size( ) > 0
// postcondition: the "current node" is now the root
// of the tree.
//
template <typename Item>
void complete_tree<Item>::shift_to_root( )
{
assert( get_size( ) > 0 );
current_index = 0;
}
// shift_up
//
// precondition: has_parent( ) == true
// postcondition: the "current node" has been shifted
// up to the parent of the old current node.
//
template <typename Item>
void complete_tree<Item>::shift_up( )
{
assert( has_parent( ) == true );
current_index = get_parent_index( );
}
// shift_left
//
// precondition: has_left_child( ) == true
// postcondition: the "current node" has been shifted
// down to the left child of the old current node.
//
template <typename Item>
void complete_tree<Item>::shift_left( )
{
assert( has_left_child( ) == true );
current_index = get_left_index( );
}
// shift_right
//
// precondition: has_right_child( ) == true
// postcondition: the "current node" has been shifted
// down to the right child of the old current node.
//
template <typename Item>
void complete_tree<Item>::shift_right( )
{
assert( has_right_child( ) == true );
current_index = get_right_index( );
}
// shift_to_lowest_right
//
// precondition: size( ) != 0
// postcondition: the "current node" has been shifted
// down to the rightmost item on the lowest level
//
template <typename Item>
void complete_tree<Item>::shift_to_lowest_right( )
{
assert( get_size( ) > 0 );
current_index = used-1;
}
// print_tree
//
// preconditions: if !empty( ), depth is the depth of the
// calling complete_tree instance.
// postconditions: if !empty, then the contents of the
// root and all of its descendants have been written to
// cout with the << operator using a backward in-order
// traversal. Each node is indented four times its depth.
//
template <typename Item>
void complete_tree<Item>::print_tree(size_t depth)
{
print(0, depth);
}
/****************************************************/
/* OVERLOADED OPERATORS */
/****************************************************/
// =
//
// postcondition: the complete_tree that activated this
// will be made to have the same items as source
//
// Library facilities used: algorithm
//
template <typename Item>
void complete_tree<Item>::operator =(const complete_tree<Item>& source)
{
Item *new_nodes;
if (this == &source) // Handle self-assignment
{
return;
}
// If needed, allocate an array with a different size:
if (capacity != source.capacity)
{
new_nodes = new Item[source.capacity];
delete [ ] nodes;
nodes = new_nodes;
capacity = source.capacity;
}
// Copy the data from the source array:
used = source.used;
copy(source.nodes, source.nodes + used, nodes); // from
//algorithm lib
// where should current_ptr for new copy point?
// If copied an empty tree, current_index is -1
// otherwise, we'll start it at the root, I think.
if (used == 0)
{
current_index = -1;
}
else
{
current_index = 0;
}
}
/****************************************************/
/* private helper functions */
/****************************************************/
// get_parent_index
//
// precondition: has_parent( ) == true
// postcondition: returns index of current node's
// parent
//
template <typename Item>
size_t complete_tree<Item>::get_parent_index( ) const
{
assert(has_parent( ) == true);
return ( (current_index - 1)/ 2 );
}
// get_left_index
//
// postcondition: returns what WOULD be index of
// current node's left child (BUT it may not
// currently exist!)
//
template <typename Item>
size_t complete_tree<Item>::get_left_index( ) const
{
return ( (2 * current_index) + 1 );
}
// get_right_index
//
// postcondition: returns what WOULD be index of
// current node's right child (BUT it may not
// currently exist!)
//
template <typename Item>
size_t complete_tree<Item>::get_right_index( ) const
{
return ( (2 * current_index) + 2 );
}
// print
//
// used to help accomplish print_tree
//
// Library facilities used: iomanip, iostream, stdlib
//
template <typename Item>
void complete_tree<Item>::print(size_t index, size_t depth)
{
size_t save_current;
save_current = current_index;
if ((index >= 0) && (index < used) )
{
// get_right_index works based on current_index...!
current_index = index;
print( get_right_index( ), depth+1 );
// Index 4*depth spaces
cout << setw(4*depth) << "";
cout << nodes[index] << endl;
// get_left_index works based on current_index...!
current_index = index;
print( get_left_index( ), depth+1 );
}
current_index = save_current;
}