CS 228 meeting -*- Outline -*- * binary trees and binary search trees (HR 13.2-4) ** binary trees (HR 13.2) ------------------------------------------ BINARY TREES (HR 13.2) Motivating problems: Searching or sorting a collection that has lots of inserts and deletes to process ------------------------------------------ Want to be able to insert, delete, and search in O(log N) time; and want to be able to get output in sorted order. E.g., hotel guest list Other applications: expression trees, etc. *** definitions ------------------------------------------ def: a *binary tree* is a tree such that each node has Examples: A Z L B C X I Y N W V E ------------------------------------------ ... at most 2 children, which are distinguished as left and right children Also note that the trees A A B B are distinct, because in one B is the left child, and in the other it is the right. *** example problem, design, and implementation **** problem ------------------------------------------ PROBLEM Design a guest register for a hotel that supports average time O(log N) insertion, deletion, and searching, and permits outputs in sorted order. Information to maintain on Guests: name ------------------------------------------ Q: What other information might one maintain? Address RoomNumber Charges might also want a list of charges and reasons credit card number etc. Q: What should the types of these be? use String, String, int, float We'll keep things reasonably simple ------------------------------------------ GUEST INFORMATION STRUCT // GuestInfo.h #ifndef _GuestInfo_h #define _GuestInfo_h 1 #include "String.h" struct GuestInfo { String name; String address; int roomNumber; float charges; }; #endif ------------------------------------------ **** design ------------------------------------------ LAYERED DESIGN OF SOLUTION main GuestDatabase DisplayMenu GuestRegister GuestRegister Insert Delete SearchFor InOrderPrint GuestTree IOStuff ------------------------------------------ Draw boxes etc. for Booch diagram Note that the stuff on the left is ops of GuestRegister. Explain GuestDatabase handles the menu choices, it's a module, not a class GuestRegister is a class that abstractly is a set GuestTree is the binary tree stuff we'll use. Q: What does each module hide? main hides command interface, control of program GuestDatabase hides guestDB instance of GuestRegister GuestRegister hides the main algorithms (searching, printing) GuestTree hides implementatation details of trees. *** implementation layers **** main ------------------------------------------ // test.C #include #include #include #include "prompt.h" #include "GuestDatabase.h" #include "DisplayMenu.h" int main() // MODIFIES: cin, cout, cerr { char cmd; // prompt DisplayMenu(); prompt("your choice? "); cin >> cmd; while (cin) { cin.ignore(100, '\n'); switch (tolower(cmd)) { case 'a': AddGuest(); break; case 'b': PrintSortedGuestList(); break; case 'c': SearchForGuest(); break; case 'd': DeleteGuest(); break; case 'e': return(EXIT_SUCCESS); break; default: cerr << "Error: unknown command" << endl; break; } // prompt DisplayMenu(); prompt("your choice? "); cin >> cmd; } } ------------------------------------------ That's the top level, it just passes the buck to stuff in the module GuestDatabase **** GuestDatabase ------------------------------------------ // GuestDatabase.h #include "GuestInfo.h" extern void AddGuest(); // MODIFIES: cout, cin // POST: prompts are added to cout, // and information about a guest is ____ // ________. This information is stored // in the database. extern void DeleteGuest(); // MODIFIES: cout, cin, cerr // POST: a prompt is added to cout, and // a guest name is read from cin. The // guest is removed if in the database, // and an error message is issued if // the guest is not in the database. extern void SearchForGuest(); // MODIFIES: cout, cin, cerr // POST: a prompt is added to cout, and // the a guest name is read from cin. // The guest's information is added // to cout if the guest is found, // otherwise an error is added to cerr. extern void PrintSortedGuestList(); // MODIFIES: cout // POST: a list of guest information is // added to cout; the information is in // ascending order by _______________. ------------------------------------------ The implementation stores the static variable which is made a class so it can be automatically initialized. ------------------------------------------ // GuestDatabase.C #include #include "bool.h" #include "prompt.h" #include "IOStuff.h" #include "GuestDatabase.h" #include "GuestRegister.h" static GuestRegister guestDB; void AddGuest() { GuestInfo g; prompt("Guest name? "); cin >> g.name; Boolean found; guestDB.SearchFor(g.name, found, g); if (found) { cerr << "Already registered as:\n" << g; } else { prompt("address (on one line)? "); cin >> g.address; prompt("room number? "); cin >> g.roomNumber; cin.ignore(100, '\n'); g.charges = 0.0; guestDB.Insert(g); } } ------------------------------------------ PrintSortedGuestList just calls guestDB.InOrderPrint(); ------------------------------------------ void SearchForGuest() { String name; prompt("Guest name? "); cin >> name; GuestInfo g; Boolean found; guestDB.SearchFor(name, found, g); if (found) { cout << g; } else { cerr << "Error: guest " << name << " not found!" << endl; } } void DeleteGuest() { String name; prompt("Guest name? "); cin >> name; Boolean found; guestDB.Delete(name, found); if (!found) { cerr << "Error: guest " << name << " not found!" << endl; } } ------------------------------------------ Nothing terribly interesting here, just forwarding calls **** GuestRegister (part 1) ------------------------------------------ // GuestRegister.h #ifndef _GuestRegister_h #define _GuestRegister_h 1 #include "bool.h" #include "GuestInfo.h" #include "GuestTree.h" class GuestRegister { public: // ABSTRACTLY: a set of guest // information records, // with only one record with // a given guest name. GuestRegister(); // MODIFIES: self // POST: self is the empty set void Insert(const GuestInfo & g); // PRE: no record with g.name is in self // MODIFIES: self // POST: void Delete(const String & name, Boolean & found); // MODIFIES: self // POST: // && found --> void SearchFor(const String & name, Boolean & found, GuestInfo& g) const; // MODIFIES: found, g // POST: found --> // void InOrderPrint() const; // MODIFIES: cout // POST: // // #include "GuestRegister.pri" }; #endif ------------------------------------------ Skip search and InOrder print for now. ------------------------------------------ // GuestRegister.pri private: GuestTree theTree; // ABSTRACTION MAP: the set of guest // information records consists of // the nodes of theTree ------------------------------------------ **** GuestTree, representing binary trees (HR 2. 611) ------------------------------------------ REPRESENTING BINARY TREES #include "GuestInfo.h" struct TreeNode { GuestInfo data; TreeNode *lLink; // left child TreeNode *rLink; // right child // constructor TreeNode( const GuestInfo & g, TreeNode *l, NodePtr *r); }; ------------------------------------------ Note: it's unusual to have a constructor in a struct, but this is convenient, like Cons... draw pictures ** operations on binary trees this is something like operations on dynamic linked lists ------------------------------------------ ADDING A LEAF TO A BINARY TREE struct TreeNode { GuestInfo data; TreeNode *lLink; // left child TreeNode *rLink; // right child // constructor TreeNode( const GuestInfo & g, TreeNode *l, NodePtr *r); }; typedef TreeNode *NodePtr; Before: After: void AppendLeft( NodePtr ptr, const GuestInfo & g ) { } ------------------------------------------ ptr->lLink = new TreeNode( g, NULL, NULL); Q: What would you change to make this AppendRight? ------------------------------------------ OTHER OPERATIONS ON BINARY TREES #include "GuestTree.h" GuestInfo Data( NodePtr ptr ) { return ptr->data; } void Store( NodePtr ptr, const GuestInfo & g ) { ptr->data = g; } NodePtr LChild( NodePtr ptr ) { return (ptr==NULL) ? NULL : ptr->lLink; } NodePtr RChild( NodePtr ptr ) { return (ptr==NULL) ? NULL : ptr->rLink; } Boolean IsLeaf( NodePtr ptr ) { } ------------------------------------------ ... return (ptr->lLink == NULL && ptr->rLink == NULL); The above are all manipulations that are not at the root. We need to handle the root as a special case, by analogy to linked list operations, ------------------------------------------ WORKING AT THE ROOT // GuestTree.h #include "bool.h" #include "GuestInfo.h" struct TreeNode; typedef TreeNode* NodePtr; class GuestTree { public: // ABSTRACTLY: a tree of guest // information records GuestTree(); // MODIFIES: self // POST: self is the empty tree Boolean IsEmpty() const; // POST: FCTVAL == (tree has no nodes) void BuildRoot( const GuestInfo & g ); // PRE: IsEmpty() // MODIFIES: self // POST: Tree has root node, call it R // && Contents(R) == g && IsLeaf(R) NodePtr Root() const; // POST: FCTVAL == NULL, if IsEmpty() // == pointer to root node, otherwise #include "GuestTree.pri" }; // ... declarations of stuff seen before ------------------------------------------ Here's the private part ------------------------------------------ // GuestTree.pri private: NodePtr rootPtr; // ABSTRACTION MAP: if rootPtr == NULL // then the tree is empty, // otherwise the tree contains the // record in *rootPtr and all records // found by following its pointers ------------------------------------------ ------------------------------------------ FOR YOU TO DO Implement the class GuestTree, starting as follows // GuestTree.C #include #include "IOStuff.h" #include "GuestTree.h" struct TreeNode { GuestInfo data; NodePtr lLink; // left child NodePtr rLink; // right child // constructor TreeNode( const GuestInfo &, NodePtr, NodePtr ); }; ------------------------------------------ /////////////////////// TreeNode::TreeNode( const GuestInfo & g, NodePtr leftPtr, NodePtr rightPtr ) : data(g), lLink(leftPtr), rLink(rightPtr) {} GuestTree::GuestTree() { rootPtr = NULL; } Boolean GuestTree::IsEmpty() const { return (rootPtr == NULL); } void GuestTree::BuildRoot( const GuestInfo & g) { rootPtr = new TreeNode(g, NULL, NULL); } NodePtr GuestTree::Root() const { return rootPtr; } ////////////////////////// ** binary search trees (HR 13.3) ------------------------------------------ INSERTION AND SEARCHING Problem: how to insert in O(log N) time, so can search in O(log N) time (on average). Data structure: binary search tree def: a *binary search tree* is a binary tree such that: 1. 2. 3. Pictures: ------------------------------------------ ... 1. no two nodes contain the same key value 2. the keys are totally ordered by <, ==, and > 3. the key in each node is: - greater than every key in its left subtree - less than every key in its right subtree Q: What has to be true about a child node? if it's a left child, then it's less than its parent, if it's a right child, then it's greater than its parent Draw examples and counterexamples (as is Fig. 13.10) *** insertion ------------------------------------------ // GuestRegister.C #include "GuestRegister.h" GuestRegister::GuestRegister() : theTree() { } void GuestRegister::Insert( const GuestInfo & g) { } ------------------------------------------ develop the following by figuring out: the cases, for the second case: what has to happen at the end and working backwards { if (theTree.Root() == NULL) { theTree.BuildRoot(g); } else { NodePtr prevPtr = theTree.Root(); NodePtr probePtr = prevPtr; // INV: prevPtr != NULL while (probePtr != NULL) { prevPtr = probePtr; if (g.name < Data(probePtr).name) { probePtr = LChild(probePtr); } else { // ASSERT: g.name > Data(probePtr).name probePtr = RChild(probePtr); } } // ASSERT: probePtr == NULL && prevPtr != NULL // && g.name < Data(prevPtr).name --> LChild(prevPtr) == NULL // && g.name > Data(prevPtr).name --> RChild(prevPtr) == NULL if (g.name < Data(prevPtr).name) { AppendLeft(prevPtr, g); } else { AppendRight(prevPtr, g); } } } *** deletion (skip) not covered in the book Needs a few more capabilities in GuestTree.[Ch] either that, or lose the abstraction see the example on-line for details *** searching (HR 617) ------------------------------------------ FOR YOU TO DO Implement the following: // GuestRegister.C (continued) void GuestRegister::SearchFor( const String & name, Boolean & found, GuestInfo& g) const { } ------------------------------------------ Q: How is this similar to binary search? Q: What's the time efficiency? O(log N) if the tree is balanced. O(N) in worst case ** traversals (HR 13.4) ------------------------------------------ TRAVERSALS Motivating problems: - print information on the the guests in alphabetic order - convert from infix to prefix notation - delete all the nodes in a tree Gary Cecil Hillary Ben Eve Ivan Ada Dan Fricka Lenny Jim Karl def: an *inorder* traversal visits def: a *preorder* traversal visits def: a *postorder* traversal visits ------------------------------------------ ... the left subtree, then the root, then the right ... the the root, then the left subtree, then the right ... the left subtree, then the right, then the root Q: can you give the output from each on this tree? ------------------------------------------ INORDER TRAVERSAL // GuestTree.h #include "bool.h" #include "GuestInfo.h" struct TreeNode; typedef TreeNode* NodePtr; // ... extern void PrintInOrder( NodePtr ptr ); // POST: information about the subtree // at ptr is printed to cout in order. // GuestTree.C #include #include "IOStuff.h" #include "GuestTree.h" // ... void PrintInOrder( NodePtr ptr ) { } ------------------------------------------ if (ptr != NULL) { PrintInOrder(LChild(ptr)); GuestInfo temp = ptr->data; cout << temp; PrintInOrder(RChild(ptr)); } Q: What change to this code would you make to print in preorder? In postorder? ** destructor (HR p. 624) Q: What kind of traversal is appropriate for writing a destructor? postorder Q: Can you write it? ** copy constructor Q: What kind of traversal is appropriate for writing a copy constructor? inorder Q: Can you write it?