Stacks • Linear list. • One end is called top. • Other end is called bottom. • Additions to and removals from the top end only.
Feb 22, 2016
Stacks
• Linear list.• One end is called top.• Other end is called bottom.• Additions to and removals from the top end
only.
Stack Of Cups
• Add a cup to the stack.
bottom
top
C
A
B
D
E
F
• Remove a cup from new stack.• A stack is a LIFO list.
bottom
top
C
A
B
D
E
The Abstract Class stack
template<class T>class stack { public: virtual ~stack() {} virtual bool empty() const = 0; virtual int size() const = 0; virtual T& top() = 0; virtual void pop() = 0; virtual void push(const T& theElement) = 0;};
Derive From A Linear List Class
• arrayList• chain
Derive From arrayList
stack top is either left end or right end of linear list empty() => arrayList::empty()
• O(1) time size() => arrayList::size()
• O(1) time top() => get(0) or get(size() - 1)
• O(1) time
0 1 2 3 4 5 6
a b c d e
Derive From arrayList
• when top is left end of linear list push(theElement) => insert(0, theElement) O(size) time pop() => erase(0) O(size) time
0 1 2 3 4 5 6
a b c d e
Derive From arrayList
when top is right end of linear list• push(theElement) => insert(size(), theElement)• O(1) time• pop() => erase(size()-1)• O(1) time
use right end of list as top of stack
0 1 2 3 4 5 6
a b c d e
Derive From Chain
stack top is either left end or right end of linear list
empty() => chain::empty()• O(1) time
size() => chain::size()• O(1) time
a b c d e
NULL
firstNode
Derive From Chain
a b c d e
NULL
firstNode
– when top is left end of linear list top() => get(0) O(1) time push(theElement) => insert(0, theElement) O(1) time pop() => erase(0) O(1) time
Derive From Chain
a b c d enull
firstNode
– when top is right end of linear list• top() => get(size() - 1)• O(size) time• push(theElement) => insert(size(), theElement)• O(size) time• pop() => erase(size()-1)• O(size) time
– use left end of list as top of stack
Derive From arrayList
template<class T>class derivedArrayStack : private arrayList<T>, public stack<T>{ public: // code for stack methods comes here };
Constructor
derivedArrayStack(int initialCapacity = 10) : arrayList<T> (initialCapacity) {}
empty() And size()
bool empty() const{return arrayList<T>::empty();}
int size() const{return arrayList<T>::size();}
0 1 2 3 4 5 6
a b c d e
top()
T& top(){ if (arrayList<T>::empty()) throw stackEmpty(); return get(arrayList<T>::size() - 1);}
0 1 2 3 4 5 6
a b c d e
push(theElement)
void push(const T& theElement){insert(arrayList<T>::size(), theElement);}
0 1 2 3 4 5 6
a b c d e
pop()
void pop(){ if (arrayList<T>::empty()) throw stackEmpty(); erase(arrayList<T>::size() - 1);}
0 1 2 3 4 5 6
a b c d e
Evaluation• Merits of deriving from arrayList
Code for derived class is quite simple and easy to develop.
Code is expected to require little debugging. Code for other stack implementations such as a
linked implementation are easily obtained.• Just replace private arrayList<T> with private chain<T>• For efficiency reasons we must also make changes to use
the left end of the list as the stack top rather than the right end.
Demerits
• Unecessary work is done by the code. top() verifies that the stack is not empty before get
is invoked. The index check done by get is, therefore, not needed.
insert(size(), theElement) does an index check and a copy_backward. Neither is needed.
pop() verifies that the stack is not empty before erase is invoked. erase does an index check and a copy. Neither is needed.
So the derived code runs slower than necessary.
Evaluation
• Code developed from scratch will run faster but will take more time (cost) to develop.
• Tradeoff between software development cost and performance.
• Tradeoff between time to market and performance.
• Could develop easy code first and later refine it to improve performance.
A Faster pop()if (arrayList<T>::empty()) throw stackEmpty();erase(arrayList<T>::size() - 1);
vs.try {erase(arrayList<T>::size()-1);catch (illegalIndex{throw stackEmpty();}
Code From Scratch• Use a 1D array stack whose data type is T.
same as using array element in arrayList• Use an int variable stackTop.
Stack elements are in stack[0:stackTop]. Top element is in stack[stackTop]. Bottom element is in stack[0]. Stack is empty iff stackTop = -1. Number of elements in stack is stackTop + 1.
Code From Scratchtemplate class<T>class arrayStack : public stack<T>{ public: // public methods come here private: int stackTop; // current top of stack int arrayLength; // stack capacity T *stack; // element array};
Constructor
template<class T>arrayStack<T>::arrayStack(int initialCapacity){// Constructor. if (initialCapacity < 1) {// code to throw an exception comes here } arrayLength = initialCapacity; stack = new T[arrayLength]; stackTop = -1;}
push(…)
template<class T>void arrayStack<T>::push(const T& theElement){// Add theElement to stack. if (stackTop == arrayLength - 1) {// code to double capacity coms here } // add at stack top stack[++stackTop] = theElement;}
0 1 2 3 4
a b c d e
top
pop()
void pop(){ if (stackTop == -1) throw stackEmpty(); stack[stackTop--].~T(); // destructor for T}
0 1 2 3 4
a b c d e
top
Linked Stack From Scratch
• See text.
Performance
50,000,000 pop, push, and peek operations initial capacityClass 10 50,000,000arrayStack 2.7s 1.5sderivedArrayStack 7.5s 6.3sSTL 5.6s -derivedLinkedStack 41.0s 41.0slinkedStack 40.5s 40.5s
Queues
• Linear list.• One end is called front.• Other end is called rear.• Additions are done at the rear only. • Removals are made from the front only.
The Abstract Class queuetemplate <class T>class queue { public: virtual ~queue() {} virtual bool empty() const = 0; virtual int size() const = 0; virtual T& front() = 0; virtual T& back() = 0; virtual void pop() = 0; virtual void push(const T& theElement) = 0;};
Revisit Of Stack Applications• Applications in which the stack cannot be
replaced with a queue. Parentheses matching. Towers of Hanoi. Switchbox routing. Method invocation and return. Try-catch-throw implementation.
• Application in which the stack may be replaced with a queue. Rat in a maze.
• Results in finding shortest path to exit.
Derive From arrayList
when front is left end of list and rear is right end• empty() => queue::empty()
– O(1) time• size() => queue::size(0)
– O(1) time• front() => get(0)
– O(1) time• back() => get(size() - 1)
– O(1) time
0 1 2 3 4 5 6
a b c d e
Derive From arrayList
• pop() => erase(0)– O(size) time
• push(theElement) => insert(size(), theElement)– O(1) time
0 1 2 3 4 5 6
a b c d e
Derive From arrayList
when front is right end of list and rear is left end• empty() => queue::empty()
– O(1) time• size() => queue::size(0)
– O(1) time• front() => get(size() - 1)
– O(1) time• back() => get(0)
– O(1) time
0 1 2 3 4 5 6
a b c d e
Derive From arrayList
• pop() => erase(size() - 1)– O(1) time
• push(theElement) => insert(0, theElement)– O(size) time
0 1 2 3 4 5 6
a b c d e
Derive From arrayList
to perform each opertion in O(1) time (excluding array doubling), we need a customized array representation.
Derive From extendedChain
a b c d e
NULL
firstNode lastNode
front rear when front is left end of list and rear is right end
• empty() => extendedChain::empty()– O(1) time
• size() => extendedChain::size()– O(1) time
Derive From ExtendedChain
a b c d e
NULL
firstNode lastNode
front rear
• front() => get (0)– O(1) time
• back() => getLast() … new method
Derive From ExtendedChain
a b c d e
NULL
firstNode lastNode
front rear
• push(theElement) => push_back(theElement)– O(1) time
• pop() => erase(0)– O(1) time
Derive From extendedChain
e d c b a
NULL
firstNode lastNode
rear front when front is right end of list and rear is left end
• empty() => extendedChain::empty()– O(1) time
• size() => extendedChain::size()– O(1) time
Derive From extendedChain
a b c d e
NULL
firstNode lastNode
rear front
• front() => getLast()– O(1) time
• back() => get(0)– O(1) time
Derive From extendedChain
a b c d e
NULL
firstNode lastNode
rear front
• push(theElement) => insert(0, theElement)– O(1) time
• pop() => erase(size() - 1)– O(size) time
Custom Linked Code
• Develop a linked class for queue from scratch to get better preformance than obtainable by deriving from extendedChain.
Custom Array Queue
• Use a 1D array queue.
queue[]
• Circular view of array.
[0]
[1]
[2] [3]
[4]
[5]
Custom Array Queue• Possible configuration with 3 elements.
[0]
[1]
[2] [3]
[4]
[5]
A B
C
Custom Array Queue• Another possible configuration with 3
elements.
[0]
[1]
[2] [3]
[4]
[5]AB
C
Custom Array Queue• Use integer variables theFront and theBack.
– theFront is one position counterclockwise from first element
– theBack gives position of last element– use front and rear in figures
[0]
[1]
[2] [3]
[4]
[5]
A B
Cfront rear
[0]
[1]
[2] [3]
[4]
[5]AB
Cfront
rear
Push An Element
[0]
[1]
[2] [3]
[4]
[5]
A B
Cfront rear
• Move rear one clockwise.
Push An Element• Move rear one clockwise.
[0]
[1]
[2] [3]
[4]
[5]
A B
Cfront
rear
• Then put into queue[rear].
D
Pop An Element
[0]
[1]
[2] [3]
[4]
[5]
A B
Cfront rear
• Move front one clockwise.
Pop An Element
[0]
[1]
[2] [3]
[4]
[5]
A B
C
frontrear
• Move front one clockwise.• Then extract from queue[front].
Moving rear Clockwise
[0]
[1]
[2] [3]
[4]
[5]
A B
Cfront rear
• rear++; if (rear = = arrayLength) rear = 0;
• rear = (rear + 1) % arrayLength;
Empty That Queue
[0]
[1]
[2] [3]
[4]
[5]AB
Cfront
rear
Empty That Queue
[0]
[1]
[2] [3]
[4]
[5]B
C
front
rear
Empty That Queue
[0]
[1]
[2] [3]
[4]
[5]
C
front
rear
Empty That Queue
• When a series of removes causes the queue to become empty, front = rear.
• When a queue is constructed, it is empty.• So initialize front = rear = 0.
[0]
[1]
[2] [3]
[4]
[5]front
rear
A Full Tank Please
[0]
[1]
[2] [3]
[4]
[5]AB
Cfront
rear
A Full Tank Please
[0]
[1]
[2] [3]
[4]
[5]AB
Cfront
rearD
A Full Tank Please
[0]
[1]
[2] [3]
[4]
[5]AB
Cfront
rearD E
A Full Tank Please
[0]
[1]
[2] [3]
[4]
[5]AB
Cfront
rear
D E
F
• When a series of adds causes the queue to become full, front = rear.
• So we cannot distinguish between a full queue and an empty queue!
Ouch!!!!!• Remedies.
Don’t let the queue get full.• When the addition of an element will cause the queue to be
full, increase array size.• This is what the text does.
Define a boolean variable lastOperationIsPush.• Following each push set this variable to true.• Following each pop set to false.• Queue is empty iff (front == rear) && !lastOperationIsPush• Queue is full iff (front == rear) && lastOperationIsPush