Stack Implementation
Stack Implementation
1
Stack
A stack is a list of homogenous elements in which the addition and deletion of elements occurs only at one end, called the top of the stack. For
example, to get to your favorite computer science book, which is underneath your math and history books, you must first remove the math and
history books. After removing these books, the computer science book becomes the top book—that is, the top element of the stack. Figure 1
shows some examples of stacks.
It is a linear data structure that follows the Last-In-First-Out (LIFO) principle. This means that the last element added to the stack is the first
one to be removed. It is also sometimes referred to as First In Last Out (FILO) ordering, as the first element added to the stack is the last one
to be removed.
Peek: return the top element of the stack without removing it. (sometimes called top)
isEmpty: returns true if the stack is empty (i.e., has no elements) and false otherwise.
isFull: this operation returns true if the stack is full (i.e., has reached its maximum size) and
false otherwise.
Figure 1
2
Stack
There are two ways we can implement a stack:
Using an array
Using a linked list
Array-based implementation:
Stack is implemented using an array to store the elements. The stack uses a variable to keep track of the index of the top element in the array.
It is fixed size stack has a fixed size and cannot grow or shrink dynamically. If the stack is full and an attempt is made to add an element to it, an
overflow error occurs. If the stack is empty and an attempt is made to remove an element from it, an underflow error occurs.
Linked-based implementation:
Stack is implemented using a linked list to store the elements. The stack uses a pointer to keep track of the top element in the linked list.
It is a dynamic size stack can grow or shrink dynamically. When the stack is full, it automatically increases its size to accommodate the new
element, and when the stack is empty, it decreases its size. As it is implemented using a linked list, as it allows for easy resizing of the stack.
3
Stack
Push (ItemType newItem)
Function: Adds newItem to the top of the stack.
Preconditions: Stack has been initialized and is not full.
Postconditions: newItem is at the top of the stack.
4
Stack (implementation)
5
Stack (linked-based implementation)
6
Stack (linked-based implementation)
/* Node class for a singly linked list*/
template<class T>
class Node {
public:
T data; /* Data stored in the node*/
Node<T>* next; /* Pointer to the next node in the list*/
};
/* Stack class using a linked list*/
template<class T>
class Stack {
private:
Node<T>* top; /* Pointer to the top node of the stack*/
public:
Stack() ;
T peek() ;
bool isEmpty() ;
bool isFull();
void push(T item);
void pop(T & item);
void peek(T& item) ;
void display();
};
7
Stack (linked-based implementation)
/* Constructor to initialize an empty stack*/
template<class T>
Stack<T> :: Stack() {
top = NULL; /* Set the top pointer to NULL*/
}
/* Function to check if the stack is empty*/
template<class T>
bool Stack<T> :: isEmpty() {
return top == NULL; /* If the top pointer is NULL, the stack is empty*/
}
/* Function to check if the stack is full*/
template<class T>
bool Stack<T> :: isFull() {
return false; } /*stack is always not full*/
}
/* Function to push an element onto the stack*/
template<class T>
void Stack<T> :: push(T item) {
Node<T>* newNode = new Node<T>; /* Create a new node with the given data*/
newNode->data=item;
newNode->next = top; /* Set the new node's next pointer to the current top node*/
top = newNode; /* Set the top pointer to the new node*/
} 8
Stack (linked-based implementation)
/*Function to pop an element from the stack*/ /*Function to peek at the top element of the stack*/
template<class T> template<class T>
void Stack<T> ::pop(T& item) { void Stack<T> ::peek(T& item) {
/* Check if the stack is empty*/ // Check if the stack is empty
if (isEmpty()) { if (isEmpty()) {
cout << "Stack Underflow" << endl; cout << "Stack Underflow" << endl;
} }
else else
{ item= top->data;
/* Get the item of the top node and /* Return the item of the top node*/
the next node*/ }
item = top->data; /*Function to display the elements of the stack*/
Node<T>* temp = top->next; template<class T>
/* Delete the top node and set the top void Stack<T>::display() {
pointer to the next node*/ if (isEmpty()) {
delete top; cout << "Stack is empty" << endl; }
top = temp; else {
} Node<T>* current = top;
} while (current != NULL) {
cout << current->data << " ";
current = current->next;
}
9
cout << endl;} }
Stack (linked-based implementation)
#include <iostream> //Example of stack of char elements
using namespace std; Stack<char> st; // create a stack of integers
#include "stack.h" char item_ch;
// push elements onto the stack
int main() { st.push('A');
//Example of stack of int elements st.push('B');
Stack<int> s; // create a stack of integers st.push('C');
int item; // display the stack
// push elements onto the stack cout << "Stack elements are "; st.display();
s.push(100); // pop an element from the stack
s.push(200); st.pop(item_ch);
s.push(300); cout << "Popped item: " << item_ch << endl;
// display the stack // display the stack
cout << "Stack elements are "; cout << "Stack element after pop operation are ";
s.display(); st.display();
// pop an element from the stack // peek at the top element of the stack
s.pop(item); st.peek(item_ch);
cout << "Popped item: " << item << endl; cout << "Top item is " << item_ch << endl;
// display the stack system("pause");
cout << "Stack element after pop operation are "; return 0;
s.display(); }
// peek at the top element of the stack
s.peek(item);
cout << "Top item is " << item << endl;
10
Stack (array-based implementation)
11
Stack (array-based implementation)
#define maxSize 100 // Maximum number of elements the stack can hold
template<class T>
class Stack {
private:
int top; // Index of the top element in the stack
T arr[maxSize]; // Array that holds the stack elements
public:
Stack(); // Constructor
bool isEmpty(); // Function to check if the stack is empty
bool isFull(); // Function to check if the stack is full
void push(T item); // Function to push an element onto the stack
void pop(T& item); // Function to pop an element from the stack
void peek(T& item); // Function to peek at the top element of the stack
void display(); // Function to display the elements of the stack
};
12
Stack (array-based implementation)
/* Constructor */ /* Function to push an element onto the stack*/
template<class T> template<class T>
Stack<T>::Stack() { void Stack<T>::push(T item) {
top = -1; // Set the top index to -1 to indicate if (isFull()) {// Check if the stack is full
an empty stack cout << "Error: stack overflow" << endl;
} } else {
top++; // Increment the top index
/* Function to check if the stack is empty*/ arr[top] = item; // Add the new element
template<class T> to the top of the stack
bool Stack<T>::isEmpty() { }
return top == -1; // If the top index is -1, the }
stack is empty
} /* Function to pop an element from the stack*/
template<class T>
/* Function to check if the stack is full*/ void Stack<T>::pop(T& item) {
template<class T> if (isEmpty()) {// Check if the stack is empty
bool Stack<T>::isFull() { cout << "Error: stack underflow" << endl;
return top == maxSize - 1; // If the top index is } else {
equal to the maxSize - 1, the stack is full item = arr[top]; // Get the element at the
} top of the stack
top--; // Decrement the top index
}
13
}
Stack (array-based implementation)
/* Function to peek at the top element of the stack*/
template<class T>
void Stack<T>::peek(T& item) {
if (isEmpty()) { // Check if the stack is empty
cout << "Error: stack underflow" << endl;
} else {
item = arr[top]; // Get the element at the top of the stack
}
}
16
Stack (array-based implementation) (dynamic array)
template<class T>
class Stack {
private:
int top; // Index of the top element in the stack
int maxSize; // Maximum number of elements the stack can hold
T* arr; // Pointer to the dynamic array that holds the stack elements
public:
Stack(int maxSize); // Constructor to initialize the stack with the given maxSize
~Stack(); // Destructor to free the memory allocated for the dynamic array
bool isEmpty(); // Function to check if the stack is empty
bool isFull(); // Function to check if the stack is full
void push(T item); // Function to push an element onto the stack
void pop(T& item); // Function to pop an element from the stack
void peek(T& item); // Function to peek at the top element of the stack
void display(); // Function to display the elements of the stack
};
17
Stack (array-based implementation) (dynamic array)
/* Function to check if the stack is full*/
/* Constructor to initialize the stack with the
template<class T>
given maxSize*/
bool Stack<T>::isFull() {
template<class T>
return top == maxSize - 1; // If the top index
Stack<T>::Stack(int maxSize) {
is equal to the maxSize - 1, the stack is full
top = -1; // Set the top index
}
to -1 to indicate an empty stack
arr = new T[maxSize]; // Allocate memory
/* Function to push an element onto the stack*/
for the dynamic array
template<class T>
}
void Stack<T>::push(T item) {
/* Destructor to free the memory allocated for the
if (isFull()) { // Check if the
dynamic array*/
stack is full
template<class T>
cout << "Stack Overflow" << endl;
Stack<T>::~Stack() {
} else {
delete[] arr;
top++; // Increment the
}
top index
/* Function to check if the stack is empty*/
arr[top] = item; // Add the new
template<class T>
element to the top of the stack
bool Stack<T>::isEmpty() {
}
return top == -1; // If the top index is -1,
}
the stack is empty
}
18
Stack (array-based implementation) (dynamic array)
/* Function to pop an element from the stack*/ /* Function to display the elements of the stack*/
template<class T> template<class T>
void Stack<T>::pop(T& item) { void Stack<T>::display() {
if (isEmpty()) {// Check if the stack is empty if (isEmpty()) { // Check if the
cout << "Stack Underflow" << endl; stack is empty
} else { cout << "Stack is empty" << endl;
item = arr[top]; // Get the element at the top } else {
of the stack for (int i = top; i >= 0; i--) { //
top--; // Decrement the top index Iterate over the elements of the stack from the
} top to the bottom
} cout << arr[i] << " ";
/* Function to peek at the top element of the stack*/ }
template<class T> cout << endl;
void Stack<T>::peek(T& item) { }
if (isEmpty()) {// Check if the stack is empty }
cout << "Stack Underflow" << endl;
} else {
item = arr[top]; // Get the element at the top
of the stack
}
}
19
Stack (array-based implementation) (dynamic array)
#include <iostream>
using namespace std;
#include "stack.h"
int main() {
Stack<int> s(50); // create a stack of integers
int item;
// push elements onto the stack
s.push(100);
s.push(200);
s.push(300);
cout << "Stack elements are "; s.display(); // display the stack
// pop an element from the stack
s.pop(item);
cout << "Popped item: " << item << endl;
// display the stack
cout << "Stack element after pop operation are "; s.display();
// peek at the top element of the stack
s.peek(item);
cout << "Top item is " << item << endl;
// check if the stack is empty
if (s.isEmpty()) cout << "Stack is empty" << endl;
else cout << "Stack is not empty" << endl;
return 0;
20
}
Stack application
21
Stack application
Undo/redo functionality.
Browser history.
Processing function calls
Reverse a data
Delimiter checking
Evaluation of arithmetic expressions
Tower of hanoi game
Recursion
Browser history: The back and forward buttons in web browsers can be implemented using stacks. Each time a user visits a new web page, it is
added to the stack, and when the user clicks the back button, the most recently visited page is popped off the stack.
Undo/redo functionality: Many software applications allow users to undo or redo their actions. Stacks can be used to store the state of the
application at each step, so that the user can undo or redo their actions by popping items off the stack.
Recursion: Recursive algorithms often use stacks to keep track of the function calls. Each time a function is called, its parameters and return
address are pushed onto the stack, and when the function returns, they are popped off the stack.
22
Stack application
Processing function calls
Stack plays an important role in programs that call several functions in succession. Suppose we have a program containing three functions: A, B,
and C. function A invokes function B, which invokes the function C.
When we invoke function A, which contains a call to function B, then its processing will not be completed until function B has completed its
execution and returned. Similarly for function B and C. So we observe that function A will only be completed after function B is completed and
function B will only be completed after function C is completed. Therefore, function A is first to be started and last to be completed. To conclude,
the above function activity matches the last in first out behavior and can easily be handled using Stack.
Consider addrA, addrB, addrC be the addresses of the statements to which control is returned after completing the function A, B, and C,
respectively.
23
Stack application
Reverse data
To reverse a given set of data, we need to reorder the data so that the first and last elements are exchanged, the second and second last element are
exchanged, and so on for all other elements.
Example: Suppose we have a string Welcome, then on reversing it would be Emoclew.
There are different reversing applications:
Reversing a string
Converting Decimal to Binary
Reverse string
A Stack can be used to reverse the characters of a string. This can be
achieved by simply pushing one by one each character onto the
Stack, which later can be popped from the Stack one by one. Because
of the last in first out property of the Stack, the first character of the
Stack is on the bottom of the Stack and the last character of the
String is on the Top of the Stack and after performing the pop
operation in the Stack, the Stack returns the String in Reverse order.
24
Stack application (reverse string)
#include <iostream>
#include <string>
#include "stack.h"
using namespace std;
void reverseString(string& str) {
Stack<char> s; char c;
// Push each character of the string onto the stack
for (int i = 0; i < str.length(); i++) {
s.push(str[i]); }
// Pop each character from the stack and overwrite the string in reverse order
for (int i = 0; i < str.length(); i++) {
s.pop(c);
str[i] = c;
} }
int main() {
string str;
cout << "Enter a string: ";
getline(cin, str);
reverseString(str);
cout << "Reversed string: " << str << endl;
return 0;
}
25
Stack application
Converting decimal to binary:
Although decimal numbers are used in most business applications, some scientific and technical applications require numbers in either binary,
octal, or hexadecimal. A stack can be used to convert a number from decimal to binary/octal/hexadecimal form. For converting any decimal
number to a binary number, we repeatedly divide the decimal number by two and push the remainder of each division onto the Stack until the
number is reduced to 0. Then we pop the whole Stack and the result obtained is the binary equivalent of the given decimal number.
26
Stack application
Delimiter checking
The common application of Stack is delimiter checking, i.e., parsing that involves analyzing a source program syntactically. It is also called
parenthesis checking. When the compiler translates a source program written in some programming language such as C, C++ to a machine
language, it parses the program into multiple individual parts such as variable names, keywords, etc. By scanning from left to right. The main
problem encountered while translating is the unmatched delimiters. We make use of different types of delimiters include the parenthesis checking
(,), curly braces {,} and square brackets [,], and common delimiters /* and */. Every opening delimiter must match a closing delimiter, i.e., every
opening parenthesis should be followed by a matching closing parenthesis. Also, the delimiter can be nested. The opening delimiter that occurs
later in the source program should be closed before those occurring earlier.
To perform a delimiter checking, the compiler makes use of a stack. When a compiler translates a source program, it reads
the characters one at a time, and if it finds an opening delimiter it places it on a stack. When a closing delimiter is found, it
pops up the opening delimiter from the top of the Stack and matches it with the closing delimiter.
27
Stack application
Delimiter checking
28
Stack application (delimiter checking)
#include <iostream>
#include <string>
#include "stack.h"
using namespace std;
// Checks if the delimiters in the string are balanced using a stack
bool checkDelimiters(string str)
{
Stack<char> s; /* Create a stack of characters*/
int len = str.length(); // Get the length of the string*/
/* Loop through each character in the string*/
for (int i = 0; i < len; i++) {
/* If the character is an opening delimiter, push it onto the stack*/
if (str[i] == '(' || str[i] == '[' || str[i] == '{') {
s.push(str[i]);
}
/* If the character is a closing delimiter, pop the top element from the stack
/ and compare it to the current character to see if they match*/
else if (str[i] == ')' || str[i] == ']' || str[i] == '}')
{
/* If the stack is empty, then there is no matching opening delimiter for the closing
delimiter*/
if (s.isEmpty())
return false;
29
Stack application (delimiter checking)
/* Get the top element of the stack without removing it*/
char top;
s.peek(top);
/* Compare the top element of the stack to the current character to see if they match.
If they do match, then pop the top element from the stack
If they don't match, then the delimiters are not balanced*/
if ((str[i] == ')' && top == '(') ||
(str[i] == ']' && top == '[') ||
(str[i] == '}' && top == '{'))
s.pop(top);
else
return false;
}
}
/* If the stack is empty at the end of the loop, then all the delimiters are balanced
/ If the stack is not empty, then there are some opening delimiters that don't have matching
closing delimiters*/
return s.isEmpty();
}
30
Stack application (delimiter checking)
int main() {
char str[100]; // declare a character array to hold the input string
cout << "Enter a string: ";
cin.getline(str, 100); // read the input string from the user
// Check if the delimiters in the string are balanced
if (checkDelimiters(str))
cout << "Delimiters are balanced." << endl;
else
cout << "Delimiters are not balanced." << endl;
return 0;
}
31
Stack application
Evaluation of Arithmetic Expressions
A stack is a very effective data structure for evaluating arithmetic expressions in programming languages. An arithmetic
expression consists of operands and operators.
In addition to operands and operators, the arithmetic expression may also include parenthesis like "left parenthesis" and
"right parenthesis".
Example: A + (B - C)
To evaluate the expressions, one needs to be aware of the standard precedence rules for arithmetic expression. The
precedence rules for the five basic arithmetic operators are:
32
Stack application
Evaluation of Arithmetic Expressions
Notations for Arithmetic Expression
There are three notations to represent an arithmetic expression:
•Infix Notation
•Prefix Notation
•Postfix Notation
Infix Notation
The infix notation is a convenient way of writing an expression in which each operator is placed between the operands. Infix
expressions can be parenthesized or unparenthesized depending upon the problem requirement.
Example: A + B, (C - D) etc.
All these expressions are in infix notation because the operator comes between the operands.
Prefix Notation
The prefix notation places the operator before the operands. Example: + A B, -CD etc.
All these expressions are in prefix notation because the operator comes before the operands.
Postfix Notation
The postfix notation places the operator after the operands. Example: AB +, CD+, etc.
All these expressions are in postfix notation because the operator comes after the operands.
33
Stack application
Evaluation of Arithmetic Expressions
34
Stack application
Evaluation of postfix expression
Stack is the ideal data structure to evaluate the postfix expression because
the top element is always the most recent operand. The next element on
the Stack is the second most recent operand to be operated on.
Before evaluating the postfix expression, the following conditions must
be checked. If any one of the conditions fails, the postfix expression is
invalid.
When an operator encounters the scanning process, the Stack must
contain a pair of operands or intermediate results previously calculated.
When an expression has been completely evaluated, the Stack must
contain exactly one value.
Example:
Now let us consider the following infix expression 2 * (4+3) - 5.
Its equivalent postfix expression is 2 4 3 + * 5.
The following step illustrates how this postfix expression is evaluated.
35
Stack application
Evaluation of postfix expression
Scan the given postfix expression from left to right character by character.
If the character is an operand, push it into the stack.
But if the character is an operator, pop the top two values from stack.
Concatenate this operator with these two values (2nd top value+operator+1st top value) to get a new string.
Now push this resulting string back into the stack.
Repeat this process until the end of postfix expression. Now the value in the stack is the infix expression.
Example: 43*82-+
pop(3) as op2
Empty Then Calculate pop(2) as op2 Calculate pop(6) as op2 Calculate Pop(20)
stack push(4) push(3) (op2*op1) Then (op2-op1) This is the
pop(4) as op1 Then (op2-op1)
push(3*4) push(8) push(2) pop(8) as op1 push(8-2) pop(14) as op1 push(6+14) result
2 2
3 3 8 8 8 6 6
4 4 4 12 12 12 12 12 12 18 18 36
Stack application (postfix evaluation )
#include <iostream>
#include <string>
#include "stack.h"
37
Stack application (postfix evaluation )
/* If the current character is an operator, pop two operands from the stack, apply the operator, and push
the result onto the stack */
else
{
stack.pop(operand2);
stack.pop(operand1);
switch(ch) {
case '+': stack.push(operand1 + operand2); break;
case '-': stack.push(operand1 - operand2); break;
case '*': stack.push(operand1 * operand2); break;
case '/': stack.push(operand1 / operand2); break;
default: cout << "Invalid operator" << endl;
}} }
/* The final result is the only item remaining on the stack */
int result;
stack.pop(result);
if (!stack.isEmpty()) {
cout << "Invalid expression" << endl;
return 0;
}
return result;
}
38
Stack application (postfix evaluation )
int main() {
// string expression = "234*+5-";
string expression;
// Get the postfix expression from the user
cout << "Enter the postfix expression: ";
cin >> expression; /* Read the postfix expression from the user */
39