Stack and Queues using Linked Structures
Stack and Queues using Linked Structures. Kruse and Ryba Ch 4. Implementing stacks using arrays. Simple implementation The size of the stack must be determined when a stack object is declared Space is wasted if we use less elements We cannot "push" more elements than the array can hold.
Stack and Queues using Linked Structures
E N D
Presentation Transcript
Stack and Queues using Linked Structures Kruse and Ryba Ch 4
Implementing stacks using arrays • Simple implementation • The size of the stack must be determined when a stack object is declared • Space is wasted if we use less elements • We cannot "push" more elements than the array can hold
Dynamic allocation of eachstack element • Allocate memory for each new element dynamically ItemType* itemPtr; ... itemPtr = new ItemType; *itemPtr = newItem;
Dynamic allocation of eachstack element (cont.) • How should we preserve the order of the stack elements?
Chaining the stack elements together (cont.) • Each node in the stack should contain two parts: • info: the user's data • next: the address of the next element in the stack
Node Type • template<class ItemType> struct NodeType { ItemType info; NodeType* next; };
First and last stack elements • We need a data member to store the pointer to the top of the stack • The next element of the last node should contain the value NULL
Stack class specification // forward declaration of NodeType (like function prototype) template<class ItemType> struct NodeType; template<class ItemType> class StackType { public: StackType(); ~StackType(); void MakeEmpty(); bool IsEmpty() const; bool IsFull() const; void Push(ItemType); void Pop(ItemType&); private: NodeType<ItemType>* topPtr; };
Pushing on a non-empty stack (cont.) • The order of changing the pointers is very important !!
Function Push template <class ItemType> void StackType<ItemType>::Push(ItemType item) { NodeType<ItemType>* location; location = new NodeType<ItemType>; location->info = newItem; location->next = topPtr; topPtr = location; }
Popping the top element(cont.) Need to use a temporary pointer
Function Pop template <class ItemType> void StackType<ItemType>::Pop(ItemType& item) { NodeType<ItemType>* tempPtr; item = topPtr->info; tempPtr = topPtr; topPtr = topPtr->next; delete tempPtr; }
Other Stack functions template<class ItemType> StackType<ItemType>::StackType() { topPtr = NULL; } template<class ItemType> void StackType<ItemType>::MakeEmpty() { NodeType<ItemType>* tempPtr; while(topPtr != NULL) { tempPtr = topPtr; topPtr = topPtr->next; delete tempPtr; } }
Other Stack functions (cont.) template<class ItemType> bool StackType<ItemType>::IsEmpty() const { return(topPtr == NULL); } template<class ItemType> bool StackType<ItemType>::IsFull() const { NodeType<ItemType>* location; location = new NodeType<ItemType>; if(location == NULL) return true; else { delete location; return false; } } template<class ItemType> StackType<ItemType>::~StackType() { MakeEmpty(); }
Copy Constructors for stacks • Suppose we want to make a copy of a stack, will the following work? template<class ItemType> void StackType(StackType<ItemType> oldStack, StackType<ItemType>& copy) { StackType<ItemType> tempStack; ItemType item; while(!oldStack.IsEmpty()) { oldStack.Pop(item); tempStack.Push(item); } while(!tempStack.IsEmpty()) { tempStack.Pop(item); copy.Push(item); } }
Copy Constructors (cont.) • Shallow Copy: an object is copied to another object without copying any pointed-to data • Deep Copy: makes copies of any pointed-to data When do you need a copy constructor? (1) When parameters are passed by value (2) Return the value of a function (return thisStack;) (3) Initializing a variable in a declaration (StackType<int> myStack=yourStack;)
Copy constructor for stacks template<class ItemType> Stack Type<ItemType>::StackType(const StackType<ItemType>& anotherStack) { NodeType<ItemType>* ptr1; NodeType<ItemType>* ptr2; if(anotherStack.topPtr == NULL) topPtr = NULL; else { topPtr = new NodeType<ItemType>; topPtr->info = anotherStack.topPtr->info; ptr1 = anotherStack.topPtr->next; ptr2 = topPtr; while(ptr1 !=NULL) { ptr2->next = new NodeType<ItemType>; ptr2 = ptr2->next; ptr2->info = ptr1->info; ptr1 = ptr1->next; } ptr2->next = NULL; } } Alternatively, copy one stack to another using the assignment operator (you need to overload it though!!)
Implementing queues using arrays • Simple implementation • The size of the queue must be determined when a stack object is declared • Space is wasted if we use less elements • We cannot "enqueue" more elements than the array can hold
Implementing queues using linked lists • Allocate memory for each new element dynamically • Link the queue elements together • Use two pointers, qFront and qRear, to mark the front and rear of the queue
Queue class specification // forward declaration of NodeType (like function prototype) template<class ItemType> struct NodeType; template<class ItemType> class QueueType { public: QueueType(); ~QueueType(); void MakeEmpty(); bool IsEmpty() const; bool IsFull() const; void Enqueue(ItemType); void Dequeue(ItemType&); private: NodeType<ItemType>* qFront; NodeType<ItemType>* qRear; };
Enqueuing (empty queue) • We need to make qFront point to the new node also qFront = NULL New Node qRear = NULL newNode
Function Enqueue template <class ItemType> void QueueType<ItemType>::Enqueue(ItemType newItem) { NodeType<ItemType>* newNode; newNode = new NodeType<ItemType>; newNode->info = newItem; newNode->next = NULL; if(qRear == NULL) qFront = newNode; else qRear->next = newNode; qRear = newNode; }
Dequeueing (the queue contains only one element) • We need to reset qRear to NULL also qFront After dequeue: qFront = NULL qRear = NULL Node qRear
Function Dequeue template <class ItemType> void QueueType<ItemType>::Dequeue(ItemType& item) { NodeType<ItemType>* tempPtr; tempPtr = qFront; item = qFront->info; qFront = qFront->next; if(qFront == NULL) qRear = NULL; delete tempPtr; }
qRear, qFront revisited • The relative positions of qFront and qRear are important!
Other Queue functions template<class ItemType> void QueueType<ItemType>::MakeEmpty() { NodeType<ItemType>* tempPtr; while(qFront != NULL) { tempPtr = qFront; qFront = qFront->next; delete tempPtr; } qRear=NULL; }
Other Queue functions (cont.) template<class ItemType> bool QueueType<ItemType>::IsEmpty() const { return(qFront == NULL); } template<class ItemType> bool QueueType<ItemType>::IsFull() const { NodeType<ItemType>* ptr; ptr = new NodeType<ItemType>; if(ptr == NULL) return true; else { delete ptr; return false; } }
Other Queue functions (cont.) template<class ItemType> QueueType<ItemType>::~QueueType() { MakeEmpty(); }
Comparing queue implementations • Memory requirements • Array-based implementation • Assume a queue (size: 100) of strings (80 bytes each) • Assume indices take 2 bytes • Total memory: (80 bytes x 101 slots) + (2 bytes x 2 indexes) = 8084 bytes • Linked-list-based implementation • Assume pointers take 4 bytes • Total memory per node: 80 bytes + 4 bytes = 84 bytes
Comparing queue implementations (cont.)
Comparing queue implementations (cont.) • Memory requirements • Array-based implementation • Assume a queue (size: 100) of short integers (2 bytes each) • Assume indices take 2 bytes • Total memory: (2 bytes x 101 slots) + (2 bytes x 2 indexes) = 206 bytes • Linked-list-based implementation • Assume pointers take 4 bytes • Total memory per node: 2 bytes + 4 bytes = 6 bytes
Comparing queue implementations (cont.)