Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Linked Lists, Stacks, and Queues Linked Lists A linked list is a series of connected nodes allocated in the heap. A node is a structure variable or an object with two types of member variables: A data part: one or more member variables appropriate to hold the data in the list, and A next part: a pointer variable to hold the address of the next node in the list. Examples of Nodes Declared as Structure Variables 1. Node to hold an integer value: struct IntegerNode { int value; IntegerNode *next; }; //data part // next part 2. Node to hold the information about a product represented by the following structure type: struct { ProductInfo int prodNum; double unitPrice; int quantity; // to hold a product number // to hold a product unit price // to hold a product quantity }; Can be defined by one of the following structures: struct ProductNode1 { int prodNum; double unitPrice; int quantity; // to hold a product number // to hold its unit price // to hold its quantity struct ProductNode2 { ProductInfo product; // a product’s information ProductNode2 *next; // address of next node }; ProductNode1 *next; // address of next node }; However, structure type ProductNode2 is more suitable than structure type ProductNode1 for lists processing. ©2011 Gilbert Ndjatou Page 221 Examples of Nodes Declared as Object Instances of a Class a. Node to hold an integer value: class IntegerNode { public: int value; IntegerNode *next; }; //data part // next part b. Node to hold the information about a product represented by the following structure type: struct { ProductInfo int prodNum; double unitPrice; int quantity; // to hold a product number // to hold a product unit price // to hold a product quantity }; Can be defined by one of the following classes: class ProductNode1 { public: int prodNum; double unitPrice; int quantity; // to hold a product number // to hold its unit price // to hold its quantity class ProductNode2 { public: ProductInfo product; // a product’s information ProductNode2 *next; // address of next node }; ProductNode1 *next; // address of next node }; As in the case of structures, class ProductNode2 is more suitable than class ProductNode1 for lists processing. In general, given a class AClass, we can define a class that can be used to create nodes with its data members as the data part as follows: class AClassNode { public: AClass data; AClassNode *next; }; This class may also have constructors and other member functions. ©2011 Gilbert Ndjatou Page 222 Exercise L1 1. Write the definition of a structure and the definition of a class that can be used to create a node with a double precision value in its data part. 2. The information about a course is represented by the following structure: struct CourseInfo { string courseNum; int credit; double gpv; }; Write the definition of a structure and the definition of a class that can be used to create a node with this information in its data part. 3. The information about a student is represented by the following structure: struct StudentInfo { string firstName, lastName; int idNum; double gpa; }; Write the definition of a structure and the definition of a class that can be used to create a node with this information in its data part. 4. Write the definition of a class that can be used to create nodes with the data part the data members of the class Date that you defined in exercise O7. 5. Write the definition of a class that can be used to create nodes with the data part the data members of the class Employee that you defined in exercise O9. Creating a Linked List The nodes of a linked list are connected as follows: 1. There is a pointer variable (that is referred to as the head of the list) that holds the address of the first node in the linked list. It holds NULL when the list is empty. 2. The pointer variable next of the first node holds the address of the second node; that of the second node holds the address of the third node; . . ., etc. 3. The pointer variable next of the last node holds NULL. ©2011 Gilbert Ndjatou Page 223 Examples a. Empty linked list: NULL List Head b. Linked lists with three nodes: Data members List Head Data members Data members next next NULL next Example: Address: 104 104 List Head 116 12 116 134 56 next 134 next 35 NULL next You create an empty linked list as follows: 1. Define a pointer variable to hold the address of the first node in the linked list: head of the list. 2. Initialize that pointer variable to NULL. Examples 1. Empty linked list to hold integer values: IntegerNode *integerListHead ; integerListHead = NULL; 2. Empty linked lists to hold information about products: ProductNode1 *productListfirst1 = NULL; ProductNode2 *productListfirst2 = NULL; ©2011 Gilbert Ndjatou or Page 224 Basic Operations on Linked Lists The following table lists some basic operations that can be performed on a linked list: Operation Inserting a node at the head of a linked list. Inserting a node after a given node in the linked list. Insert a node at the end (append) of a linked list. Removing the node from the head of the linked list. Removing the node after a given node in the linked list. Traversing a linked list (for example to display the data in each node) Destroying a linked list We implement the above operations by using a linked list of integer values. Algorithm to Remove a Node from the Front of a Linked List The pointer variable discardptr is used to hold the address of the node to be removed from the list. The algorithm follows: 1. If the list is empty, then stop. 2. Set the pointer variable discardptr to the address of the first node in the list: discardptr = head; 3. Set the head of the list to the node that follows the node to be removed: head = discardptr -> next; 4. Release the first node: delete discardptr; ©2011 Gilbert Ndjatou Page 225 Given the following linked list: Address: 104 104 116 12 List Head 116 134 56 134 35 next next NULL next The list after the first node is removed: Address: 116 116 List Head 134 56 134 next 35 NULL next The function is defined as follows: void removeFromFront( IntegerNode *head ) { IntegerNode *discardptr; if( head == NULL ) // cannot remove node from empty list: stop { cerror << endl << “cannot remove node from an empty linked list” << endl; exit( 1 ); } discardptr = head; head = discardptr -> next; delete discardptr; } Algorithm to Remove the Node after a Given Node from a Linked List The pointer variable discardptr is used to hold the address of the node to be removed from the list. The pointer variable afterMePtr hold the address of the node that precedes the node to be removed. The algorithm follows: ©2011 Gilbert Ndjatou Page 226 1. If the address in the pointer variable afterMePtr is NULL or this node is the last node in the list (afterMePtr -> next == NULL), then stop. 2. Set the pointer variable discardptr to the address of the node that follows the given node in the list: discardptr = afterMePtr -> next; 3. Connect the given node to the node that follows the node to be removed: afterMePtr -> next = discardptr -> next; 4. Release the node: delete discardptr; Given the following linked list: Address: 104 104 116 12 List Head 116 134 56 134 next next 35 NULL next The list after the second node is deleted: afterMePtr == 104 Address: 104 104 List Head 12 134 134 35 next NULL next The function is defined as follows: void removeAfterMe( IntegerNode * afterMePtr ) { IntegerNode *discardptr; if( afterMePtr == NULL || afterMePtr -> next == NULL ) // There is no node to be removed { cerror << endl << “there is no node to be removed” << endl; exit( 1 ); } discardptr = afterMePtr -> next; afterMePtr -> next = discardptr -> next; delete discardptr; } ©2011 Gilbert Ndjatou Page 227 Algorithm to Insert a Node at the Front of a Linked List It is assumed that the data part of the node to be inserted is in the variable dataVal. The algorithm follows: 1. Create a new node and store its address in the pointer variable newptr: IntegerNode *newptr = new IntegerNode; 2. Initialize the data part of this new node with the value in dataVal: newptr -> value = dataVal; 3. Connect this node to the previous first node: newptr -> next = head; 4. Make this node the first node: head = newptr; Given the following linked list: Address: 104 104 116 12 List Head 116 134 56 134 35 next next NULL next And the following node: newptr = 160 Address: 160 40 NULL next The list after this node is inserted at the front of the linked list: Address: 104 160 116 12 List Head 116 next 134 56 134 next 35 NULL next Address: 160 40 104 Next The function is defined as follows: ©2011 Gilbert Ndjatou Page 228 void insertAtFront(IntegerNode *head, int dataVal ) { IntegerNode *newptr = new IntegerNode; newptr -> value = dataVal; newptr -> next = head; head = newptr; } Algorithm to Insert a Node after a Given Node in a Linked List It is assumed that the address of the node that precedes the point of insertion is given and is stored in the pointer variable afterMePtr. It is also assumed that the data part of the node to be inserted is in the variable dataVal. The algorithm follows: 1. If the pointer variable afterMePtr holds NULL, then stop. 2. Create a new node and store its address in the pointer variable newptr: IntegerNode *newptr = new IntegerNode; 3. Initialize the data part of this new node with the value in dataVal: newptr -> value = dataVal; 4. Connect this node to the node that follows it in the list: newptr -> next = afterMePtr -> next; 5. Connect this node to the node that precedes it in the list: afterMePtr -> next = newptr; Given the following linked list: Address: 104 104 116 12 List Head 116 next 134 56 134 next 35 NULL next And the following node: newptr = 160 Address: 160 40 NULL next The list after the node is inserted after the second node: AfterMePtr == 116 ©2011 Gilbert Ndjatou Page 229 Address: 104 104 116 12 List Head 116 next 134 56 160 35 next NULL next Address: 160 40 134 next The function is defined as follows: void insertAfterMe(IntegerNode *afterMePtr, int dataVal ) { if( afterMePtr == NULL ) // Invalid address: stop { cerror << endl << “Invalid address” << endl; exit( 1 ); } IntegerNode *newptr = new IntegerNode; newptr -> value = dataVal; newptr -> next = afterMePtr -> next; afterMePtr -> next = newptr; } Algorithm to Output the Data Parts of the Nodes of a Linked List (List Traversal) The algorithm follows: 1. Set the current pointer variable currentptr to the address of the first node in the linked list: currentptr = head; 2. As long as the address in the current pointer variable currentptr is not NULL, do the following: a. output the data part of the current node : currentptr -> value b. Set the current pointer variable currentptr to the address of the next node: currentptr = currentptr -> next; The function is defined as follows: ©2011 Gilbert Ndjatou Page 230 void printList((ostream &outs, IntegerNode *head ) { IntegerNode * currentptr; currentptr = head; while( currentptr != NULL ) { outs << endl << currentptr -> value << endl; currentptr = currentptr -> next; } Algorithm to Destroy a Linked List The algorithm follows: 1. Set the current pointer variable currentptr to the address in the first node in the linked list: currentptr = head; 2. As long as the address in pointer variable currentptr is not NULL, do the following: a. Save the address of the current node (so that it can be released later): releaseptr = currentptr; b. Set the current pointer variable currentptr to the address of the next node: currentptr = currentptr -> next; c. Release the node: delete releaseptr; The function is defined as follows: void destroyList((IntegerNode *head ) { IntegerNode * currentptr, // to hold the address of the current node *releaseptr; // to hold the address of the node to be removed currentptr = head; while( currentptr != NULL ) { releaseptr = currentptr; currentptr = currentptr -> next; delete releaseptr; } ©2011 Gilbert Ndjatou Page 231 Algorithm to Append a Node to a Linked List It is also assumed that the data part of the node to be inserted is in the variable dataVal. The algorithm follows: 1. Create a new node and store its address in the pointer variable newptr: node *newptr = new node; 2. Initialize the data part of this new node with the value in dataVal: newptr -> data = dataVal; 3. Initialize the next part of this node with NULL: newptr -> next = NULL; 4. If the list is empty (head == NULL), do the following: a. make this node the first node in the list: head = newptr; 5. Otherwise, do the following: a. Set the current pointer variable currentptr to the address of the first node in the list: currentptr = head; b. As long as the next part of the current node is not NULL (currentptr -> next != NULL), do the following: i. Set the current pointer variable currentptr to the address of the next node: currentptr = currentptr -> next; c. Connect the last node of the list to the new node: currentptr -> next = newptr; The function is defined as follows: void appendNode((IntegerNode *head, int dataVal ) { /*-----------------------create the new node to be inserted -----------------------------------*/ IntegerNode *newptr = new IntegerNode; newptr -> value = dataVal; newptr -> next = NULL; if( head == NULL ) // Make this node the head of the list if the list is empty head = newptr; else { IntegerNode * currentptr; currentptr = head; while( currentptr -> next != NULL ) // find the last node in the list currentptr = currentptr -> next; currentptr -> next = newptr; // insert the new node } ©2011 Gilbert Ndjatou Page 232 Algorithm for Searching a Linked List The value to be searched is in the variable target. The address of the node that contains the value that is being searched is returned if that value is in the list; otherwise, NULL is returned. The algorithm follows: 1. Set the current node pointer variable currentPtr to the first node or the address in the pointer variable head: currentPtr = head; 2. As long as the data in the current node is not the searched value and the address in currentPtr is not NULL, do the following: ( currentPtr != NULL && currentPtr -> value != target ) a. Make the next node the list the current node: currentPtr = currentPtr -> next; 3. Return the address in the pointer variable currentPtr. The function is defined as follows: IntergerNode * searchValue((IntegerNode *head, int target ) { IntegerNode *currentPtr = head; while( currentPtr != NULL && currentPtr -> value != target ) currentPtr = currentPtr -> next; return( currentPtr ); } ©2011 Gilbert Ndjatou Page 233 Exercise L2 For the following exercises, a node of the linked list is defined as follows: struct Node { int data; Node * next; }; A. Pointer variable pt is defined as follows: Node *pt; Assuming that pt holds the address of a node, write the sequence of statement(s) to store into pointer variable pt the address of the node that follows this node in the linked list. B. Given the following linked list (with the address of the first node in the pointer variable head) and the additional node, do the following: Address: 128 128 140 78 List Head 140 next 154 23 154 next 15 NULL next Aditional node: newptr = 172 Address: 172 10 NULL next 1. a. Show the list after the new node is inserted at the front of the list. b. Write the sequence of instructions to insert a new node at the front of the list. 2. a. Show the list after the new node is inserted after the node with address 140. b. Write the sequence of instructions to insert the node with address in pointer variable newptr after the node with address in pointer variable predPtr. 3. a. Show the list after the new node is inserted at the end of (appended) the list. b. Write the sequence of instruction to append the node with address in pointer variable newptr at the end of a non-empty list. 4. a. Show the list after the first node is deleted. b. Write the sequence of instructions to delete the first node from a non-empty list. ©2011 Gilbert Ndjatou Page 234 5. a. Show the list after the node that comes after the node with address 140 is removed from the list. b. Write the sequence of instructions to delete from a list the node that comes after the node with address in pointer variable predPtr. Exercise L3 An ordered linked list is a linked list such that the data values of the nodes are ordered either in ascending order or in descending order. 1. Write the definition of the function Node *searchPosition( Node *first, int value) that receives as arguments the head of an ordered linked list (in ascending order) and a value and does the following: a. If the list is empty, it returns NULL. b. If the list is not empty and the value received is less than the data value of the first node in the list, it returns NULL. c. Otherwise, it searches the linked list for the node that must precede the node with the value received in the list and then returns its address. 2. Write the definition of the function void insertNode( Node *first, int value) that receives as arguments the head of an ordered linked list (in ascending order) and a value and does the following: a. It first creates a new node and places the value received in the data part of the new node. b. It then calls function searchPosition( ) to search for the position of this new node in the list. c. And finally, it inserts the new node in its position in the linked list. Note that the new node is inserted at the front of the list if NULL is returned by function searchPosition( ). 3. Write the definition of the function int countNode( Node *first) that receives as argument the head of a linked list and returns the number of nodes in that linked list. Exercise L4 Write the definition of the function void reverseList( Node * & first ) that receives the head of a nonempty linked list and reverses its elements: That means the first node becomes the last node in the list and the last node becomes the first. The function uses the reference parameter to return the address of the head of the new list. ©2011 Gilbert Ndjatou Page 235 Stacks A stack is a list of data designed such that data are retrieved in the reverse of the order in which they are stored in the list. You can think of a stack as a hole in the ground: in order to remove an item from the hole, you must first remove all the items on top of it. For this reason a stack is often called a last in/first out (LIFO) data structure. In the following example, data are entered in the stack in the order: A B C D D C B A And they are retrieved in the order: D C B A A stack can be implemented by using an array, a dynamic array, or a linked list. In the implementation of a stack, a variable (referred to as the top of the stack) is used to hold the position of the top of the stack: the location of the last data entered in the stack. The following are the basic operations on a stack: Operation Description push top pop empty Adds an element on top of the stack (if there is room in the stack); otherwise, indicates that there is no room in the stack. Returns the data at the top of the stack. Returns the data at the top of the stack and then removes it from the top of the stack. Checks if a stack is empty: returns true if it is and false otherwise. Implementing a Stack with an Array An array of size CAPACITY is used to hold the elements of a stack. Elements are entered in the stack at index 0, then 1, then 2, . . ., CAPACITY – 1. We indicate that a stack is empty by setting its top at position -1 (nothing is entered in the array). ©2011 Gilbert Ndjatou Page 236 [5] [5] [4] [4] [3] [3] [2] [2] 90 [1] [1] 67 [0] [0] 25 Empty stack: top = -1 top = 2 The class is defined as follows: ©2011 Gilbert Ndjatou Page 237 /*-------------------------------------------- Stack.h ---------------------------------------------------*/ #ifndef STACK_H #define STACK_H const int CAPACITY = 100; typedef int dataType; // maximum number of elements in the stack // to be set before you compile the program class Stack { public: Stack( ); // constructor: create an empty stack void push( dataType dataVal ); // add an element on top of the stack void pop(dataType & dataVal ); // return the data at the top of the stack and then remove it void top(dataType & dataVal ); // return the data at the top of the stack bool isEmpty( ); // return true if the stack is empty and false otherwise bool isFull( ); // return true if the stack is full and false otherwise private: dataType stackArray[ CAPACITY ]; int stackTop; // The top of the stack }; /*----------------------------------end of Stack.h -----------------------------------------------------------*/ /*----------------------------------------- Stack.cpp --------------------------------------------------------*/ #include <iostream> #include “Stack.h” /*------------------------------------ definition of the constructor ----------------------------------------*/ Stack :: Stack( ) { stackTop = -1; // empty stack } /*------------------------------------- member function top( ) ----------------------------------------------*/ /*-----------------use a reference variable to return the data at the top of the stack ------------------*/ void Stack :: top( dataType &dataVal ) { if( isEmpty( ) ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackArray[ stackTop ]; } ©2011 Gilbert Ndjatou Page 238 /*---------------------------------- member function pop( ) -------------------------------------------------*/ /*---- Use a reference variable to return the data at the top of the stack and then remove it -------*/ void Stack :: pop( dataType &dataVal ) { if( isEmpty( ) ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackArray[ stackTop ]; stackTop -- ; } /*---------------------------------- member function push( ) ----------------------------------------------*/ void Stack :: push(dataType dataVal ) { if( isFull( ) ) // the stack is full { cerr << endl << “Stack is full: new element cannot be added” << endl; exit( 1 ); } /*-----------------------------add the element to the top of the stack ----------------------------------*/ stackTop ++; stackArray[ stackTop ] = dataVal; } /*--------------------------------------- member function isEmpty( )--------------------------------------*/ bool Stack :: isEmpty( ) { return( stackTop == -1 ); } /*------------------------------------- member function isFull( ) ---------------------------------------*/ bool Stack :: isFull( ) { return( stackTop == CAPACITY - 1 ); } ©2011 Gilbert Ndjatou Page 239 Implementing a Stack with a Dynamic Array One problem with implementing a stack with an array is that the size of the stack is fixed: one size fits all. All the stack objects have the same size. With a dynamic array, the size of the array is set by the constructor. Elements are entered in the stack in the same way as with static arrays: at index 0, then 1, then 2, . . ., capacity – 1. We indicate that a stack is empty in the same way that we did with static array: by setting its top at position -1 (nothing is entered in the array). The definitions of the member functions push( ), pop( ), top( ), isEmpty( ), and isFull( ) are the same as their definitions for the implementation of a stack with a static array. However, we must provide the definitions of the copy constructor, the destructor, and the overloaded assignment operator for the class. The class is defined as follows: /*-------------------------------------------- DStack.h ---------------------------------------------------*/ #ifndef DSTACK_H #define DTACK_H typedef int dataType; // to be set before you compile the program class Stack { public: Stack( int size = 10); // create an empty stack: 10 is the default array size Stack( const Stack & original ); // copy constructor ~Stack( ); // destructor const Stack & operator =( const Stack & rightHandSide); // overloaded assignment void push( dataType dataVal ); // add an element on top of the stack void pop(dataType & dataVal ); // return the data at the top of the stack and then remove it void top(dataType & dataVal ); // return the data at the top of the stack bool isEmpty( ); // return true if the stack is empty and false otherwise bool isFull( ); // return true if the stack is full and false otherwise private: dataType *stackArray; // dynamic array to hold the stack int capacity; // the size of the array int stackTop; // the top of the stack }; /*----------------------------------end of DStack.h -----------------------------------------------------------*/ ©2011 Gilbert Ndjatou Page 240 /*----------------------------------------- DStack.cpp --------------------------------------------------------*/ #include <iostream> #include “DStack.h” /*------------------------------------------------------- constructor--------------------------------------------*/ Stack :: Stack( int size ) : capacity ( size ) { stackArray = new dataType[ size ]; stackTop = -1; } /*-------------------------copy constructor Stack( const Stack & original ) -------------------------------*/ /* Initialize the elements of the new stack object with the elements of stack original */ Stack :: Stack( const Stack & original ) : capacity( original.capacity ) { stackArray = new dataTye [ capacity ]; for ( int i = 0; i < capacity ; i ++ ) stackArray [ i ] = original.stackArray[ i ]; stackTop = original.stackTop; } /*-----------------------overloaded assignment operator------------------------------------------------------*/ /* use of const return to avoid left associativity: (A = B) = C */ /* do not copy a list into itself and if the two list do not have the same number of elements, deallocate the original list and create another one with the same number of elements as the list being copied */ const Stack & Stack :: operator = ( const Stack & rightHandSide ) { if ( this != & rightHandSide ) // copy only if the two lists are not the same { if ( capacity != rightHandSide.capacity ) { delete [ ] stackArray; // deallocate memory locations for the left side list capacity = rightHandSide.capacity; stackArray = new dataType [ capacity ]; } for ( int i = 0; i < capacity ; i ++ ) stackArray [ i ] = original.stackArray [ i ]; stackTop = rightHandSide.stackTop; } return( *this); } ©2011 Gilbert Ndjatou Page 241 /*------------------------------------------------destructor ~Stack( )---------------------------------------*/ Stack :: ~Stack( ) { delete [ ] stackArray; } /*---------------------------------- member function push( ) --------------------------------------------*/ void Stack :: push( dataType dataVal ) { if( isFull( ) ) // the stack is full { cerr << endl << “Stack is full: new element cannot be added” << endl; exit( 1 ); } /*-----------------------------add the element at the top of the stack -------------------------------*/ stackTop ++; stackArray[ stackTop ] = dataVal; } /*----------------------------------member function top( )------------------------------------------------*/ /*-----------------use a reference variable to return the data at the top of the stack ----------------*/ void Stack :: top( dataType &dataVal ) { if( isEmpty( ) ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackArray[ stackTop ]; } /*----------------------------- member function isEmpty( ) ----------------------------------------------*/ bool Stack :: isEmpty( ) { return( stackTop == -1 ); } ©2011 Gilbert Ndjatou Page 242 /*----------------------------------member function isFull( ) ----------- ------------------------------------*/ bool Stack :: isFull( ) { return( stackTop == capacity - 1 ); } /*---------------------------------- member function pop( ) -----------------------------------------------*/ /*---- Use a reference variable to return the data at the top of the stack and then remove it -----*/ void Stack :: pop( dataType &dataVal ) { if( isEmpty( ) ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackArray[ stackTop ]; stackTop -- ; } /*---------------------------------------------end of DStack.cpp-----------------------------------------------*/ Exercise L5 1. Suppose that the following operations are performed on an empty stack s: s.push( 5); s.push(2); s.push(10); s.push(3); Show the array and the value of variable stackTop after the execution of these operations. [4] [3] [2] [1] [0] stackTop = -1 2. ©2011 Gilbert Ndjatou Page 243 Suppose that the following operations are performed on an empty stack s: s.push( 5); s.push(2); s.pop(num); s.push(10); s.top(num); s.push(3); Show the array and the values of the variables num1, num2, and stackTop after the execution of these operations. [4] [3] [2] [1] [0] stackTop = -1 3. Write the sequence of statements to create an empty stack of 10 elements using a dynamic array. 4. Write the sequence of statements to perform the push operation on a stack implemented as an array that is not full. What is the condition for a stack implemented as an array to be full? 5. Write the sequence of statements to perform the pop and the top operations on a stack implemented as an array that is not empty. What is the condition for a stack implemented as an array to be empty? 6. Write the sequence of instructions to read 10 integer values and to use a stack to print them in the reverse of the order that they have been read in. Implementing a Stack with a Linked List An array-based implementation of a stack imposes an upper limit on the size of the stack. That means that a stack can become full and the user of this stack must be aware of this fact. A linked list implementation of a stack solves this problem by allowing a stack to grow without limit and to shrink without wasting unused storage. With the linked list implementation, the top of the stack is implemented by the head of the list, and a stack is empty when the head of the list holds NULL. /*-------------------------------------------- LStack.h ---------------------------------------------------*/ #ifndef LSTACK_H #define LSTACK_H typedef int dataType; // to be set before you compile the program /*------------------------------ Definition of the linked list node ------------------------------------*/ struct StackNode { dataType data; StackNode * next; }; ©2011 Gilbert Ndjatou Page 244 class Stack { public: Stack( ); // create an empty stack Stack( const Stack & original ); // copy constructor ~Stack( ); // destructor const Stack & operator =( const Stack & rightHandSide); // overloaded assignment void push( dataType dataVal ); // add an element on top of the stack void pop(dataType & dataVal ); // return the data at the top of the stack and then remove it void top(dataType & dataVal ); // return the data at the top of the stack bool isEmpty( ); // return true if the stack is empty and false otherwise private: stackNode *stackTop; // head of the linked list refers to the top of the stack }; /*----------------------------------end of LStack.h -----------------------------------------------------------*/ /*----------------------------------------- LStack.cpp --------------------------------------------------------*/ #include <iostream> #include “LStack.h” /*------------------------------------------------------- constructor-------------------------------------------*/ Stack :: Stack( ) : stackTop( NULL ) { } /*--------------------------------destructor ~Stack( )--------------------------------------------------------*/ Stack :: ~Stack( ) { StackNode *releasePtr; // to hold the address of the node to be removed while( StackTop != NULL ) { releasePtr = StackTop; StackTop = StackTop -> next; delete releasePtr; } } ©2011 Gilbert Ndjatou Page 245 /*-------------------------copy constructor Stack( const Stack & original ) ------------------------------*/ /* Create a new stack object and initialize its elements with the elements of the original stack */ Stack :: Stack( const Stack & original ) { if( original.stackTop == NULL ) // original stack is empty stackTop = NULL; else { StackNode *from, // hold the address of the current node in the original stack *to; // hold the address of the new node of the new stack /* - create the first node of the new stack and copy the first node of the original stack to it -*/ to = new StackNode; to -> data = original. stackTop -> data; stackTop = to; // copy the first node /*----------------------------------- copy the rest of the nodes -------------------------------------------*/ from = original.stackTop -> next; while( from != NULL ) { to -> next = new StackNode; // create a new node and connect it to the current node to = to -> next; // make the new node the current node to -> data = from -> data // copy the data to the current node from = from -> next; // make the next node in the original stack the current node } to -> next = NULL; } } /*-----------------------overloaded assignment operator---------------------------------------------------------*/ /* use of const return to avoid left associativity: (A = B) = C */ /* do not copy a stack into itself. First destroy the left hand side stack and create another one with the same number of nodes as the right hand side stack */ const Stack & Stack :: operator = ( const Stack & rightHandSide ) { if ( this != & rightHandSide ) // copy only if the two stacks are not the same { this -> ~Stack( ); // destroy the left hand side stack if ( rightHandSide.empty( ) ) // the right hand side stack is empty stackTop = NULL; else ©2011 Gilbert Ndjatou Page 246 { StackNode *from, *to; // hold the address of the current node in the right hand side stack // hold the address of the new node of the left hand side stack /* create the first node of the left hand side stack and copy the first node of the right hand side stack to it */ to = new StackNode; to -> data = rightHandSide. stackTop -> data; stackTop = to; // copy the first node /*----------------------------------- copy the rest of the nodes -------------------------------------*/ from = rightHandSide.stackTop -> next; while( from != NULL ) { to -> next = new StackNode; // create a new node and connect it to the current node to = to -> next; // make the new node the current node to -> data = from -> data // copy the data to the current node from = from -> next;// make the next node in the right hand side stack the current node } to -> next = NULL; } } return( *this ); } /*---------------------------------- member function push( ) -----------------------------------------------*/ void Stack :: push( dataType dataVal ) { /*-----------------------------add the element to the top of the stack ----------------------------------*/ StackNode *newptr = new StackNode; newptr -> data = dataVal; newptr -> next = stackTop; stackTop = newptr; } /*------------------------------- member function isEmpty( bool Stack :: isEmpty( ) { return( stackTop == NULL ); } ©2011 Gilbert Ndjatou ) ----------------------------------------*/ Page 247 /*---------------------------------- member function top( ) --------------------------------------------------*/ /*--------------------------------return the data at the top of the stack -------------------------------------*/ void Stack :: top(dataType & dataVal ) { if( stackTop == NULL ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackTop -> data; } /*---------------------------------- member function pop -----------------------------------------------------*/ /*---------------return the data at the top of the stack and clear the top of the stack -----------------*/ void Stack :: pop(dataType & dataVal ); { if( stackTop == NULL ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackTop -> data; // return the data at the top of the stack StackNode *discardptr; // to hold the address of the node to be removed discardptr = stackTop; stackTop = stackTop -> next; // set the top of the stack to the next node delete discardptr; } /*---------------------------------------------end of LStack.cpp-------------------------------------------*/ Exercise L6 For the following exercises, assume that the structure used to define a node of a linked list is named StackNode (with the data members data and next) and the head of the list (top of the stack) has its address in pointer variable stackTop. 1. Write the sequence of statements to create an empty stack using a linked list. 2. Write the sequence of statements to perform the push operation on a stack (implemented as a linked list) using the value in the variable dataVAl. 3. Write the sequence of statements to perform the pop and the top operations on a stack implemented as a linked list that is not empty. The value retrieved front the stack will be stored in the variable dataVal. 4. What is the condition for a stack implemented as a linked list to be empty? ©2011 Gilbert Ndjatou Page 248 Queues A queue is a list of data designed such that data are retrieved in the order in which they are stored in the list. You can think of a queue as a line in front of a cash register: the first person that arrives at the cash register is the first to be served. For this reason a queue is often called a first in/first out (FIFO) data structure. In the following example, data are entered in the queue in the order: A B C D D C B A And they are retrieved in the order: A B C D A queue can be implemented with an array, a dynamic array, or a linked list. In the implementation of a queue, a variable (referred to as the front/head of the queue) is used to hold the position of the first element of the queue and a variable (referred to as the back/tail/rear of the queue) is used to hold the position where the new element will be added in the queue. The following are the basic operations on a queue: Operation enqueue front dequeue empty Description Adds an element to the back of the queue: Indicate if there is no room in the queue for the new element. Returns the data at the front of the queue. Returns the data at the front of the queue and then removes it from the front of the queue. Checks if a queue is empty: returns true if it is and false otherwise. Implementing a Queue with a Circular Array An array of size CAPACITY is used to hold the elements of a queue. Elements are entered and retrieved from the queue in the order, index 0, then 1, then 2, . . ., then CAPACITY – 1, then 0, then 1 . . . etc. ©2011 Gilbert Ndjatou Page 249 A variable numElement is used to hold the number of elements entered in the queue: 0 <= numElement <= CAPACITY A queue is empty if numElement = 0. A queue is full if numElement = CAPACITY. [4] [4] [3] [3] 25 [2] [2] 90 [1] [1] 67 [0] [0] Empty queue: front = 0 back = 0 numElement = 0 front = 1 back = 4 numElement = 3 The class is defined as follows: /*-------------------------------------------- Queue.h ---------------------------------------------------*/ #ifndef QUEUE_H #define QUEUE_H const int CAPACITY = 100; typedef int dataType; // maximum number of elements in the queue // to be set before you compile the program class Queue { public: Queue( ); // constructor: create an empty queue void enqueue( dataType dataVal ); // add an element to the back of the queue void dequeue(dataType & dataVal ); // return the data at the front of the queue and remove it void front(dataType & dataVal ); // return the data at the front of the queue bool isEmpty( ); // return true if the queue is empty and false otherwise bool isFull( ); // return true if the queue is full and false otherwise private: dataType queueArray[ CAPACITY ]; int queueFront; // the front of the queue int queueBack; // the back of the queue int numElement; // the number of elements in the queue }; /*----------------------------------end of Queue.h -----------------------------------------------------------*/ ©2011 Gilbert Ndjatou Page 250 /*----------------------------------------- Queue.cpp --------------------------------------------------------*/ #include <iostream> #include “Queue.h” /*------------------------------------ definition of the constructor ----------------------------------------*/ Queue:: Queue( ) // empty queue { QueueFront = 0; QueueBack = 0; NumElement = 0; } /*----------------------------------definition of member function enqueue( )------------------------*/ void Queue :: enqueue( dataType dataVal ) { if( isFull( ) ) // the queue is full { cerr << endl << “Queue is full: new element cannot be added” << endl; exit( 1 ); } /*-----------------------------add the element to the back of the queue---------------------------*/ queueArray[ queueBack ] = dataVal; queueBack ++; if( queueBack == CAPACITY ) queueBack = 0; // queueBack = ( queueBack +1) % CAPACITY numElement++; } /*----------------------------------definition of member function front( ) -----------------------------*/ /*-----------------use a reference variable to return the data at the front of the queue------------*/ void Queue :: front( dataType &dataVal ) { if( isEmpty( ) ) // the queueis empty { cerr << endl << “Queue is empty” << endl; exit( 1 ); } dataVal = queueArray[ queueFront ]; } ©2011 Gilbert Ndjatou Page 251 /*-------------------------definition of member function dequeue-- ------------------------------------*/ /*---- Use a reference variable to return the data at the front of the queue and then remove it -*/ void Queue :: dequeue( dataType &dataVal ) { if( isEmpty( ) ) // the queue is empty { cerr << endl << “Queue is empty” << endl; exit( 1 ); } dataVal = stackArray[ queueFront ]; queueFront ++ ; if( queueFront == CAPACITY ) queueFront = 0; // queueFront = (queueFront + 1) % CAPACITY numElement--; } /*---------------------------------------------- member function isEmpty-------------------------------------*/ bool Queue:: isEmpty( ) { return( numElement == 0 ); } /*---------------------------------------------- member function isFull ---------------------------------------*/ bool Queue :: isFull( ) { return( numElement == CAPACITY ); } Implementing a Queue with a Circular Dynamic Array One problem with implementing a queue with an array is that the size of the queue is fixed: one size fits all. All the queue objects have the same size. With a dynamic array, the size of the array is set by the constructor. Elements are entered and retrieved from the queue in the same way as with static arrays: at index 0, then 1, then 2, . . ., capacity – 1, then 0, then 1 . . . As with static arrays, a variable named numElement is used to hold the number of elements entered in the queue: 0 <= numElement <= capacity ©2011 Gilbert Ndjatou Page 252 A queue is empty if numElement = 0. A queue is full if numElement = capacity. The definitions of the member functions enqueue( ), front( ), dequeue( ), isEmpty( ), and isFull( ) are the same as their definitions for the implementation of a queue with a static array. However, we must provide the definitions of the copy constructor, the destructor, and the overloaded assignment operator for the class. The definitions of the copy constructor, the destructor, and the overloaded assignment operator are similar to those provided for the implementation of a stack with dynamic arrays. The class is defined as follows: /*-------------------------------------------- DQueue.h ---------------------------------------------------*/ #ifndef DQUEUE_H #define DQUEUE_H typedef int dataType; // to be set before you compile the program class Queue { public: Queue( int size = 10); // create an empty queue: 10 is the default array size Queue( const Queue & original ); // copy constructor ~Queue( ); // destructor const Queue & operator =( const Queue & rightHandSide); // overloaded assignment void enqueue( dataType dataVal ); // add an element to the back of the queue void dequeue(dataType & dataVal ); // return the data at the front of the queue and remove it void front(dataType & dataVal ); // return the data at the front of the queue bool isEmpty( ); // return true if the queue is empty and false otherwise bool isFull( ); // return true if the queue is full and false otherwise private: dataType *queueArray; // dynamic array to hold the queue int capacity; // the size of the array int queueFront; // the position of the front of the queue int queueBack; // the position of the back of the queue int numElement; // the number of element in the queue }; /*-----------------------------------------end of DQueue.h --------------------------------------------------*/ ©2011 Gilbert Ndjatou Page 253 /*----------------------------------------- DQueue.cpp ------------------------------------------------------*/ #include <iostream> #include “DQueue.h” /*------------------------------------ definition of the constructor ----------------------------------------*/ Queue:: Queue( int size ) : capacity( size ) { queueArray = new dataType[ size ]; QueueFront = 0; QueueBack = 0; NumElement = 0; } <Definitions of the other member functions> /*-------------------------------- end of DQueue.cpp ---------------------------------------------------------*/ Exercise L7 Write the definitions of the copy constructor, the destructor, and the overloaded assignment operator of the class Queue implemented with a dynamic circular array. Exercise L8 1. Suppose that the following operations are performed on an empty queue q: q.enqueue( 5); q.enqueue(2); q.enqueue( 9); q.dequeue( num1); q.enqueue(3); q.front(num2); q.enqueue(10); q.dequeue(num3); q.enqueue( 7); q.enqueue(4); q.dequeue( num4 ); [4] [3] [2] [1] [0] QueueFront = 0 QueueBack = 0 NumElement = 0 Show he circular array, and the values of the variables QueueFront, QueueBack, NumElement, num1, num2, num3, and num4 after the execution of these operations. ©2011 Gilbert Ndjatou Page 254 2. Suppose that the following operations are performed on the queue q shown on the right: q.enqueue( 5); q.enqueue(2); q.enqueue( 9); q.dequeue( num1); q.dequeue(num2); q.front(num3); q.dequeue( num4); q.dequeue(num5); q.enqueue( 7); q.dequeue( num6); Show he circular array, and the values of the variables QueueFront, QueueBack, NumElement, num1, num2, num3, num4, num5, and num6 after the execution of these operations [4] [3] 15 [2] 20 [1] [0] QueueFront = 2 QueueBack = 4 NumElement = 2 3. Write the sequence of statements to enter (enqueue) the value in variable dataVal into a queue (implemented with a circular array) that is not full. 4. Write the sequence of statements to remove (dequeue) one element from a queue (implemented with a circular array) that is not empty and to store its value into the variable dataVal. 5. Write the definition of a member function dataType getlast( element entered in the queue. ) that returns the value of the last 6. Write the definition of a member function void printQueue1( ) that outputs the values of the elements of a queue (implemented with a circular array) in the order in which they are entered in the queue (with a possible destruction of the queue). 7. Write the definition of a member function void printQueue2( ) that outputs the values of the elements of a queue (implemented with a circular array) in the order in which they are entered in the queue (without destroying the queue). Exercise L9 The service time of an individual that arrives at a cash register is the time it will take the cashier to service that individual and the turn-around time of that individual is the sum of his service time and his waiting time (that means the time that he must wait to get the service). Ten individuals have arrived at the same time at the cash register and their service times are input in their order of arrival. 1. Write a code segment to define a queue that can hold at least 10 integer values, and to read these service times into that queue. 2. Assuming that there is no interruption of service between an individual and the next one in the queue, write a code segment to compute each individual’s turn-around time, and the average turnaround time. ©2011 Gilbert Ndjatou Page 255 Implementing a Queue with a Linked List An array-based implementation of a queue imposes an upper limit on the size of the queue. That means that a queue can become full and the user of this queue must be aware of this fact. A linked list implementation of a queue solves this problem by allowing a queue to grow without limit and to shrink without wasting unused storage. With the linked list implementation, the head/front of the queue is implemented by the head of the list, and a new pointer variable is defined to hold the address of the back/tail of a queue. A queue is empty when the head of the list holds NULL. /*-------------------------------------------- LQueue.h ---------------------------------------------------*/ #ifndef LQUEUE_H #define LQUEUE_H typedef int dataType; // to be set before you compile the program /*------------------------------ Definition of the linked list node ------------------------------------*/ struct QueueNode { dataType data; QueueNode * next; }; class Queue { public: Queue( ); // create an empty stack Queue ( const Queue & original ); // copy constructor ~ Queue ( ); // destructor const Queue & operator =( const Queue & rightHandSide); // overloaded assignment void enqueue( dataType dataVal ); // add an element at the back of the queue void dequeue(dataType & dataVal ); // return the data at the front of the queue and remove it void front(dataType & dataVal ); // return the data at the front of the queue bool isEmpty( ); // return true if the queue is empty and false otherwise private: queueNode *queueFront; queueNode *queueBack; // head of the linked list refers to the front of the queue // the last node entered in the queue }; /*----------------------------------end of LQueue.h -----------------------------------------------------------*/ ©2011 Gilbert Ndjatou Page 256 /*----------------------------------------- LQueue.cpp --------------------------------------------------------*/ #include <iostream> #include “L Queue.h” /*------------------------------------------------------- constructor-------------------------------------------*/ Queue :: Queue ( ) : queueFront( NULL ), queueBack( NULL ) { } /*--------------------------------destructor ~ Queue ( )----------------------------------------------------*/ Queue :: ~ Queue ( ) { QueueNode *releasePtr; // to hold the address of the node to be removed while( queueFront != NULL ) { releasePtr = queueFront; queueFront = queueFont -> next; delete releasePtr; } } /*-------------------------copy constructor Queue( const Queue & original ) ------------------------------*/ /* Create a new queue object and initialize its elements with the elements of the original Queue */ Queue :: Queue ( const Queue & original ) { if( original.queueFront == NULL ) // original queue is empty queueFront = queueBack = NULL; else { QueueNode *from, // hold the address of the current node in the original queue /* - create the first node of the new queue and copy the first node of the original queue to it -*/ queueFront = queueBack = new QueueNode; queueFront -> data = original. queueFront -> data; /*----------------------------------- copy the rest of the nodes -------------------------------------------*/ from = original.queueFront -> next; while( from != NULL ) { queueBack -> next = new QueueNode; // create a new node and connect it to the current last node ©2011 Gilbert Ndjatou Page 257 queueBack = queueBack -> next; // make the new node the current last node queueBack -> data = from -> data // copy the data to the current last node from = from -> next; // make the next node in the original queue the current node } queueBack -> next = NULL; } } /*-----------------------overloaded assignment operator---------------------------------------------------------*/ /* use of const return to avoid left associativity: (A = B) = C */ /* do not copy a queue into itself. First destroy the left hand side queue and create another one with the same number of nodes as the right hand side queue */ const Queue & Queue :: operator = ( const Queue & rightHandSide ) { if ( this != & rightHandSide ) // copy only if the two queues are not the same { this -> ~ Queue ( ); // destroy the left hand side queue if ( rightHandSide.empty( ) ) // the right hand side queue is empty queueFront = queueBack = NULL; else { QueueNode *from, // hold the address of the current node in the original queue /* create the first node of the new queue and copy the first node of the original queue to it*/ queueFront = queueBack = new QueueNode; queueFront -> data = rightHandSide. queueFront -> data; /*-------------------------------- copy the rest of the nodes --------------------------------------*/ from = rightHandSide.queueFront -> next; while( from != NULL ) { queueBack -> next = new QueueNode; // create a new node and connect it to the current last node queueBack = queueBack -> next; // make the new node the current last node queueBack -> data = from -> data // copy the data to the current last node from = from -> next; // make the next node in the original queue the current node } queueBack -> next = NULL; } } return( *this ); } ©2011 Gilbert Ndjatou Page 258 /*---------------------------------- member function enqueue( ) -----------------------------------------------*/ void Queue :: enqueue( dataType dataVal ) { /*--------------------------------add the element to the back of the queue ----------------------------------*/ QueueNode *newptr = new QueueNode; newptr -> data = dataVal; newptr -> next = NULL; if( queueFront != NULL) // the queue is not empty { queueBack -> next = newptr; // connect the last node to the new node queueBack = newptr; // make the new node the last node } else queueFront = queueBack = newptr; } /*---------------------------------- member function front( ) --------------------------------------------------*/ /*--------------------------------return the data at the front of the queue -------------------------------------*/ void Queue :: front(dataType & dataVal ) { if( queueFront == NULL ) // the queue is empty { cerr << endl << “Queue is empty” << endl; exit( 1 ); } dataVal = queueFront -> data; } /*---------------------------------- member function dequeue( )-----------------------------------------------*/ /*---------------return the data at the front of the queue and clear the front of the queue -----------------*/ void Queue :: dequeue(dataType & dataVal ); { if( queueFront == NULL ) // the queue is empty { cerr << endl << “Queue is empty” << endl; exit( 1 ); } dataVal = queueFront -> data; // return the data at the front of the queue QueueNode *discardptr; // to hold the address of the node to be removed discardptr = queueFront; queueFront = queueFront -> next; // set the front of the queue to the next node delete discardptr; ©2011 Gilbert Ndjatou Page 259 if(queueFront == NULL ) queueBack = NULL; } /*------------------------------- member function isEmpty( bool Queue :: isEmpty( ) { return( queueFront == NULL ); } ) ----------------------------------------*/ /*---------------------------------------------end of LQueue.cpp-------------------------------------------*/ Exercise L10 For the following exercises, assume that the structure used to define a node of a linked list is named QueueNode (with the data members data and next) and the head of the list (front of the queue) has its address in pointer variable queueFront, and the tail of the queue has its address in the pointer variable queueBack. 1. Write the sequence of statements to create an empty queue using a linked list. 2. Write the sequence of statements to perform the enqueue operation (with the value in variable dataVal) on a queue (implemented with a linked list). 3. Write the sequence of statements to perform the dequeue operation on a queue (implemented with a linked list) that is not empty and to store the value into the variable dataVal. 4. Write the definition of a member function dataType getlast( ) (of the class Queue implemented with a linked list) that returns the value of the last element entered in the queue. 5. Write the definition of a member function void printQueue2( ) (of the class Queue implemented with a linked list) that outputs the values of the elements of a queue in the order in which they are entered in the queue (without destroying the queue). ©2011 Gilbert Ndjatou Page 260 Class Templates In addition to function templates, you can define a class that can be applied to more than one data type. A class that can be applied to more than one data type is referred to as a class template. You define a class template in the same way that you define a function template by preceding its definition with the keyword template followed by a template parameter specified in angle brackets as follows: template < class T > or template < typename T > Where: T is the type parameter that represents the different data types that can be used in the definition of the class. Member functions of a class template are defined as function templates. In the function prototypes and the definitions of the member functions and friend functions of a class template, you follow the name of the class with < T >. Example: Stack< T >. Example The following is the class template that corresponds to the class Stack implemented with a static array: /*-------------------------------------------- Stack.Th ---------------------------------------------------*/ #ifndef STACK_TH #define STACK_TH #include <iostream> using namespace std; const int CAPACITY = 100; // maximum number of elements in the stack template < class dataType > class Stack { public: Stack( ); // constructor: create an empty stack void push( dataType dataVal ); // add an element on top of the stack void pop(dataType & dataVal ); // return the data at the top of the stack and then remove it void top(dataType & dataVal ); // return the data at the top of the stack bool isEmpty( ); // return true if the stack is empty and false otherwise bool isFull( ); // return true if the stack is full and false otherwise private: dataType stackArray[ CAPACITY ]; int stackTop; // The top of the stack }; ©2011 Gilbert Ndjatou Page 261 /*------------------------------------ definition of the constructor ----------------------------------------*/ template < class dataType > Stack<dataType> :: Stack( ) { stackTop = -1; // empty stack } /*------------------------------------- member function top( ) ----------------------------------------------*/ /*-----------------use a reference variable to return the data at the top of the stack ------------------*/ template < class dataType > void Stack<dataType> :: top( dataType &dataVal ) { if( isEmpty( ) ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackArray[ stackTop ]; } /*---------------------------------- member function pop( ) -------------------------------------------------*/ /*---- Use a reference variable to return the data at the top of the stack and then remove it -------*/ template < class dataType > void Stack<dataType> :: pop( dataType &dataVal ) { if( isEmpty( ) ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackArray[ stackTop ]; stackTop -- ; } ©2011 Gilbert Ndjatou Page 262 /*---------------------------------- member function push( ) ----------------------------------------------*/ template < class dataType > void Stack<dataType>:: push(dataType dataVal ) { if( isFull( ) ) // the stack is full { cerr << endl << “Stack is full: new element cannot be added” << endl; exit( 1 ); } /*-----------------------------add the element to the top of the stack ----------------------------------*/ stackTop ++; stackArray[ stackTop ] = dataVal; } /*--------------------------------------- member function isEmpty( )--------------------------------------*/ template < class dataType > bool Stack<dataType> :: isEmpty( ) { return( stackTop == -1 ); } /*------------------------------------- member function isFull( ) ---------------------------------------*/ template < class dataType > bool Stack<dataType> :: isFull( ) { return( stackTop == CAPACITY - 1 ); } #endif /*------------------------------------------------end of file Stack.Th---------------------------------------------*/ Using a Class Template to Define Objects You use a class template to define an object by specifying the data type to be used for the parameter type. Examples Stack<int> stackOfIntegers; Stack<char> stackOfCharacters, stch; Stack<Employee> stackOfEmployees; ©2011 Gilbert Ndjatou Page 263 Exercise L11 1. Write the class template that corresponds to the class Stack implemented with a Dynamic array. 2. Write the class template that corresponds to the class Queue implemented with a Dynamic array. Class Template Implemented with a Linked List One way to define a class template implemented with a linked list is to write the structure (or the class) definition of the linked list node in a private/public section of the class template definition. Example The following is the class template that corresponds to the class Stack implemented with a linked list: /*-------------------------------------------- LStack.Th ---------------------------------------------------*/ #ifndef LSTACK_TH #define LSTACK_TH #include <iostream> using namespace std; template < class dataType > class Stack { public: Stack( ); // create an empty stack Stack( const Stack<dataType> & original ); // copy constructor ~Stack( ); // destructor const Stack & operator =( const Stack<dataType> & rightHandSide); void push( dataType dataVal ); // add an element on top of the stack void pop(dataType & dataVal ); // return the data at the top of the stack and then remove it void top(dataType & dataVal ); // return the data at the top of the stack bool isEmpty( ); // return true if the stack is empty and false otherwise private: /*----------------- Definition of the linked list node --------------------------------*/ struct StackNode { dataType data; StackNode * next; }; stackNode *stackTop; // head of the linked list refers to the top of the stack }; ©2011 Gilbert Ndjatou Page 264 /*------------------------------------------------------- constructor-------------------------------------------*/ template < class dataType > Stack<dataType> :: Stack( ) : stackTop( NULL ) { } /*--------------------------------destructor ~Stack( )--------------------------------------------------------*/ template < class dataType > Stack<dataType> :: ~Stack( ) { StackNode *releasePtr; // to hold the address of the node to be removed while( StackTop != NULL ) { releasePtr = StackTop; StackTop = StackTop -> next; delete releasePtr; } } /*-------------------------copy constructor Stack( const Stack & original ) ------------------------------*/ /* Create a new stack object and initialize its elements with the elements of the original stack */ template < class dataType > Stack<dataType> :: Stack( const Stack<dataType> & original ) { if( original.stackTop == NULL ) // original stack is empty stackTop = NULL; else { StackNode *from, // hold the address of the current node in the original stack *to; // hold the address of the new node of the new stack /* - create the first node of the new stack and copy the first node of the original stack to it -*/ to = new StackNode; to -> data = original. stackTop -> data; stackTop = to; // copy the first node /*----------------------------------- copy the rest of the nodes -------------------------------------------*/ from = original.stackTop -> next; while( from != NULL ) { to -> next = new StackNode; // create a new node and connect it to the current node to = to -> next; // make the new node the current node to -> data = from -> data // copy the data to the current node ©2011 Gilbert Ndjatou Page 265 from = from -> next; // make the next node in the original stack the current node } to -> next = NULL; } } /*-----------------------overloaded assignment operator---------------------------------------------------------*/ /* use of const return to avoid left associativity: (A = B) = C */ /* do not copy a stack into itself. First destroy the left hand side stack and create another one with the same number of nodes as the right hand side stack */ template < class dataType > const Stack<dataType> & Stack<dataType> ::operator = (const Stack<dataType> &rightHandSide) { if ( this != & rightHandSide ) // copy only if the two stacks are not the same { this -> ~Stack( ); // destroy the left hand side stack if ( rightHandSide.empty( ) ) // the right hand side stack is empty stackTop = NULL; else { StackNode *from, // hold the address of the current node in the right hand side stack *to; // hold the address of the new node of the left hand side stack /* create the first node of the left hand side stack and copy the first node of the right hand side stack to it */ to = new StackNode; to -> data = rightHandSide. stackTop -> data; stackTop = to; // copy the first node /*----------------------------------- copy the rest of the nodes -------------------------------------*/ from = rightHandSide.stackTop -> next; while( from != NULL ) { to -> next = new StackNode; // create a new node and connect it to the current node to = to -> next; // make the new node the current node to -> data = from -> data // copy the data to the current node from = from -> next;// make the next node in the right hand side stack the current node } to -> next = NULL; } } return( *this ); } ©2011 Gilbert Ndjatou Page 266 /*---------------------------------- member function push( ) -----------------------------------------------*/ template < class dataType > void Stack<dataType>:: push( dataType dataVal ) { /*-----------------------------add the element to the top of the stack ----------------------------------*/ StackNode *newptr = new StackNode; newptr -> data = dataVal; newptr -> next = stackTop; stackTop = newptr; } /*------------------------------- member function isEmpty( template < class dataType > bool Stack<dataType> :: isEmpty( ) { return( stackTop == NULL ); } ) ----------------------------------------*/ /*---------------------------------- member function top( ) --------------------------------------------------*/ /*--------------------------------return the data at the top of the stack -------------------------------------*/ template < class dataType > void Stack<dataType> :: top(dataType & dataVal ) { if( stackTop == NULL ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackTop -> data; } /*---------------------------------- member function pop -----------------------------------------------------*/ /*---------------return the data at the top of the stack and clear the top of the stack -----------------*/ template < class dataType > void Stack<dataType> :: pop(dataType & dataVal ); { if( stackTop == NULL ) // the stack is empty { cerr << endl << “Stack is empty” << endl; exit( 1 ); } dataVal = stackTop -> data; // return the data at the top of the stack StackNode *discardptr; // to hold the address of the node to be removed discardptr = stackTop; stackTop = stackTop -> next; // set the top of the stack to the next node ©2011 Gilbert Ndjatou Page 267 delete discardptr; } #endif /*---------------------------------------------end of LStack.Th-------------------------------------------*/ Exercise L12 Write the class template of the class Queue implemented with a linked list. ©2011 Gilbert Ndjatou Page 268