CS 228 meeting -*- Outline -*- * dynamic data implementations of linked lists ** why pointers are needed (HR 8.2) ------------------------------------------ WHY POINTERS? // Wrong.h #include "String.h" struct GuestNode { String name; GuestNode link; // ??? }; // GuestNode.h #include "String.h" struct GuestNode { String name; GuestNode *link; }; typedef GuestNode *GuestPtr; // GuestNode2.h #include "String.h" struct GuestNode; // forward decl. typedef GuestNode *GuestPtr; struct GuestNode { String name; GuestPtr link; }; ------------------------------------------ Note that GuestNode2.h shows general way to do recursive decls. ** basics of linked list manipulation (HR 8.2) ------------------------------------------ USAGE (C++ DETAILS) // GuestNode.h #include "String.h" struct GuestNode { String name; GuestNode *link; }; typedef GuestNode *GuestPtr; // GuestClient.C #include "GuestNode.h" #include int main() { GuestPtr head = new GuestNode; (*head).name = String("Jones"); head->name = String("Jones"); head->link = new GuestNode; head->link->name = String("Smith"); head->link->link = NULL; return 0; } ------------------------------------------ draw pictures! point out the convenience of the -> operator when working with a pointer to struct ** traversals (HR 8.3) ------------------------------------------ PROBLEM Implement the following specification // PrintRegister.h #include #include "GuestNode.h" extern void PrintRegister(GuestPtr head); // PRE: head is a valid pointer // MODIFIES: cout // POST: cout has the names in the // list head added to it, in order // from the head to end, one per line. ------------------------------------------ // PrintRegister.C #include "PrintRegister.h" #include void PrintRegister(GuestPtr head) { GuestPtr current = head; // INV: all guests before current have been printed // and current is NULL or pointing at an item in the list staring with head while (current != NULL) { cout << current->name << endl; current = current->link; } } draw pictures Q: Why can't you use current++ to go to the link node? draw picture with addresses if needed Q: can you write that with a recursion? Q: How would you print the list in reverse order? say, because we wanted to see who had been around longest ------------------------------------------ FOR YOU TO DO Implement the following specification // NumGuests.h #include "GuestNode.h" extern int NumGuests(GuestPtr head); // PRE: head is a valid pointer // POST: FCTVAL == the number of guests // in the list starting at head. ------------------------------------------ Q: How is this like Scheme? Q: Can you write IsNull, Car, and Cdr for these lists? Q: How would you write Cons? ------------------------------------------ HIGHER-LEVEL (SCHEME-LIKE) LIST OPS // GuestListOps.h #include "bool.h" #include "GuestNode.h" extern Boolean IsNull(GuestPtr head); // POST: FCTVAL == (head == 0) extern String& Car(GuestPtr head); // MODIFIES: cerr // POST: IF head == 0 // THEN give error and stop program // ELSE return the name cell of head extern GuestPtr& Cdr(GuestPtr head); // MODIFIES: cerr // POST: IF head == 0 // THEN give error and stop program // ELSE return the link cell of head extern GuestPtr Cons(String s, GuestPtr rest); // MODIFIES: cerr // POST: IF can't allocate // THEN give error and stop program // ELSE return pointer to new GuestNode // initialized with s and rest ------------------------------------------ // GuestListOps.C #include #include #include "GuestListOps.h" Boolean IsNull(GuestPtr head) { return (head == NULL); } // Note: return reference so can assign to it String& Car(GuestPtr head) { assert (head != NULL); return head->name; } // Note: similarly return reference so can assign to it GuestPtr& Cdr(GuestPtr head) { assert (head != NULL); return head->link; } GuestPtr Cons(String s, GuestPtr rest) { GuestPtr result = new GuestNode; assert (result != NULL); result->name = s; result->link = rest; return result; } Could also treat new errors using set_new_handler (see ../pointers-dynamic/dynamic-data.txt) ** insertion (HR 8.4) *** with head pointer variable as argument ------------------------------------------ PROBLEM (INSERTION INTO LIST) Implement the following specification: // InsertAlphabetic.h #include "GuestNode.h" extern void InsertAlphabetic ( GuestPtr& head, const String & gName); // PRE: head contains a valid pointer // && the list starting at head is // in nondecreasing order. // MODIFIES: head or one element // of the list. // POST: gName is in the list starting // at head && it's still nondecreasing. ------------------------------------------ // InsertAlphabetic.C #include #include "InsertAlphabetic.h" #include "GuestListOps.h" void InsertAlphabetic ( GuestPtr& head, const String & gName) { if (IsNull(head) || gName < Car(head)) { head = Cons(gName, head); } else { InsertAlphabetic(Cdr(head), gName); // Note: Cdr returns a reference! } } //// Nonrecursive, need to use pointers because can't unseat references (ugh) // InsertAlphabetic_NonRecursive.C #include #include "InsertAlphabetic.h" #include "GuestListOps.h" void InsertAlphabetic ( GuestPtr& head, const String & gName) { GuestPtr * current = &head; // INV: *current contains NULL or points to an element of the list starting // at head && all the elements before *current are no greater than gName while (!IsNull(*current) && gName < Car(*current)) { current = &((*current)->link); } // ASSERT: *current contains NULL or points to an element of the list starting // at head && all the elements before *current are no greater than gName // && *current != NULL --> gName < Car(*current) *current = Cons(gName, *current); } See the book for how to do things when you don't take the point of view that the pointer variable is an argument, as is done above. ------------------------------------------ FOR YOU TO DO Implement the following specification: // Snoc.h #include "GuestNode.h" extern void Snoc ( GuestPtr& head, const String& gName); // MODIFIES: head or one element // of the list. // POST: gName is at the end of the list // starting at head. ------------------------------------------ // Snoc.C #include "Snoc.h" #include "GuestListOps.h" void Snoc ( GuestPtr& head, const String & gName) { if (IsNull(head)) { head = Cons(gName, head); } else { Snoc(Cdr(head), gName); // Note: Cdr returns a reference! } } *** without head pointer variable as argument ------------------------------------------ PROBLEM Suppose instead of a struct for nodes, we have an object of the class, and can't get a reference to the link member. How to insert? ------------------------------------------ Have to stand off by having pointer to list node. Draw pictures to show the 2 cases: have pointer to node that would be the old head have the head variable (as special case :-( ) ------------------------------------------ INSERTION INTO LIST WITHOUT HEAD POINTER VARIABLE Implement the following specification: // InsertAlphabetic2.h #include "GuestNode.h" extern void InsertAlphabetic2 ( GuestPtr prevPtr, const String& gName); // PRE: prevPtr contains a valid pointer // && the list starting at prevPtr is // in nondecreasing order // && prevPtr->name <= gName // MODIFIES: one element of the list. // POST: gName is in the list starting // at head && it's still nondecreasing. ------------------------------------------ // InsertAlphabetic2.C #include "InsertAlphabetic2.h" #include "GuestListOps.h" void InsertAlphabetic2 ( GuestPtr prevPtr, const String& gName) { if (IsNull(Cdr(prevPtr)) || gName < Car(Cdr(prevPtr))) { prevPtr->link = Cons(gName, prevPtr->link); } else { InsertAlphabetic2(Cdr(prevPtr), gName); } } ** deletion (HR 8.4, pp. 355ff) *** with head pointer variable as argument ------------------------------------------ PROBLEM (DELETION FROM LIST) Implement the following specification // CheckOut.h #include "GuestNode.h" extern void CheckOut ( GuestPtr& head, const String& gName); // PRE: head contains a valid pointer // && gName is in the list starting // at head (exactly once). // MODIFIES: head or one node of list. // POST: gName is no longer in the // list starting at head && that node is // returned to the free store. ------------------------------------------ // CheckOut.C #include "CheckOut.h" #include "GuestListOps.h" void CheckOut ( GuestPtr& head, const String& gName ) { if (Car(head) == gName) { GuestPtr tempPtr = head; head = Cdr(head); delete tempPtr; } else { CheckOut (Cdr(head), gName); } } Q: How is this different from what you did in the first part of 227? it doesn't make a new list, but mutates an old one *** without head pointer variable ------------------------------------------ DELETION FROM LIST WITHOUT HEAD POINTER VARIABLE FOR YOU TO DO Implement the following: // CheckOut2.h #include "GuestNode.h" extern void CheckOut2 ( GuestPtr prevPtr, const String& gName); // PRE: prevPtr is a valid pointer // && gName is in the list following // prevPtr (exactly once). // MODIFIES: one node of list. // POST: gName is no longer in the // list following prevPtr && that node is // returned to the free store. ------------------------------------------ // CheckOut2.C #include "CheckOut2.h" #include "GuestListOps.h" void CheckOut2 ( GuestPtr prevPtr, const String& gName ) { if (Car(Cdr(prevPtr)) == gName) { GuestPtr tempPtr = Cdr(prevPtr); prevPtr->link = Cdr(Cdr(prevPtr)); delete tempPtr; } else { CheckOut2(Cdr(prevPtr), gName); } } Q: What to do if you want to delete the first node? special case, draw it ** head nodes (HR 8.5) ------------------------------------------ LINKED LISTS WITH HEAD NODES If don't have pointer variable for head, no way to insert before the first node. Solution, have the first node be a dummy "head node", so will always ------------------------------------------ ... have a previous node This is an instance of the general idea: avoiding case analysis, or making everything look the same. draw a picture ------------------------------------------ Picture: Cautions: - don't ever delete ------------------------------------------ ... the head node Q: How do you print such a list? skip the head, in general true for traversals ** doubly-linked and circular lists (8.6) we'll leave the details of this to exercises *** doubly-linked lists ------------------------------------------ PROBLEM Return the node preceeding a given node, want to do this in O(1) time. ------------------------------------------ Useful for insertion and deletion, as we've seen. Might also be useful in an editor (hint) if wanted to back up a line. Problem is it will be O(n) in length of list so far. ------------------------------------------ Solution: doubly linked list // DblGuestNode.h #ifndef DblGuestNode_h #define DblGuestNode_h 1 #include "String.h" struct DblGuestNode { String name; DblGuestNode *nextLink; DblGuestNode *prevLink; }; typedef DblGuestNode *DblGuestPtr; #endif Picture: ------------------------------------------ draw picture show how to do insert in picture. ------------------------------------------ PROBLEM // InsertAfter.h #include "DblGuestNode.h" extern void InsertAfter(DblGuestPtr prev, const String & gName); // PRE: prev is a legal pointer // && there is enough storage available // MODIFIES: the node pointed at by prev // and the following node, if any // POST: the list following prev has // gName after prev, and then the node // following prev originally, if any // InsertAfter.C #include "InsertAfter.h" #include void InsertAfter(DblGuestPtr prev, const String & gName) { ------------------------------------------ write the following, and draw pictures as you go. // allocate the new node, and initialize it DblGuestPtr temp = new DblGuestNode; temp->name = gName; temp->nextLink = prev->nextLink; temp->prevLink = prev; // hook up the node following prev, if it exists if (prev->nextLink != NULL) { prev->nextLink->prevLink = temp; } // hook up prev prev->nextLink = temp; } *** circular lists ------------------------------------------ CIRCULAR LISKED LISTS Convenient for implementing unordered ADTs Problem: need to be careful in traversals! Picture: Variation with tail pointer: ------------------------------------------ Q: in the variation, how do you find the first node? the last? Q: What ADT would this be useful in implementing? Q: Can you have a doubly-linked circular list? you bet