2. The stack ADT
Definition
Subset of a list with two main operations
Push: adds an item to the list
Pop: removes the most recently added item from the list
Also sometimes called LIFO (Last In, First Out)
2 / 15
3. Typical implementations
Array (vector)
Push and pop to/from end of array
amortized top
top
top
pop()
push()
(Singly) linked list
Push and pop to/from beginning of list
top
top
top
pop()
push()
The stack ADT
Typical API
// Return number of items in stack
unsigned int Size();
// Return top of stack
T& Top();
// Remove top of stack
void Pop();
// Push item to top of stack
void Push(const T &item);
stack_vector.h
O(1)
O(1)
3 / 15
4. template <typename T>
class Stack {
...
private:
std::vector<T> items;
};
template <typename T>
unsigned int Stack<T>::Size() {
return items.size();
}
stack_vector.h
template <typename T>
T& Stack<T>::Top(void) {
if (!items.size())
throw std::underflow_error("Empty stack!");
return items.back();
}
template <typename T>
void Stack<T>::Pop() {
if (!items.size())
throw std::underflow_error("Empty stack!");
items.pop_back();
}
template <typename T>
void Stack<T>::Push(const T &item) {
items.push_back(item);
}
stack_vector.h
The stack ADT
Array implementation
Entirely based on std::vector
Wrappers to subset of methods
With a bit of error management
4 / 15
5. template <typename T>
class Stack {
...
private:
unsigned int cur_size = 0;
std::forward_list<T> items;
};
template <typename T>
unsigned int Stack<T>::Size() {
return cur_size;
}
stack_list.h
template <typename T>
T& Stack<T>::Top(void) {
if (!cur_size)
throw std::underflow_error("Empty stack!");
return items.front();
}
template <typename T>
void Stack<T>::Pop() {
if (!cur_size)
throw std::underflow_error("Empty stack!");
items.pop_front();
cur_size--;
}
template <typename T>
void Stack<T>::Push(const T &item) {
items.push_front(item);
cur_size++;
}
stack_list.h
The stack ADT
Linked list implementation
Entirely based on std::forward_list
With current size info
With a bit of error management
5 / 15
6. The stack ADT
Some application examples
Balanced symbol checking
Compiler checks that every right brace, bracket and parenthesis correspond to its left
counterpart.
if (array[0) { std::cout << "Hurray!" << std::endl; }
^
error: expected ']' before ')' token
Algorithm (in pseudocode) using a stack:
create empty stack
do
get next token in file
if token is opening symbol
push on stack
else if token is closing symbol
pop from stack
if popped symbol is not the corresponding opening symbol
report error!
while not end of file
if stack not empty
report error!
6 / 15
7. create empty stack
do
if token is number then
push to stack
else if token is operator then
pop two numbers from stack
perform operation
push result to stack
while there are still tokens
pop final result from stack
The stack ADT
Postfix expressions
Infix expressions can be interpreted differently, according to which precedence is given
to operator. Ex: how to calculate 4 + 5 + 6 * 2?
Is it (4 + 5 + 6) * 2 = 30 or 4 + 5 + (6 * 2) = 21?
The latter is the scientific answer, as multiply has higher precedence
With a postfix notation (a.k.a. Reverse Polish Notation), the order of evaluation becomes
explicit and does not require parentheses
4 5 + 6 2 * +
Used by HP in all their calculators in the 70s and 80s
Can easily be computed using a stack!
7 / 15
8. int f(int n) {
if (n == 0)
return 1
else
return n * f(n - 1);
}
void g(void) {
int a = 4;
int b = f(a);
}
g()
f()
a = 4
b = ?
n = 4
n = 3
n = 2
f()
f()
...
g()
a = 4
b = 24
time
The stack ADT
Function calls
Inherent structure under most function calling conventions
Call stack:
Arguments to a function are pushed into new stackframe
Stackframe also holds local variables (e.g. in C)
Popped when function returns
Supports nested and recursive function calls
8 / 15
9. The stack ADT
Many other uses
Java virtual machine
Undo in word processor
Back button in web browser
Etc.
9 / 15
10. The queue ADT
Definition
Also a subset of a list with two main operations:
Enqueue: adds an item to the list
Dequeue: removes the least recently added item from the list
Also sometimes called FIFO (First In, First Out)
10 / 15
11. (Doubly) linked list
Push to end and pop from beginning of list
front tail
Circular array
tail front
The queue ADT
Typical API
// Return number of items in queue
unsigned int Size();
// Return front of queue
T& Front();
// Remove front of queue
void Pop();
// Push item to back of queue
void Push(const T &item);
queue_list.h
Typical implementations
O(1)
11 / 15
12. template <typename T>
class Queue {
...
private:
std::list<T> items;
};
template <typename T>
unsigned int Queue<T>::Size() {
return items.size();
}
queue_list.h
template <typename T>
T& Queue<T>::Front() {
if (!items.size())
throw std::underflow_error("Empty queue!");
return items.front();
}
template <typename T>
void Queue<T>::Pop() {
if (!items.size())
throw std::underflow_error("Empty queue!");
items.pop_front();
}
template <typename T>
void Queue<T>::Push(const T &item) {
items.push_back(item);
}
queue_list.h
The queue ADT
Linked list implementation
Entirely based on std::list
Wrappers to subset of methods
With a bit of error management
12 / 15
13. Some algorithms related to graphs
Queue of jobs for printers
Pipes (inter-process communication) $ cat final_grades.csv | grep 'F' | wc -l
0
$ echo "Hurray" | banner
...
I/O requests scheduling (e.g. disk,
network)
The queue ADT
Some application examples
13 / 15
14. Standard C++ containers
The C++ Standard Library defines the implementations of a stack ADT and a queue ADT.
Both are only adaptor containers as they are only wrappers to underlying sequence
containers:
std::stack
Can be implemented with std::dequeue (default), std::list and std::vector
// By default, will be based on a deque
std::stack<int> st_dq;
// But can also specify another container
std::stack<int, std::list<int>> st_lst;
std::queue
Can be implemented with std::dequeue (default) and std::list
14 / 15
15. void check_balance(std::string &str) {
static const std::string symbols[] = { "({[", ")}]" };
std::stack<char> balance;
for (auto c : str) {
// 1. Check if c is an opening symbol
if (symbols[0].find(c) != std::string::npos) {
balance.push(c);
continue;
}
// 2. Check if c is an closing symbol
auto pos = symbols[1].find(c);
if (pos == std::string::npos)
continue; // skip if not
// 3. Check proper symbol matching
if (!balance.empty() && balance.top() == symbols[0][pos]) {
balance.pop();
} else {
std::cerr << "Unbalanced string!" << std::endl;
return;
}
}
if (!balance.empty())
std::cerr << "Unbalanced string!" << std::endl;
}
std_stack_example.cc
Execution:
$ "[(){}]"
$ ")"
Unbalanced string!
$ "[(){]"
Unbalanced string!
Standard C++ containers
Stack
15 / 15