Phase 3 for Program 2

Due 17 April 2006 by 11:59pm

Final Deliverable for Program 2: Full functionality

Due 24 April 2006 by Noon

Phase1 description Phase2 description Phase3 description


Time to leverage what you have learned.
You already know how to recursively locate nodes in the tree and then perform operations on them from one-level higher at the "parent" node.
The parent node is either the root node or a regular node so think carefully about which class to add a function to.

You will find a script, named run_p2_tests.pl, to run your program against all the tests I expect you to pass.
These tests are now ALL of the tests.
If you have questions about what the tests do click here and read the description at the bottom of the directory listing.

It is essential that you read the first two paragraphs on page 548 of the book.
Read ALL that kruse has to say on btree deletions.

Tackle this phase in steps:

  1. write a function that can "delete" a value, test it with tests t51 - t57
  2. write function to return the value of the search order predecessor, I named mine get_predecessor()
  3. test your predecessor() function with test t58
  4. now you are ready to take delete to the next level by extending remove to be more like push_down()
  5. you need a remove_smallest()/remove_largest() and the helper function can_borrow()
  6. finish off with Node::combine()

Tips:

Deleting does not involve pointers! Why? Think about how deletes only occur at the leaf level.

Combines are what move pointers around.

To finish up with the assignment you will need to add the following stuff to your classes:


class Node
{ 
  // member variables common two both types of nodes:
  int m_id;
  int m_count;
  int m_order;
  int m_max;
  int m_min;
  int* m_data;
  Node** m_branch;

  // helper functions
  void verbose_print();                                  // you know what this does
  bool has_room();                                       // returns true if m_count < m_max
  void wipe();                                           // empty out a node's data/pointers 

  // insertion functions:
  void push_in(int value, Node* pointer, int position);  // take this from kruse
  void push_in_lesser(int value, Node* pointer);         // when the pointer < value
  void push_in_greater(int value, Node* pointer);        // when the pointer > value

  // functions used to force an insert when a node is full
  void pop_out_smallest(int& value, Node*& pointer);     // notice that value and pointer are "output parameters"
  void pop_out_largest(int& value, Node*& pointer);      // so you pass in stuff and expect to get back smallest/largest

  // the motherload split, these must be performed by the parent of the node that is full
  // both regular nodes AND the parent node may perform this kind of split, DO NOT MAKE IT VIRTUAL!!!!
  void split(--arguments will vary depending on how you want to handle regular node splits--, must have at least: int position);


  // you may use Kruse's copy_predecessor() page 552
  // There are many ways to write this function, it does not have to be recursive
  get_predecessor(int position);

  // remove a value from a leaf node
  void remove_data(int position);


  // perform the array operation of removing a value
  void remove_data(int position);               // used on the leaf node that has value to delete

  // used by delays:
  bool can_borrow();                            // returns m_count > m_min ?
  void remove_smallest(int& value);         // remove smallest value/ptr from a node, return as output parameters
  void reomve_largest(int& value);          // same except do so for the largest value/ptr

  // functions needed for a combine
  void combine(int position);                   // full 3 node to 2 node combine
  void remove_data_pointer(int position);       // used by Node::combine() to remove the node that gets deleted


  // virtual destructor to satisfy the compiler warnings
  virtual ~Node() {};
};

Your Broot class will need to have a root_combine()

class Broot : public Node
{
    public:

      // constructor, needs order and id
      B_root(int order, int id);

      // needs the value and pointer that aren't in any node yet
      void root_split(int value, Node* pointer);


      // does not have arguments.  Think about where btree::remove() will call this!
      void root_combine();


      // destructor, probably never gets called
      ~B_root();
};


///////////////////////////////////////////////////////////////////////////////////////////////////
// you need this to signal previous callers of pushdown() what happened during the recursive call
// Be carefull of naming these status or result because those are already
// defined by the standard namespace and you will get a strange compile error
///////////////////////////////////////////////////////////////////////////////////////////////////
enum BTREE_RESULT
{
  BTREE_DUPLICATE,   
  BTREE_OVERFLOW,    
  BTREE_SPLIT,
  BTREE_SUCCESS,
  BTREE_NOT_PRESENT
};

class Btree
{

    public:
        // these functions are your public interface to main.cpp
        /* A */     bool insert(int value);
        /* D */     bool delete(int value);
        /* S */     bool search(int value);
        /* P */     bool print();
        /* V */     bool verbose();


        // constructor
        B_tree(int order);                  // only should need order;


    // the non-public functions do all the heavy lifting
    private:

        // member variables, not many
        Node* m_root;
        int m_order;


        // recursive search function
        void search(int value, Node* node, bool& success);     // look familiar?

        // recursive print functions
        void print(Node* node);
        void verbose(Node* node);


        // this is a very important utility function!
        // returns true on duplicate value, else position is set to the pointer in m_branch[] to follow next
        bool search_node(Node* node, int value, int& position);


        // finally the one you've all been waiting for:
        // returns a message to the caller, insert_value and insert_branch are the value that MIGHT come 
        //   from lower in the tree due to splits OR when you bottom out.
        // Notice the use of "output" parameters again.
        BTREE_RESULT push_down(Node* node, int new_value, new_pointer; int& insert_value, Node*& insert_branch);


        // the recursive remove function, does not have to use signals
        void remove(Node* node, int value, bool& success);

};