// Computer Science 367, Section 3, Fall 1997 // Instructor: Michael Siff (siff@cs.wisc.edu) // // Programming Assignment One: Solution // // // FILE: list.C // // // A list template class using linked lists. // // // INVARIANT for List: // // The first element (the front of the list - a ListItem) is pointed to by // the member variable head. The last element (the back of the list - a // ListItem) is pointed to by the member variable tail. So head->prev and // tail->next should always end up being NULL. // // The list cursor, indicated by the member variable location, either // points to an element on the list (a ListItem), or to NULL, indicating // that it is at the end of the list. // // Each element of the list is represented by the class ListItem, // a friend of the List class. // // A ListItem stores the ItemType in its member variable val, and // has two pointers: prev points to the previous element on the list or // NULL if it is the head of the list; next points to the next element on // the list or NULL if it is the last element on the list. #include // Provides assert #include // Provides size_t, NULL // PRIVATE MEMBER FUNCTIONS (utilities) // ------------------------ template void List::deleteList() // Library facilities used: stdlib.h // Releases the dynamic memory used by the nodes of the list. { ListItem *temp; location = head; while(location!=NULL) { temp = location->next; delete location; location = temp; } location = tail = head = NULL; count = 0; } template void List::copyList(const List& source) // Library facilities used: stdlib.h // Makes a complete copy of the source list to the activating list. { ListItem *p,*q; deleteList(); // delete whatever was in this list location = tail = head = NULL; capacity = source.capacity; for(p=source.head; p != NULL; p=p->next) append(p->val); for(q=head, p=source.head; p != NULL; p=p->next, q=q->next) { if (source.location==p) { location = q; break; } } } // CONSTRUCTORS // ------------ template List::List(size_t cap) // Library facilities used: stdlib.h : capacity(cap), count(0), head(NULL), tail(NULL), location(NULL) { } template List::List(const List& source) // copy constructor { head = NULL; copyList(source); } // DESTRUCTOR // ---------- template List::~List() { deleteList(); } // CONSTANT MEMBER FUNCTIONS // ------------------------- template bool List::isEmpty() const // Library facilities used: stdlib.h // Returns true if and only if the list is empty, indicated by the head // member variable being a null pointer. { return(head==NULL); } template bool List::isFull() const // Returns true if and only if the list is full, indicated by the number of // items on the list (the length) being equal to the member variable // capacity. { return(length()==capacity); } template size_t List::length() const // Returns the number of items on the list { return count; } template bool List::isFront() const // Returns true if and only if the cursor is at the front of the list, // indicated by the location member variable being the same as the head. { return (location==head); } template bool List::isEnd() const // Library facilities used: stdlib.h // Returns true if and only if the cursor is at the end of the list, // indicated by the location member being a null pointer. { return (location==NULL); } template bool List::isLast() const // Returns true if and only if the cursor is at the last item on the list, // indicated by the location member variable being the same as the tail and // the list not being empty. { return ((!isEmpty()) && (location==tail)); } template bool List::isMember(const ItemType& item) const // Library facilities used: stdlib.h // Returns true if and only if the item is in the list. { bool result = false; for(ListItem *p = head; !result && p!=NULL; p=p->next) result = (p->val == item); return result; } template ItemType List::current() const // Library facilities used: assert.h // Returns the item pointed to by the cursor (assuming the cursor is not at // the end of the list). { assert(!isEnd()); return location->val; } template bool List::operator==(const List& operand) const // Library facilities used: stdlib.h // Returns true if and only if the two lists have the same items in the // same order. { bool result = (length() == operand.length()); ListItem *p=head; ListItem *q=operand.head; for(; result && p!=NULL; p=p->next, q=q->next) result = (p->val == q->val); return result; } // MODIFICATION MEMBER FUNCTIONS // ----------------------------- template void List::reset() // Sets the cursor to the front of the list { location = head; } template void List::atEnd() // Library facilities used: stdlib.h // Sets the cursor to the end of the list { location = NULL; } template void List::advance() // Library facilities used: assert.h // Advances the cursor to next item, assuming its not at end of list { assert(!isEnd()); location=location->next; } template void List::back() // Library facilities used: assert.h // Moves the cursor to previous item, assuming its not at front of list; // if the cursor was at the end of the list, the cursor now points to the // last item on the list. { assert(!isFront()); if (isEnd()) location=tail; else location=location->prev; } template void List::find(const ItemType& item) // Library facilities used: stdlib.h // Places the cursor is at the first occurrence of the item at or after the // previous cursor location. If the item is not at the list, the cursor is // placed at the end. { for(; location!=NULL && location->val!=item ; location=location->next); } template void List::insert(const ItemType& entry) // Library facilities used: assert.h // Inserts entry at the front of the list. A new ListItem is allocated and // assigned the value entry. If the list was empty then head, tail, and // location now point to the newly allocated item. Otherwise, head points // to the new item, its next field point to the old head, the old head's // prev field points to the new head. { assert(!isFull()); // special case for empty list if (isEmpty()) location = tail = head = new ListItem; else { ListItem *temp = head; head = new ListItem; head->next = temp; temp->prev = head; } head->val = entry; count++; } template void List::addBeforeCurrent(const ItemType& entry) // Library facilities used: assert.h // Inserts entry in front of the cursor. A new ListItem is allocated and // assigned the value entry. // There are three special cases: // 0. The list was empty: head and tail now point to the newly allocated item // 1. The cursor was at front of the list: head points to the new item, the // next field of the old head points to the the new head, the old head's // prev field points to the new head. // 2. The cursor was at the end of the list: the last item's next now // points to the new item, the new item's prev points to the former last item. // // Otherwise: the current item's prev points to the new item, the new // item's next points to the current item. { assert(!isFull()); // special case for empty list if (isEmpty()) { tail = head = new ListItem; head->val = entry; } else { ListItem *temp = new ListItem; if(isFront()) { // special case for cursor at front temp->next = head; head->prev = temp; head = temp; } else if (isEnd()) { // special case for cursor at end temp->prev = tail; tail->next = temp; } else { temp->prev = location->prev; temp->prev->next = temp; temp->next = location; location->prev = temp; } temp->val = entry; } count++; } template void List::addAfterCurrent(const ItemType& entry) // Library facilities used: assert.h // Inserts entry in back of the cursor, assuming cursor not at end // A new ListItem is allocated and assigned the value entry. // The current item's next points to the new item, the new // item's prev points to the current item. If the cursor is at the last // item then the tail is now the new item. { assert(!isFull() && !isEnd()); ListItem *temp = new ListItem; temp->prev = location; temp->next = location->next; location->next = temp; if (location == tail) tail = temp; else temp->next->prev = temp; temp->val = entry; count++; } template void List::append(const ItemType& entry) // Library facilities used: assert.h // Appends entry to end of list. A new ListItem is allocated and assigned // the value entry. If list was empty, location, tail, and head now point // to the newly allocated item. Otherwise, the last item's next points to // the new item, the new item's prev points to that last item. { assert(!isFull()); // special case for empty list if (isEmpty()) { location = tail = head = new ListItem; head->val = entry; } else { ListItem *temp = tail; tail = new ListItem; tail->prev = temp; temp->next = tail; } tail->val = entry; count++; } template void List::remove() // Library facilities used: assert.h // Removes entry at cursor. Cursor points to next item (or end if cursor // was at last item). { assert(!isEnd()); // make a note if cursor is at front of list bool wasFront = isFront(); // connect previous element to next element if (isLast()) tail = location->prev; else location->next->prev=location->prev; if (!wasFront) location->prev->next=location->next; // remember where the next element is ListItem *temp = location->next; // the removal: free memory associated with current list element // and decrement the count delete location; count--; // reassign the cursor to the next element location = temp; // if cursor was originally at front, adjust the head pointer if (wasFront) head = location; } template void List::operator=(const List& source) { if (source.head == head) // check to see if assignment is to itself return; // exit if so copyList(source); }