750 likes | 867 Vues
Learn operations and applications of linked lists, including template-based linked lists and object-based doubly linked lists. Understand iterators and different types of lists using templates.
E N D
Container Classes Lesson #12 Note: CIS 601 notes were originally developed by H. Zhu for NJIT DL Program. The notes were subsequently revised by M. Deek
Content • Linked Lists (Template based) • Doubly Linked Lists (Objects based) • Iterator Class • Applications of Linked Lists
Lists • A list consists of a set of sequentially organized elements. • A specific type of graph, where each node except the first has a single preceding node, and each node except the last has a single following node. • Contains 0-n nodes. • Implementation: array and linked lists.
Linked Lists • Each element in a list is called a node, and a connection between any two nodes is called a link. • Implementation: dynamic memory allocation( saves memory). • Limitation: nodes are accessed sequentially.
Operations on a list • Append: Add a node to the end • Prepend: Add a node at the beginning • Insert: Insert a node in place • Find: Find a specific node • Get: Get a node at the current position • Replace: Replace the contents of a node • IsEmpty: Find out if the list is empty • Remove: Remove a node • Clear: Remove all the nodes
Generic Linked List head tail info next
X Insert Operation head tail A B
X Insert Operation head tail A B
X Append Operation head tail
Remove a node tail head A X B
RemoveHead Operation head tail
RemoveTail Operation head tail
Lists with different types • Using templates • With the same structure, we can have integer, float, or character string as a value of a node. • We can have a list with values in different types with the same base class.
next value Tnode template <class T> class Tnode { friend class Tlist<T>; public: Tnode():next(0){ } Tnode( const T & val ); Tnode<T> * Next() const; friend ostream & operator <<(ostream & os, const Tnode<T> & N); private: T value; // data stored in node Tnode * next; // points to next node };
Tnode template <class T> Tnode<T>::Tnode( const T & val ): value(val) { next = 0;} template <class T> Tnode<T> * Tnode<T>::Next() const { return next;} template <class T> ostream & operator <<( ostream & os, const Tnode<T> & N ) { os << N.value << ','; return os; }
Tlist.h template <class T> class Tlist { public: Tlist(); ~Tlist(); int Advance(); // Return 0 if current is already at the end of the list; //otherwise, current will point to the next node and return 1. void Append( const T & nodeVal ); // Add a new node to the end // of the list. void Clear(); // Remove all nodes. T Get() const; // Get the data at the current position. void GoLast(); // Set current to the last node in the list. void GoTop(); // Set current to the header node.
Tlist.h void InsertAfter( const T & nodeVal ); // Insert new node after current one. int IsEmpty() const; // Return 1 if the list is empty; otherwise,return 0. void Prepend( const T & nodeVal ); // Insert a node at the beginning of the list. void Replace( const T & newVal ); // Replace the data in the current node. friend ostream & operator <<(ostream &, const Tlist<T> &); private: Tnode<T> * head; // dummy head node Tnode<T> * tail; // dummy tail node Tnode<T> * current; // current position };
head tail current next next value value Tlist<T> class template <class T> Tlist<T>::Tlist() { head = new Tnode<T>; tail = new Tnode<T>; head->next = tail; tail->next = head; current = head; } template <class T> Tlist<T>::~Tlist() { Clear(); delete head; delete tail; }
Tlist<T> class template <class T> int Tlist<T>::Advance() {if( !current ) throw NoCurrentNode(); if( current->next != tail ) { current = current->next; return 1; } return 0; } template <class T> void Tlist<T>::Append( const T & nodeVal ) { GoLast(); InsertAfter( nodeVal ); }
Tlist<T> class template <class T> void Tlist<T>::Clear() { current = head->next; while( current != tail ) { head->next = current->next; delete current; current = head->next; } current = head; head->next = tail; } template <class T> int Tlist<T>::IsEmpty() const { return head->next == tail; }
Tlist<T> class template <class T> T Tlist<T>::Get() const { if( !current ) throw NoCurrentNode(); return current->value; } template <class T> void Tlist<T>::GoLast() { if( !current ) throw NoCurrentNode(); while( current->next != tail ) current = current->next; }
head X tail current InsertAfter A B
Tlist<T> class template <class T> void Tlist<T>::GoTop() { current = head;} template <class T> void Tlist<T>::InsertAfter( const T & nodeVal ) { if( !current ) throw NoCurrentNode(); Tnode<T> * p = new Tnode<T>( nodeVal ); p->next = current->next; current->next = p; current = p; } template <class T> void Tlist<T>::Prepend( const T & nodeVal ) { GoTop(); InsertAfter( nodeVal ); }
Insert Before • It is more difficult to insert a new node before the current. • You must search from the head, and find the node whose next is current, then insert the new node after this node.
Tlist<T> class template <class T> void Tlist<T>::Replace( const T & newVal ) { if( !current ) throw NoCurrentNode(); current->value = newVal; } template <class T> ostream & operator <<( ostream & os, const Tlist<T> & S ) { if( S.IsEmpty()) return os; Tnode<T> * p = S.head->Next(); while( p != S.tail ) { os << *p; p = p->Next(); } os << endl; return os; }
Test Tlist class NoCurrentNode { // exception class public: friend ostream & operator <<( ostream & os, const NoCurrentNode & nc ) { os << "List exception: No current node."; return os; } };
Test Tlist #include <stdlib.h> #include <iostream.h> #include "fstring.h" #include "tlist.h" // Tlist<T> class void CreateRandomScores() { Tlist<int> scores; cout << "Creating a random list of scores:\n"; for(int i = 0; i < 10; i++) { int n = rand() % 100; scores.Append( n ); } cout << scores << endl; scores.GoLast(); cout << "Last item: " << scores.Get() << '\n'; scores.GoTop(); scores.Advance();
Test Tlist cout << "First item: " << scores.Get() << '\n'; cout << "\nReplacing first node with value 101...\n"; scores.Replace( 101 ); cout << scores; cout << "\nClearing the list..."; scores.Clear(); if( scores.IsEmpty()) cout << "the list is now empty.\n"; } void CreateNameList() {Tlist<FString> names; names.Append( "Baker" ); names.Append( "Johnson" ); names.Append( "Chong" ); names.Append( "Hamamoto" ); names.Append( "Kawai" );
Test Tlist names.Append( "Figueroa" ); cout << "current: " << names.Get() << endl; cout << names; names.GoTop(); names.Advance(); names.Replace( "Allton" ); names.GoLast(); names.InsertAfter( "Gonzalez" ); names.Prepend( "Abraham" ); cout << names << endl; cout << "Clearing the list..."; names.Clear(); if( names.IsEmpty()) cout << "the list is empty.\n\n"; }
Test Tlist int main() { try { CreateNameList(); CreateRandomScores(); } catch( const NoCurrentNode & nc ) { cout << nc; } return 0; }//cis601source/ch11/tlist/main.cpp
Results current: Figueroa Baker,Johnson,Chong,Hamamoto,Kawai,Figueroa, Abraham,Allton,Johnson,Chong,Hamamoto,Kawai,Figueroa,Gonzalez, Clearing the list...the list is empty. Creating a random list of scores: 41,67,34,0,69,24,78,58,62,64, Last item: 64 First item: 41 Replacing first node with value 101... 101,67,34,0,69,24,78,58,62,64, Clearing the list...the list is now empty.
data previous next Object-Based Lists • In this section, we will introduce a method to design a list (not based on templates). • Also, we will implement a doubly linked list (every node in the list has links to the previous one and the next one).
Doubly Linked List an administrator a faculty a student . . . . . .
The structure DblNode Administrator . . . . . Faculty Student
DblNode #include <iostream.h> class DblNode { friend class DblList; friend class DblIterator; public: DblNode(); virtual ~DblNode() { } DblNode * Next() const; // Return pointer to next node. DblNode * Prev() const; // Return pointer to previous node. DblNode * Detach(); // Detach node from its neighbors. virtual int operator ==( const DblNode & N ) const = 0; friend ostream & operator << (ostream & os, const DblNode & N ); friend istream & operator >> (istream & inp, DblNode & N );
next 0 prev 0 DblNode private: virtual void printOn( ostream & os ) const = 0; virtual void readFrom( istream & is ) = 0; DblNode * next; // pointer to next node DblNode * prev; // pointer to previous node }; inline DblNode::DblNode() { next = prev = 0;} inline DblNode * DblNode::Next() const { return next;} inline DblNode * DblNode::Prev() const { return prev;}
next next next 0 next next 0 prev prev prev prev prev B C A DblNode::Detach() C A B
DblNode DblNode * DblNode::Detach() // detach from surrounding nodes { if ( next ) // any node in front? next->prev = prev; // let it point to previous node if ( prev ) // any node in back? prev->next = next; // let it point to next node prev = 0; // detach current node next = 0; return this; } ostream & operator << ( ostream & os, const DblNode & N ) { N.printOn( os ); return os; } istream & operator >> (istream & is, DblNode & N ) { N.readFrom( is ); return is; }
DblList class DblList { friend class DblIterator; public: DblList(); // Construct an empty list. ~DblList(); // Destructor: delete all nodes. void Append( DblNode * N ); // Add node to end of the list. void DeleteAll(); // Delete all nodes in the list. DblNode *Find(const DblNode & N) const; // Find a node in the list. DblNode * Remove( DblNode * N ); // Remove a node from the list. int IsEmpty() const; // Return 1 if the list is empty. DblNode * First() const; // Return pointer to first node. DblNode * Last() const; // Return pointer to last node. long GetSize() const; // Return the number of nodes. friend ostream & operator << ( ostream & os, const DblList & L ); private: virtual void printOn( ostream & ) const; DblNode * first; // first node in the list DblNode * last; // last node in the list long size; // number of elements }; first last size
0 0 DblList::Append() first last size=0 size=1 A
0 0 0 0 DblList::Append() first last size=1 size=2 B A
DblList DblList::DblList() { first = 0; last = 0; size = 0;} DblList::~DblList() { DeleteAll(); } void DblList::Append( DblNode * P ) { if( !P ) return; if( last ) // is there a last node? { last->next = P; // yes: attach new node to it P->prev = last; } else first = P; // no: attach new node to first last = P; // last points to appended node size++; // increment size of list } first=0 last =0 size =0
DblList void DblList::DeleteAll() { while( first ) delete Remove( first ); } DblNode * DblList::Find( const DblNode & N ) const { DblNode * P = first; while( P ) { if( N == *P ) return P; // “==“ is an overloaded operator P = P->next; } return 0; } DblNode * DblList::First() const { return first;} long DblList::GetSize() const { return size;}
DblList int DblList::IsEmpty() const { return first == 0;} DblNode * DblList::Last() const { return last; } void DblList::printOn( ostream & os ) const { DblNode * N = first; while( N ) { os << (*N); N = N->next; } }
0 next prev 0 DblList::Remove(p) first last size=3 size=2 C A B 0 0 p
DblList DblNode * DblList::Remove( DblNode * P ) { if( !P ) return 0; if( P == first ) // removing the first node? first = first->next; if( P == last ) // removing the last node? last = last->prev; P->Detach(); // detach node from its neighbors size--; // decrement list size return P; } ostream & operator << (ostream & os, const DblList & aList) { aList.printOn( os ); return os; }
The pointer “current” • You can add a pointer “current” to the DblList class.
Apply DblList #include "dblnode.h" #include "dbllist.h" #include "tlist.h" class Student :public DblNode { public: Student(); Student( long idNum ); void SetId( long idNum ); int operator ==( const DblNode & P2 ) const; friend ostream & operator << (ostream & os, const Student * N ); protected: void printOn( ostream & os ) const { os << id; } void readFrom( istream & is ) { is >> id; } id prev next
Apply DblList private: long id; }; ostream & operator << (ostream & os, const Student * N ) { os<< N->id; return os; } Student::Student() { id = 0;} Student::Student( long idNum ) { id = idNum;} void Student::SetId( long idNum ) { id = idNum;} int Student::operator ==( const DblNode & P2 ) const { return id == ((Student &)P2).id;}
Apply DblList int Register( Tlist<Student *> & classRoll,const DblList & college ) { Student S; long id; cout << "Enter student id: "; cin >> id; if( id == -1 ) return 0; S.SetId( id ); Student * p = (Student *) college.Find( S ); if( p ) { cout << "Adding student to the class roll.\n"; classRoll.Append( p ); } else cout << "Student not found in college list\n"; return 1; }