0% found this document useful (0 votes)
128 views34 pages

Stacks, Queues, and Deques in C++

This document discusses abstract data types (ADTs) and describes the stack ADT in particular. It provides 3 key points: 1. An ADT specifies the data stored in a collection and the operations that can be performed on that data without defining how the data is implemented. The stack ADT is described as storing elements and following a last-in first-out ordering model. 2. The main stack operations are described as push, pop, and top. Auxiliary operations like size and empty are also listed. 3. Examples of stack applications and implementations are provided, including using a linked list to implement the stack and throwing exceptions for errors like an empty stack.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
128 views34 pages

Stacks, Queues, and Deques in C++

This document discusses abstract data types (ADTs) and describes the stack ADT in particular. It provides 3 key points: 1. An ADT specifies the data stored in a collection and the operations that can be performed on that data without defining how the data is implemented. The stack ADT is described as storing elements and following a last-in first-out ordering model. 2. The main stack operations are described as push, pop, and top. Auxiliary operations like size and empty are also listed. 3. Examples of stack applications and implementations are provided, including using a linked list to implement the stack and throwing exceptions for errors like an empty stack.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd

Stacks, Queues, Deques ADT

Reading: Chapter 5
Data Structures and
Algorithms in C++
Second Edition

1
Abstract Data Types (ADTs)
• ADT is an abstraction of a data structure
• ADT is A specification of a collection of data and the operations that
can be performed on it.
– Describes what a collection does, not how it does it
– An ADT specifies:
 Data stored
 Operations on the data
 Error conditions associated with operations

• We don't know exactly how a stack or queue is implemented, and we


don't need to.
– just need to understand the idea of the collection and what operations it can
perform.

2
ADT Example
• ADT modeling a simple stock trading system
 The data stored are buy/sell orders
 The operations supported are
• order buy(stock, shares, price)
• order sell(stock, shares, price)
• void cancel(order)

 Error conditions:
• Buy/sell a nonexistent stock
• Cancel a nonexistent order

3
The Stack ADT
• The Stack ADT stores arbitrary objects
• Insertions and deletions follow the last-in first-out scheme
• Think of a spring-loaded plate dispenser
• Main stack operations:
 push(object): inserts an element
 pop(): removes the last inserted element
 object top(): returns the last inserted element without removing it

• Auxiliary stack operations:


 integer size(): returns the number of elements stored
 boolean empty(): indicates whether no elements are stored

4
Applications of Stacks
Direct applications Indirect applications
• Page-visited history in a Web browser • Auxiliary data structure for algorithms
• Undo sequence in a text editor • Component of other data structures
• Chain of method calls in the C++ run-time
system
C++ Run-Time Stack main() { bar
int i = 5; PC = 1
• The C++ run-time system keeps track of the
foo(i); m=6
chain of active functions with a stack
}
• When a function is called, the system pushes
foo(int j) { foo
on the stack a frame containing PC = 3
int k;
• Local variables and return value j=5
k = j+1;
• Program counter, keeping track of the statement k=6
bar(k);
being executed
}
• When the function ends, its frame is popped main
from the stack and control is passed to the bar(int m) {
PC = 2
function on top of the stack …
i=5
}
• Allows for recursion
C++ STL Stack Interface
• size(): Return the number of elements in the stack.
• empty(): Return true if the stack is empty and false otherwise.
• push(e): Push e onto the top of the stack.
• pop(): Pop the element at the top of the stack.
• top(): Return a reference to the element at the top of the stack.

#include <iostream>
#include <stack>
using namespace std;
int main () {
stack<int> mystack;
for (int i=0; i<5; ++i)
[Link](i);
cout << "Popping out
elements...";
while (![Link]()){
cout << ' ' <<
[Link]();
[Link]();
}
cout << '\n'; 6
return 0;
Simple implementation of stack of int using linked nodes
Stack::Stack():head(NULL),n(0) {}
class Stack { //stack of integer Stack::~Stack(){ while (!empty()) pop();}
private:
IntNode void Stack::push(int value){ //old addFront
*head; IntNode *v = new IntNode(value);
int n; //to v->next = head;
count head = v;
public: n++;
Stack(); }
~Stack();
void push(int
bool Stack::empty() const{return
value);
(head==NULL);}
bool empty()
const;
int Stack::top() const {return head->elem;}
int top()
const;
void pop(); void Stack::pop(){//old removeFront
int size(); IntNode *v = head;
}; head = head->next;
n--;
delete v;
v=NULL;
}
7
Reusing our IntLinkedList
class Stack {
private: int main() {
IntLinkedList Stack s;
stack; [Link](3);
[Link](4);
public:
void push(int [Link](5);
value) { for (int i=0; i<5; ++i) [Link](i);
cout << "Popping out elements...";
[Link](value); while (![Link]()) {
}
cout << ' ' << [Link]();
void pop(){
[Link]();
[Link](); }
} }
int top(){
return
[Link]();
}
int size(){
return
[Link]();
} 8
bool empty(){
Reusing SLinkedList<E>
template <typename E>
class Stack {
private:
SLinkedList<E> stack;
int main() {
public:
void push(const E& value); Stack<int> s;
void pop() ; [Link](3);
E& top() const; [Link](4);
bool empty() const; [Link](5);
};
for (int i=0; i<5; ++i) [Link](i);
template <typename E> cout << "Popping out elements...";
void Stack<E>::push(const E& value) { while (![Link]()) {
[Link](value); cout << ' ' << [Link]();
} [Link]();
template <typename E>
}
void Stack<E>::pop() {
[Link](); }
}

template <typename E>


E& Stack<E>::top() const{
return [Link]();
}
template <typename E>
bool Stack<E>::empty() const{
return [Link]();
}
9
Our Stack Interface in C++
template <typename E>
• C++ interface corresponding
class Stack {
to our Stack ADT public:
• Uses an exception class int size() const;
StackEmpty bool empty() const;
• Different from the built-in const E& top() const
C++ STL class stack
throw(StackEmpty);
void push(const E& e);
void pop()
Exceptions throw(StackEmpty);
}
• Attempting the execution of an operation of ADT may sometimes cause an error
condition, called an exception
• Exceptions are said to be “thrown” by an operation that cannot be executed
• In the Stack ADT, operations pop and top cannot be performed if the stack is
empty
• Attempting pop or top on an empty stack throws a StackEmpty exception 10
Our Exception classes
// generic run-time exception
class RuntimeException {
private:
string errorMsg;
public:
RuntimeException(const string& err) { errorMsg = err;}
string getMessage() const { return errorMsg; }
};

// Exception thrown on performing top or pop of an empty stack.


class StackEmpty : public RuntimeException { public:
StackEmpty(const string& err) : RuntimeException(err) {}
};
class StackFull : public RuntimeException { public:
StackFull(const string& err) : RuntimeException(err) {}
};

class QueueEmpty : public RuntimeException { public:


QueueEmpty(const string& err) : RuntimeException(err) { }
};
11
Array-based Stack
• A simple way of implementing the Stack Algorithm size()
return t + 1
ADT uses an array
• We add elements from left to right Algorithm pop()
• A variable keeps track of the index of the if empty() then
throw
top element
StackEmpty
• The array storing the stack elements may else
become full tt1
• A push operation will then throw a return S[t +
Algorithm
1] push(o)
StackFull exception if t = [Link]()  1 then
throw
StackFull
else
tt+1
S[t]  o

12
Performance and Limitations

• Performance
• Let n be the number of elements in the stack
• The space used is O(n)
• Each operation runs in time O(1)
• Limitations
• The maximum size of the stack must be defined a priori and cannot be
changed
• Trying to push a new element into a full stack causes an implementation-
specific exception

13
Array-based Stack in C++

template <typename E>


class ArrayStack {
private:
E* S; // array holding the stack
int cap; // capacity
int t; // index of top element
public:
// constructor given capacity
ArrayStack( int c):S(new E[c]), cap(c), t(-1)
{ }
void pop() {
if (empty()) throw StackEmpty(“Pop from empty
stack”);
t--;
}
void push(const E& e) {
if (size() == cap) throw
StackFull(“Push to full stack”);
S[++ t] = e;
}
… (other methods of Stack interface) 14
Example use in C++ * indicates top

ArrayStack<int> A;
// A = [ ], size = 0
[Link](7);
// A = [7*], size = 1
[Link](13);
// A = [7, 13*], size = 2
cout << [Link]() << endl; [Link](); // A = [7*], outputs: 13
[Link](9);
// A = [7, 9*], size =
2
cout << [Link]() << endl;
// A = [7, 9*], outputs: 9
cout << [Link]() << endl; [Link](); // A = [7*], outputs: 9
ArrayStack<string> B(10);
// B = [ ], size = 0
[Link]("Bob");
// B = [Bob*], size = 1
15
[Link]("Alice");
Parentheses Matching
• Each “(”, “{”, or “[” must be paired with a matching “)”, “}”, or “[”
• correct: ( )(( )){([( )])}
• correct: ((( )(( )){([( )])}
• incorrect: )(( )){([( )])}
• incorrect: ({[ ])}
Algorithm ParenMatch(X,n):
Input: An array X of n tokens, each of which is either a grouping symbol, a variable, an
arithmetic operator, or a number
Output: true if and only if all the grouping symbols in X match
Let S be an empty stack
for i=0 to n-1 do
if X[i] is an opening grouping symbol then
[Link](X[i])
else if X[i] is a closing grouping symbol then
if [Link]() then
return false {nothing to match with}
if [Link]() does not match the type of X[i] then
return false {wrong type}
if [Link]() then
return true {every symbol matched}
else return false {some symbols were never matched}
16
Evaluating Arithmetic Expressions
– 3 * 2 + 7 = (14 – (3 * 2) ) + 7
Operator precedence
* has precedence over +/–

Associativity
operators of the same precedence group
evaluated from left to right
Example: (x – y) + z rather than x – (y + z)

Idea: push each operator on the stack, but first pop and
perform higher and equal precedence operations.

17
Algorithm for Evaluating Expressions
Algorithm doOp() Two stacks:
x  [Link](); opStk holds operators
y  [Link](); valStk holds values
op  [Link](); Use $ as special “end of input” token
[Link]( y op x ) with lowest precedence
Algorithm repeatOps( refOp ):
while ( [Link]() > 1  prec(refOp) ≤ prec([Link]())
doOp()
Algorithm EvalExp()
Input: a stream of tokens representing an arithmetic expression (with numbers)
Output: the value of the expression
while there’s another token z
if isNumber(z) then
[Link](z)
else
repeatOps(z);
[Link](z)
repeatOps($);
return [Link]()

Stacks 18
Algorithm on an Example Expression

Operator ≤ has lower


14 ≤ 4 – 3 * 2 + 7 precedence than +/–

4 –
14 ≤

3 *
$ $
4 –
7 $
14 ≤ F
+ -2 + 5
+ 14 ≤ 14 ≤
2 2
3 * 3 * 6
4 – 4 – 4 – -2 +
14 ≤ 14 ≤ 14 ≤ 14 ≤

Stacks 19
The Queue ADT
• The Queue ADT stores arbitrary objects
• Insertions and deletions follow the first-in first-out scheme
• Insertions are at the rear of the queue and removals are at the front of
the queue
• Main queue operations:
• enqueue(object): inserts an element at the end of the queue
• dequeue(): removes the element at the front of the queue
• Auxiliary queue operations:
• object front(): returns the element at the front without removing it
• integer size(): returns the number of elements stored
• boolean empty(): indicates whether no elements are stored
• Exceptions
• Attempting the execution of dequeue or front on an empty queue throws an
QueueEmpty
Queues 20
Example
Operation Output Q
enqueue(5) – (5)
enqueue(3) – (5, 3)
dequeue() – (3)
enqueue(7) – (3, 7)
dequeue() – (7)
front() 7 (7)
dequeue() – ()
dequeue() “error” ()
empty() true ()
enqueue(9) – (9)
enqueue(7) – (9, 7)
size() 2 (9, 7)
enqueue(3) – (9, 7, 3)
enqueue(5) – (9, 7, 3, 5)
dequeue() – (7, 3, 5)

21
The C++ STL Queue
size(): Return the number of elements in the queue.
empty(): Return true if the queue is empty and false otherwise.
push(e): Enqueue e at the rear of the queue.
pop(): Dequeue the element at the front of the queue.
front(): Return a reference to the element at the queue’s front.
back(): Return a reference to the element at the queue’s rear.
#include <iostream>
#include <queue>
Using namespace std;
int main (){
queue<int> myqueue;
int myint;
cout << "Please enter some integers (enter 0 to end):\n";
do {
cin >> myint;
[Link] (myint);
} while (myint);

cout << "myqueue contains: ";


while (![Link]()){
cout << ' ' << [Link]();
[Link]();
}
cout << '\n';
return 0;
} 22
Queue Interface in C++
• C++ interface corresponding to our Queue ADT

template <typename E>


class Queue {
public:
int size() const;
bool empty() const;
const E& front() const;
void enqueue (const E& e);
void dequeue();
};

23
Simple implementation of queue of int using linked nodes
Queue::Queue():head(NULL), tail(NULL), n(0) {}
Queue::~Queue(){ while (!empty()) pop();}

void Queue::enqueue(int value){//old addEnd from int


IntNode *v = new IntNode(value);
class Queue {
private: if (head==NULL) {
IntNode head = v;
*head; tail = v;
IntNode }else{
*tail;
tail->next = v;
int n; //to
count tail = v;
public: }
Queue(); n++;
~ Queue(); }
void
enqueue(int value); Void Queue::dequeue(){//old removeFront
bool
IntNode *v = head;
empty() const;
int front() head = head->next;
const; n--;
void dequeue(); delete v;
int size(); v=NULL;
}; }
bool Queue::empty() const{return (head==NULL);}
int Queue::front() const {return head->elem;}
int Queue::size(){return n;} 24
Reusing our IntLinkedList or SLinkedList
template <typename E>
class Queue {
class Queue { private:
private: SLinkedList<E> q;
IntLinkedList public:
q; void push(const E& value);
void pop() ;
E& top() const;
public: bool empty() const;
void };
enqueue(int value) {
template <typename E>
[Link](value); void Queue<E>::enqueue(const E& value) {
} [Link](value);
}
void dequeue () template <typename E>
{ void Queue<E>::dequeue() {
[Link]();
[Link](); }
}
int front(){ template <typename E>
return E& Queue<E>::top() const{
return [Link]();
[Link](); }
} template <typename E>
int size(){ bool Queue<E>::empty() const{
return return [Link]();
[Link](); }
25
}
Array-based Queue: using the Modulo Operator
to Implement a Circular Array
• Use an array of size N in a circular fashion
• Three variables keep track of the front and rear
f index of the front element
r index immediately past the rear element
n number of items in the queue
• Each time we increment f or r, we simply need to compute this
increment as “( f +1) mod N” or “(r +1) mod N,” respectively.
normal configuration
Q
0 1 2 f r
wrapped-around configuration
Q
0 1 2 r f 26
Queue Operations Algorithm size()
return n
• Use n to determine size and
emptiness Algorithm empty()
return (n = 0)
• Operation enqueue throws an
exception if the array is full Algorithm enqueue(o)
if size() = N  1 then
• Operation dequeue throws an throw QueueFull(“full
exception if the queue is empty Queue”)
else
Q[r]  o
r  (r + 1) mod N
nn+1

Algorithm dequeue()
if empty() then
throw
QueueEmpty(“empty queue”)
else
f  (f + 1) mod 27
N
Applications of Queues
• Waiting lists, bureaucracy
• Access to shared resources (e.g., printer)
• Multiprogramming
• Round Robin Schedulers
• We can implement a round robin scheduler using a queue Q by
repeatedly performing the following steps:
1. e = [Link](); [Link]()
2. Service element e
3. [Link](e)
Queue

Dequeue Enqueue

Shared
Service
28
Double-Ended Queues
• Queue-like data structure that supports insertion and deletion at both
the front and the rear of the queue
• usually pronounced “deck”
• Main queue operations:
insertFront(e): Insert a new element e at the beginning of the deque.
insertBack(e): Insert a new element e at the end of the deque.
eraseFront(): Remove the first element of the deque.
eraseBack(): Remove the last element of the deque
• Auxiliary queue operations:
• object front(): returns the element at the front
• object back(): Return the last element of the deque
• integer size(): returns the number of elements stored
• boolean empty(): indicates whether no elements are stored
• Exceptions should be thrown when attempting remove or get
elements from an empty deque
• All operations are in the constant order of complexity O(1)
The C++ STL Deque
• size(): Return the number of elements in the deque.
• empty(): Return true if the deque is empty and false otherwise.
• push_front(e): Insert e at the beginning the deque.
• push_back(e): Insert e at the end of the deque.
• pop_front(): Remove the first element of the deque.
• pop_back(): Remove the last element of the deque.
• front(): Return a reference to the deque’s first element.
• back(): Return a reference to the deque’s last element.
#include <iostream>
#include <deque>
using namespace std
int main () {
deque<int> mydeque;
mydeque.push_back (100);
mydeque.push_back (200);
mydeque.push_back (300);
cout << "Popping out the elements in mydeque:";
while (![Link]()) {
cout << ' ' << [Link]();
mydeque.pop_front();
}
cout << "\nThe final size of mydeque is " << int([Link]()) <<
'\n'; return 0;
}
Implementing Deque using our doubly linked list
typedef string Elem; // list element type
class DNode { // doubly linked list node
private:
Elem elem; // node element value
DNode* prev; // previous node in list
DNode* next; // next node in list
public:
DNode();
DNode(const Elem &data);
friend class DLinkedList; // allow DLinkedList access
};

class DLinkedList { // doubly linked list


public:
DLinkedList(); // constructor
~DLinkedList(); // destructor
bool empty() const; // is list empty?
const Elem& front() const; // get front element
const Elem& back() const; // get back element
void addFront(const Elem &e); // add to front of list
void addBack(const Elem &e); // add to back of list
void removeFront(); // remove from front
void removeBack(); // remove from back

private: // local type definitions


DNode* header; // pointers to front and back nodes
DNode* trailer;
};
Implementing deque using doubly linked list
typedef string Elem; // deque element type
class LinkedDeque { // deque as doubly linked list
public:
LinkedDeque(); // constructor
int size() const; // number of items in the deque
bool empty() const; // is the deque empty?
const Elem& front() const; // the first element
const Elem& back() const; // the last element
void insertFront(const Elem& e); // insert new first element
void insertBack(const Elem& e); // insert new last element
void removeFront(); // remove first element
void removeBack(); // remove last element
private: // member data
DLinkedList D; // linked list of elements
int n; // number of elements
};
class RuntimeException { //defining an exception class
private:
string errorMsg;
public:
RuntimeException(const string& err) { errorMsg = err;}
string getMessage() const { return errorMsg; }
};
class DequeEmpty : public RuntimeException { public:
DequeEmpty(const string& err) : RuntimeException(err) { }
};
Implementing deque using doubly linked list
LinkedDeque::LinkedDeque():n(0){}
void LinkedDeque::insertFront(const Elem& e) {
[Link](e);
n++;
}
void LinkedDeque::insertBack(const Elem& e) {
[Link](e);
n++;
}
void LinkedDeque::removeFront() {
if (empty())
throw DequeEmpty("removeFront of empty deque");
[Link]();
n--;
}
void LinkedDeque::removeBack() {
if (empty())
throw DequeEmpty("removeBack of empty deque");
[Link]();
n--;
}
Implementing deque using doubly linked list
bool LinkedDeque::empty() const {
return [Link]();
}
const Elem& LinkedDeque::back() const {
if (empty())
throw DequeEmpty("back of empty deque");
return [Link]();
}
const Elem& LinkedDeque::front() const {
if (empty())
throw DequeEmpty("front of empty deque");
return [Link]();
}
int main() {
LinkedDeque d;
[Link]("you");
[Link]("are");
[Link]("How");
[Link]("handsom");
cout <<endl <<[Link]()<<" " << [Link]();
[Link]();
[Link]();
cout <<endl <<[Link]()<<" " << [Link]();
}

You might also like