Chapter 6 Algorithm Analysis

Download Report

Transcript Chapter 6 Algorithm Analysis

Chapter 16
Stacks and Queues
Saurav Karmakar
Spring 2007
Objective

In this chapter we will learn:




Stacks
Queues
Different implementations (arrays and
linked list) of both
Comparison of implementation
Stack



A stack is a data structure that
works on the principle of
Last In First Out (LIFO).
So last item put on the stack is
the first item that can be taken
off, like a physical stack of
books/plates.
In stack new elements are
added to and removed from the
top of the structure.
Two Implementations of Stack


As Vector
Storing the items contiguously .
As List
Storing items noncontiguously.
Stack

Can be implemented with an array and
an integer that indicates the top
element of the stack (tos).

Empty stack : tos = -1.

Basic Operations : PUSH and POP.
How stack works
Empty stack
push(a)
push(b)
tos=1
tos=0
tos= -1
a
pop(b)
b
a
tos=0
a
Stack – Vector Implementation
A stack can be implemented with an
vector and an integer that indicates
the index of the top element.
//construct the stack
Template <class Object>
Stack<Object>::Stack():the Array(1){
topOfStack = -1;
}
//test if the stack is logically empty
Template <class Object>
Stack<Object>::isEmpty() const{
return topOfStack == -1;
}
template <class Object>
class Stack{
{
public:
Stack( );
bool isEmpty( ) const;
const Object & top( ) const;
void makeEmpty( );
void pop( );
void push( const Object & x );
Object topAndPop( );
private:
vector<Object> theArray;
int topOfStack;
};
The push/pop function (vector-based)
template <class Object>
void Stack<Object>::push( const Object & x )
{
if( topOfStack == theArray.size( ) - 1 )
theArray.resize( theArray.size( ) * 2 + 1 );
theArray[ ++topOfStack ] = x;
} // If there is no vector doubling, push takes constant time, otherwise it
// takes O(N) time. But it does not happen often.
template <class Object>
void Stack<Object>::pop( const Object & x )
{
if( isEmpty())
throw UnderflowException();
topOfStack--;
}
Linked List Implementation


Advantage of the linked list : using
only one pointer per item at a time.
Disadvantage of contiguous vector
implementation : using excess space
equal to the number of vacant array
items .
Linked List
Implementation of Stack

The stack class can be implemented as
a linked list in which the top of the
stack is represented by the first item
in the list.
topOfStack
Linked List
Implementation of Stack

Each stack item stores


element value
pointer to next element
Application of Stack




Recognizing palindromes
Checking balanced expressions
Evaluating algebraic expressions is easier.
Searching networks, traversing trees
(keeping a track where we are).
Infix to Postfix Conversion









Scan the Infix string from left to right.
Initialise an empty stack.
If the scannned character is an operand, add it to the Postfix string.
If the scanned character is an operator and if the stack is empty Push
the character to stack.
If the scanned character is an Operator and the stack is not
empty, compare the precedence of the character with the
element on top of the stack (topStack).
If topStack has higher precedence over the scanned
character Pop the stack
else Push the scanned character to stack.
Repeat this step as long as stack is not empty and topStack has
precedence over the character.
Repeat this step till all the characters are scanned.
(After all characters are scanned, we have to add any character that
the stack may have to the Postfix string.) If stack is not empty add
topStack to Postfix string and Pop the stack.
Repeat this step as long as stack is not empty.
Return the Postfix string.
Example : String a+b*c-d
STACK
PostfixString
Postfix Expression Evaluation
for each character C in a given string
{
if C is an operand
push C onto stack;
else // C is an operator
{
pop item from stack, and store in Opr2;
pop item from stack, and store in Opr1;
result = Opr1 C Opr2, using C as an operator;
push result onto stack;
}
}
QUEUE



A queue is an abstract
data struture where
various entities such as
data, objects, persons, or
events are stored and
waiting to be processed.
The most well known
operation of the queue is
the First-In-First-Out
(FIFO) queue process .
In a FIFO queue, the first
element in the queue will
be the first one out
Queue

Can be implemented using



Vector/Array
Link List
Two main Operations :


Enqueue
Dequeue
Queue:
Vector/Array Implementation




Store items in an vector/array with front
item at index zero and back item at
index Back.
Enqueue is easy: increment Back.
Dequeue is inefficient: all elements
have to be shifted. (If use only one
index)
Result: Dequeue will be
O (N ).
Queue
Step 1. makeEmpty()
Back
Step 4. dequeue()
Back
Back
Step 2. enqueue()
a
b
Step 3. enqueue()
Back
a
b
After dequeue() :
Back
b
Better Idea


Keep a Front index.
To Dequeue, increment Front.
Therefore, Dequeue takes constant time
now.
a
b
c
Front
a
d
Back
b
Front
c
d
Back
Queue
Step 1. makeEmpty()
Step 4. dequeue()
Back
Back
Front
Back
b
Step 2. enqueue()
Front
a
After dequeue() :
Front
Step 3. enqueue()
Back
a
Front
b
Back
b
Front
Circular Implementation



Previous implementation is O( 1 ) per
operation.
However, after vector.size() times
enqueues, we are full, even if queue is
logically nearly empty for dequeue
opeartions on the elements.
Solution: use wraparound to reuse the
cells at the start of the vector. To
increment, add one, but if that goes past
end, reset to zero.
Problem with the approach :
Back
c
d
Front
Solution : Recycle / Wrap Around
Back
e
c
Front
d
Circular Example

Both Front and Back wraparound as
needed.
b
c
d
e
Front
g
b
Back
Front
f
Back
c
d
e
f
QUEUE--Vector Implementation


Mostly straightforward; maintain
 Front
 Back
 CurrentSize: Current number of items in queue
Only tricky part is vector doubling because the queue
items are not necessarily stored in an array starting
at location 0, and the contiguity of wraparound must
be maintained.
Queue – Vector Implementation
Template <class Object>
Class Queue{
public:
Queue();
bool isEmpty() const;
const Object & getFront() const;
void makeEmpty();
Object dequeue();
void enqueue (const Ojbect & x);
private:
vector<Object> theArray;
int currentSize;
int front;
int back;
void increment (int & x) const;
void doubleQueue();
}
template <class Object>
void Queue<Object>::enqueue(const Object & x){
if(currentSize == theArray.size())
doubleQueue();
increment( back);
theArray[back] = x;
currentSize++;
}
template <class Object>
void Queue<Object>::doubleQueue() {
theArray.resize(theArray.size() * 2 + 1);
if(front != 0){
for(int i=0; i<front; i++){
theArray[i+currentSize] = theArray[i];
back += currentSize;
}
}
Queue – Vector Implementation cont.
template <class Object>
const Object & Queue<Object>::getFront() const {
if (isEmpty())
throw UnderflowException();
return theArray[front];
}
template <class Object>
void Queue<Object>::makeEmpty(){
currentSize = 0;
front = 0;
back = theArray.size() –1;
}
template <class Object>
Object Queue<Object>::dequeue() {
if( isEmpty())
throw UnderflowException();
currentSize--;
Object frontItem = theArray[front];
increment(front);
return frontItem;
}
Linked List Implementation


Advantage of the linked list is excess
memory is only one pointer per item.
In contrast, a contiguous vector
implementation uses excess space.
Queue

front
Same idea as like STACK, but has front
and back
back
Comparison of the Two Methods


Both of them run in constant time per
operation.
The vector version is likely to be faster.
But it has two drawbacks:


The wraparound is a little confusing;
It might waste more space.
Deque/Double-Ended Queue




A deque is a double-ended queue.
A deque is a little modification on the queue
data structure, where access is given to both
the ends.
Operations : addFront, addRear,
removeFront, removeRear.
That is, a deque is especially optimized for
pushing and popping elements at the
beginning and end. As with vectors, storage
management is handled automatically.
Common errors (Page 561)



Do not delete the top node directly
before adjusting the top of the stack
pointer
Be aware of memory leaks
Access is constant time in both of these
implementations.