460 likes | 495 Views
Lists, Stacks, & Queues. Ch. 4. HW #3. Ch. 4: Exercises: 4.4, 4.6 and Project: 4.8 Due: November 3, 2009. Chapter Objectives. Learn about Lists – ADT & implementation Array based List Linked List Dictionary ADT Learn about: Queues and Stacks as restricted forms of Lists.
E N D
Lists, Stacks, & Queues Ch. 4
HW #3 • Ch. 4: Exercises: 4.4, 4.6 and Project: 4.8 • Due: • November 3, 2009
Chapter Objectives • Learn about Lists – ADT & implementation • Array based List • Linked List • Dictionary ADT • Learn about: • Queues and Stacks as restricted forms of Lists
What is a List? A list is a finite, orderedsequence of data items. By ordered, we mean that each List elements has a position. Note that this is different from saying Sorted list. Notation: <a0, a1, …, an-1> Definitions: empty, length, head, tail… What operations should we implement?
List Implementation Concepts The list implementation will support the concept of a current position. The list has left and right partitions separated by the fence. The fence corresponds to the current position • Either or both partitions may be empty. <20, 23 | 12, 15>
Operations Examples <20, 23 | 12, 15> • Insert 10: <20, 23 | 10 12, 15> • Append 18: <20, 23 | 10 ,12, 15 18> • Remove(x): <20, 23 | 12, 15 18>, x is 10 • getvalue(x): <20, 23 | 12, 15 18>, x is 12 • next: <20, 23, 12 | 15 ,18> • Prev: <20, 23 | 12, 15, 18> • Leftlength is 2 • Rightlength is 3
Operations Examples(cont) • Setstart < | 20, 23 ,12, 15 ,18> • Setend < 20, 23 12, 15 18 | > • Setpos(2) < 20, 23 | 12, 15 18 > • Setpos(4) < 20, 23 12, 15 | 18 > • Clear <|>
Pure Virtual Function A pure virtual function is a function that must be overridden in a derived class and need not be defined. A virtual function is declared to be "pure" using the curious "=0" syntax. For example: class Base { public: void f1(); // not virtual virtual void f2(); // virtual, not pure virtual void f3() = 0; // pure virtual }; Base b; // error: pure virtual f3 not overridden Here, Base is an abstract class (because it has a pure virtual function), so no objects of class Base can be directly created: Base is (explicitly) meant to be a base class. For example: class Derived : public Base { // no f1: fine // no f2: fine, we inherit Base::f2 void f3(); }; Derived d; // ok: Derived::f3 overrides Base::f3 Abstract classes are immensely useful for defining interfaces. In fact, a class with only pure virtual functions is often called an interface.
This ADT Defines a List interface through pure virtual functions List ADT template <class Elem> class List { public: virtual void clear() = 0; virtual bool insert(const Elem&) = 0; virtual bool append(const Elem&) = 0; virtual bool remove(Elem&) = 0; virtual void setStart() = 0; //set current position to beginning virtual void setEnd() = 0; //set current position to end virtual void prev() = 0; //move current position to previous element virtual void next() = 0; //move current position to next element virtual intleftLength() const = 0; virtual intrightLength() const = 0; virtual boolsetPos(int pos) = 0; virtual boolgetValue(Elem&) const = 0; //first element of right partition virtual void print() const = 0; };
Using getValue() • Useful to tell when sequential processing is complete, e.g. For (myList.setStart(); myList.getvalue(it); myList.next()) doSomething(it);
Array-Based List Insert In the average inserting or removing requires moving half the elements : (n) Print is also (n)
Array-Based List Class (1) template <class Elem> // Array-based list class AList : public List<Elem> { private: int maxSize; // Maximum size of list int listSize; // Actual elem count int fence; // Position of fence Elem* listArray; // Array holding list public: AList(int size=DefaultListSize) { maxSize = size; listSize = fence = 0; listArray = new Elem[maxSize]; } // continued using C++ code
Print for Array List void print() const { int temp = 0; cout << "< "; while (temp < fence) cout << listArray[temp++] << " "; cout << "| "; while (temp<listSize) cout << listArray[temp++] << " "; cout << ">\n"; }
Insert for Array List template <class Elem> // Insert at front of right partition bool AList<Elem>::insert(const Elem& item) { if (listSize == maxSize) return false; // List is full for(int i=listSize; i>fence; i--) // Shift Elems up listArray[i] = listArray[i-1]; // to make room listArray[fence] = item; listSize++; // Increment list size return true; }
Int Class // Your basic int type as an object. class Int { private: int val; public: Int(int input=0) { val = input; } // The following is for those times when we actually // need to get a value, rather than compare objects. int key() const { return val; } // Overload = to support Int foo = 5 syntax int operator=(int input) { val = input; } };
Main Code testing AList // Driver class for list implementations // Main routine for array-based list driver class int main(int argc, char** argv) { // Declare some sample lists AList<Int*> L1; // This is a list of pointers to class Int AList<Int*> L2(15); AList<Int> L3; AList<int> L4; // This is a list of integers L1.insert(new Int(2)); //see ALIST.H for implementation of insert //see BOOK.H for implementation of Int cout << "L1: "; L1.print(); //See ALIST.H for print cin.get(); L4.insert(2); cout << "L4: "; L4.print(); cin.get(); L3.insert(2); cout << "L3: "; L3.print(); cin.get(); //recall declaration from above // template <class E1, class E2, class ListImp> // void ListTest(List<E1>& L1, List<E1>& L2, List<E2>& L3) ListTest<Int*, Int, AList<int> >(L1, L2, L3); return 0; }
Linked Lists • Linked list: collection of components (nodes) • Every node (except last) has address of next node • Every node has two components (content & pointer to next) • Address of the first node of the list is stored in a separate location, called head or first
Linked List Example • Linked list above has four nodes • Address of first node is stored in head • Each node has two components: • element: stores the data • Next: stores address of the next node
Link (Node) Class Dynamic allocation of new list elements // Singly-linked list node template <class Elem> class Link { public: Elem element; // Value for this node Link *next; // Pointer to next node //First Constructor Link(const Elem& elemval, Link* nextval =NULL) { element = elemval; next = nextval; } //Overloaded Constructor Link(Link* nextval =NULL) { next = nextval; } };
Example 1: Traversing Linked List Examples: fence=head; cout<< fence->element; fence=head->next; cout<< fence->element; Note, we can use: For (myList.setStart(); myList.getvalue(it); myList.next()) doSomething(it);
Example 2: Insertion in Linked List A new node with info 50 is to be created and inserted after p: p->next=new Link(50,p->next) Equivalent to 3 steps: 1. create new_node with initial value 50, 2. make new_node->next = p-> next, 3. Change p->next = new_node
Example 3: Deletion in Linked List • Suppose the node with info 34 is to be deleted from the list: q=p->next; p->next=q->next; delete q;
Linked List class • For efficiency in appending, a pointer tail to the last node is used • Fence is the last node of the left partition: easier to insert and remove • Head is a first node which is not part of the useful nodes: Used for code efficiency to avoid special cases when inserting a node. • Default size is kept for application portability from AList to LList
Linked List Class (1) // Linked list implementation template <class Elem> class LList: public List<Elem> { private: Link<Elem>* head; // Point to list header Link<Elem>* tail; // Pointer to last Elem Link<Elem>* fence;// Last element on left int leftcnt; // Size of left int rightcnt; // Size of right void init() { // Intialization routine fence = tail = head = new Link<Elem>; leftcnt = rightcnt = 0; } // continued using C++ code
Functions to move fence void setStart() { fence = head; rightcnt += leftcnt; leftcnt = 0; } void setEnd() { fence = tail; leftcnt += rightcnt; rightcnt = 0; } void next() { if (fence != tail) // Don't move fence if right empty { fence = fence->next; rightcnt--; leftcnt++; } }
Insert and Append template <class Elem> // Insert at front of right partition bool LList<Elem>::insert(const Elem& item) { fence->next = new Link<Elem>(item, fence->next); if (tail == fence) tail = fence->next; // New tail rightcnt++; return true; } template <class Elem> // Append Elem to end of the list bool LList<Elem>::append(const Elem& item) { tail = tail->next = new Link<Elem>(item, NULL); rightcnt++; return true; }
Remove 1st element after fence // Remove and return first Elem in right partition template <class Elem> bool LList<Elem>::remove(Elem& it) { if (fence->next == NULL) return false; // Empty right it = fence->next->element; // Remember value Link<Elem>* ltemp = fence->next; // Remember link node fence->next = ltemp->next; // Remove from list if (tail == ltemp) tail = fence; // Reset tail delete ltemp; // Reclaim space rightcnt--; return true; }
Comparison of Implementations Array-Based Lists: • Size to predetermine, space requirement is Ω(n) • Insertion and deletion are (n). • Prev and direct access are (1). • Array must be allocated in advance. • No overhead if all array positions are full. Linked Lists: • Space requirement is (n) • Insertion and deletion are (1). • Prev and direct access are (n). • Space grows with number of elements. • Every element requires overhead.
Doubly Linked Lists Simplify insertion and deletion: Add aprevpointer. // Doubly-linked list link node template <class Elem> class Link { public: Elem element; // Value for this node Link *next; // Pointer to next node Link *prev; // Pointer to previous node Link(const Elem& e, Link* prevp =NULL, Link* nextp =NULL) { element=e; prev=prevp; next=nextp; } Link(Link* prevp =NULL, Link* nextp =NULL) { prev = prevp; next = nextp; } };
Doubly Linked Insert // Insert at front of right partition template <class Elem> bool LList<Elem>::insert(const Elem& item) { fence->next = new Link<Elem>(item, fence, fence->next); if (fence->next->next != NULL) fence->next->next->prev = fence->next; if (tail == fence) // Appending new Elem tail = fence->next; // so set tail rightcnt++; // Added to right return true; }
Doubly Linked Remove // Remove, return first Elem in right part template <class Elem> bool LList<Elem>::remove(Elem& it) { if (fence->next == NULL) return false; it = fence->next->element; Link<Elem>* ltemp = fence->next; if (ltemp->next != NULL) ltemp->next->prev = fence; else tail = fence; // Reset tail fence->next = ltemp->next; // Remove delete ltemp; // Reclaim space rightcnt--; // Removed from right return true; } Check code here
Sorted Array List • SAList could help in speeding up search using binary search • SAList same as LIST, but redefines insert, and hides append – How?
Insert & Append for SAList • Insert at location where element would fit in the right ascending order • Append can not be implemented so we hide it by having SAList get protected inheritance from Base Alist, ie: class SAList : protected List<Elem>
Inherit protected from Alist Note the use of a compare class: comparing two elements / /Sorted array-based list template <class Elem, class Compare> class SAList: protected AList<Elem> { public: SAList(int size=DefaultListSize) : AList<Elem>(size) {} ~SAList() {} // Destructor AList<Elem>::clear; // Expose AList clear method // Redefine insert function to keep values sorted bool insert(const Elem& item) { // Insert at right Elem curr; for (setStart(); getValue(curr); next()) if(!Compare::lt(curr, item)) break; return AList<Elem>::insert(item); } // All remaining methods are exposed from AList AList<Elem>::remove; //…. //All other member functions of Alist are listed the same way, without exposing ‘append’ //… AList<Elem>::print; };
Comparator Example class intintCompare { public: static bool lt(int x, int y) { return x < y; } static bool eq(int x, int y) { return x == y; } static bool gt(int x, int y) { return x > y; } };
Comparator Example (2) Different possible ways to compare two elements (ie classes) class PayRoll { public: int ID; char* name; }; class IDCompare { public: static bool lt(Payroll& x, Payroll& y) { return x.ID < y.ID; } …. }; class NameCompare { public: static bool lt(Payroll& x, Payroll& y) { return strcmp(x.name, y.name) < 0; } ….. };
Comparator Class How do we generalize comparison? • Use ==, <=, >=: Disastrous if not overloaded • Overload ==, <=, >=: may be needed for other reasons • Define a function with a standard name • Implied obligation • Breaks down with multiple key fields/indices for same object • Pass in a function/class • Explicit obligation • Template parameter
Dictionary Often want to insert records, delete records, search for records. “Dictionary” is a simple interface to implement such a database Required concepts: • Search key: Describe what we are looking for. Example: How would we look for student records? • Key comparison • Equality: sequential search • Relative order: sorting • Record comparison
EEComp is needed for Sorted Array list KEComp is needed when searching within dictionary Dictionary ADT // The Dictionary abstract class. template <class Key, class Elem, class KEComp, class EEComp> class Dictionary { public: virtual void clear() = 0; virtual bool insert(const Elem&) = 0; virtual bool remove(const Key&, Elem&) = 0; virtual bool removeAny(Elem&) = 0; virtual bool find(const Key&, Elem&) const = 0; virtual int size() = 0; }; Functions that need to be implemented
Possible Implementations for Dictionary • Array List (Unsorted Array List) • Sort Array List • Linked List (Unsorted Linked List) • Sorted Linked List • Other custom implementations of your choice
Unsorted Array List Dictionary template <class Key, class Elem, class KEComp, class EEComp> class UALdict : public //is-a Dictionary<Key,Elem,KEComp,EEComp> { private: AList<Elem>* list; //has-a public: bool remove(const Key& K, Elem& e) { for(list->setStart(); list->getValue(e); list->next()) if (KEComp::eq(K, e)) { //Comparator list->remove(e); return true; } return false; } … Implementing remove () Use KEComp class Assumes it has member functions: gt, lt, and eq
Using UAL dictionary int main(int argc, char** argv) { UALdict<int, Int, intIntCompare, IntIntCompare> dict; Int val; dict.insert(10); if (dict.find(10, val)) cout << "Found value " << val << " to match key value 10\n"; else cout << "Nothing found to match key value 10\n"; ….. Instantiating a dictionary Inserting an element Finding an element with key = 10
find() implementation bool find(const Key& K, Elem& e) const { for(list->setStart(); list->getValue(e); list->next()) if (KEComp::eq(K, e)) return true; return false; } Example of KEComp: class intIntCompare { public: static bool lt(int x, Int y) { return x < y.key(); } static bool eq(int x, Int y) { return x == y.key(); } static bool gt(int x, Int y) { return x > y.key(); } };