Lab Manual (Stack)
Lab Manual (Stack)
Stack is a linear data structure which follows LIFO principle. In this article, we
will learn how to implement Stack using Arrays. In Array-based approach, all stack-
related operations are executed using arrays. Let’s see how we can implement each
operation on the stack utilizing the Array Data Structure.
Implement Stack using Array:
To implement a stack using an array, initialize an array and treat its end as the
stack‟s top. Implement push (add to end), pop (remove from end), and peek (check
end) operations, handling cases for an empty or full stack.
Recommended Problem
Step-by-step approach:
1. Initialize an array to represent the stack.
2. Use the end of the array to represent the top of the stack.
3. Implement push (add to end), pop (remove from the end), and peek (check end)
operations, ensuring to handle empty and full stack conditions.
Implement Stack Operations using Array:
Here are the following operations of implement stack using array:
Push Operation in Stack:
Adds an item to the stack. If the stack is full, then it is said to be an Overflow
condition.
Algorithm for Push Operation:
Before pushing the element to the stack, we check if the stack is full .
If the stack is full (top == capacity-1) , then Stack Overflows and we cannot insert
the element to the stack.
Otherwise, we increment the value of top by 1 (top = top + 1) and the new value is
inserted at top position .
The elements can be pushed into the stack till we reach the capacity of the stack.
1/6
class Stack {
int top;
public:
int a[MAX]; // Maximum size of Stack
bool Stack::push(int x)
{
if (top >= (MAX - 1)) {
cout << "Stack Overflow";
return false;
}
else {
a[++top] = x;
cout << x << " pushed into stack\n";
return true;
}
}
int Stack::pop()
{
if (top < 0) {
cout << "Stack Underflow";
return 0;
}
else {
int x = a[top--];
return x;
}
}
int Stack::peek()
{
if (top < 0) {
cout << "Stack is Empty";
return 0;
}
else {
int x = a[top];
return x;
}
}
bool Stack::isEmpty()
{
return (top < 0);
}
return 0;
}
Output
10 pushed into stack
20 pushed into stack
30 pushed into stack
30 Popped from stack
Top element is : 20
Elements present in stack : 20 10
Complexity Analysis:
Time Complexity:
o push: O(1)
o pop: O(1)
o peek: O(1)
o is_empty: O(1)
o is_full: O(1)
Auxiliary Space: O(n), where n is the number of items in the stack.
public:
// Constructor to initialize the stack
Stack() { this->head = nullptr; }
return 0;
}
Output
Top element is 44
Top element is 22
Time Complexity: O(1), for all push(), pop(), and peek(), as we are not performing
any kind of traversal over the list. We perform all the operations through the current
pointer only.
Auxiliary Space: O(N), where N is the size of the stack
In this implementation, we define a Node class that represents a node in the linked
list, and a Stack class that uses this node class to implement the stack. The head
attribute of the Stack class points to the top of the stack (i.e., the first node in the
linked list).
To push an item onto the stack, we create a new node with the given item and set its
next pointer to the current head of the stack. We then set the head of the stack to the
new node, effectively making it the new top of the stack.
To pop an item from the stack, we simply remove the first node from the linked list by
setting the head of the stack to the next node in the list (i.e., the node pointed to by the
next pointer of the current head). We return the data stored in the original head node,
which is the item that was removed from the top of the stack.
Benefits of implementing a stack using a singly linked list include:
Dynamic memory allocation: The size of the stack can be increased or decreased
dynamically by adding or removing nodes from the linked list, without the need to
allocate a fixed amount of memory for the stack upfront.
Efficient memory usage: Since nodes in a singly linked list only have a next pointer
and not a prev pointer, they use less memory than nodes in a doubly linked list.
Easy implementation: Implementing a stack using a singly linked list is
straightforward and can be done using just a few lines of code.
Versatile: Singly linked lists can be used to implement other data structures such as
queues, linked lists, and trees.
In summary, implementing a stack using a singly linked list is a simple and efficient
way to create a dynamic stack data structure in Python.
Real time examples of stack:
Stacks are used in various real-world scenarios where a last-in, first-out (LIFO) data
structure is required. Here are some examples of real-time applications of stacks:
Function call stack: When a function is called in a program, the return address and
all the function parameters are pushed onto the function call stack. The stack allows
the function to execute and return to the caller function in the reverse order in which
they were called.
Undo/Redo operations: In many applications, such as text editors, image editors, or
web browsers, the undo and redo functionalities are implemented using a stack. Every
time an action is performed, it is pushed onto the stack. When the user wants to undo
the last action, the top element of the stack is popped and the action is reversed.
Browser history: Web browsers use stacks to keep track of the pages visited by the
user. Every time a new page is visited, its URL is pushed onto the stack. When the
user clicks the “Back” button, the last visited URL is popped from the stack and the
user is directed to the previous page.
Expression evaluation: Stacks are used in compilers and interpreters to evaluate
expressions. When an expression is parsed, it is converted into postfix notation and
pushed onto a stack. The postfix expression is then evaluated using the stack.
Call stack in recursion: When a recursive function is called, its call is pushed onto
the stack. The function executes and calls itself, and each subsequent call is pushed
onto the stack. When the recursion ends, the stack is popped, and the program returns
to the previous function call.
In summary, stacks are widely used in many applications where LIFO functionality is
required, such as function calls, undo/redo operations, browser history, expression
evaluation, and recursive function calls.
Balanced Parenthesis
Given an expression string exp, write a program to examine whether the pairs and
the orders of “{“, “}”, “(“, “)”, “[“, “]” are correct in the given expression.
Example:
Input: exp = “[ ( ) ] { } { [ ( ) ( ) ] ( ) }”
Output: Balanced
Explanation: all the brackets are well-formed
Input: exp = “[ ( ] )”
Output: Not Balanced
Explanation: 1 and 4 brackets are not balanced because
there is a closing „]‟ before the closing „(„
Illustration:
Below is the illustration of the above approach.
Follow the steps mentioned below to implement the idea:
C++Java
// C++ program to check for balanced brackets.
#include <bits/stdc++.h>
using namespace std;
// Driver code
int main()
{
string expr = "{()}[]";
// Function call
if (areBracketsBalanced(expr))
cout << "Balanced";
else
cout << "Not Balanced";
return 0;
}
Output
Balanced
Time Complexity: O(N), Iteration over the string of size N one time.
Auxiliary Space: O(N) for stack.
Evaluation of Postfix Expression
Given a postfix expression, the task is to evaluate the postfix expression.
Postfix expression: The expression of the form “a b operator” (ab+) i.e.,
when a pair of operands is followed by an operator.
Examples:
Input: str = “2 3 1 * + 9 -“
Output: -4
Explanation: If the expression is converted into an infix expression, it will be
2 + (3 * 1) – 9 = 5 – 9 = -4.
Input: str = “100 200 + 2 / 5 * 7 +”
Output: 757
Scan *, it‟s an operator. Pop two operands from stack, apply the *
operator on operands. We get 3*1 which results in 3. We push the result 3
to stack. The stack now becomes „2 3‟.
Evaluate * operator and push result in stack
Scan +, it‟s an operator. Pop two operands from stack, apply the +
operator on operands. We get 3 + 2 which results in 5. We push the result
5 to stack. The stack now becomes „5‟.
Evaluate + operator and push result in stack
Scan 9, it‟s a number. So we push it to the stack. The stack now becomes
„5 9‟.
Push 9 into stack
Scan -, it‟s an operator, pop two operands from stack, apply the –
operator on operands, we get 5 – 9 which results in -4. We push the result
-4 to the stack. The stack now becomes „-4‟.
Evaluate „-„ operator and push result in stack
There are no more elements to scan, we return the top element from the
stack (which is the only element left in a stack).
So the result becomes -4.
Follow the steps mentioned below to evaluate postfix expression using stack:
Create a stack to store operands (or values).
Scan the given expression from left to right and do the following for every
scanned element.
o If the element is a number, push it into the stack.
o If the element is an operator, pop operands for the operator from
the stack. Evaluate the operator and push the result back to the
stack.
When the expression is ended, the number in the stack is the final
answer.
Below is the implementation of the above approach:
C
// C++ program to evaluate value of a postfix expression
#include <bits/stdc++.h>
using namespace std;
// Driver code
int main()
{
string exp = "231*+9-";
// Function call
cout << "postfix evaluation: " << evaluatePostfix(exp);
return 0;
}
Output
postfix evaluation: -4
Time Complexity: O(N)
Auxiliary Space: O(N)
There are the following limitations of the above implementation.
It supports only 4 binary operators ‘+’, ‘*’, ‘-‘ and ‘/’. It can be extended
for more operators by adding more switch cases.
The allowed operands are only single-digit operands.
Postfix evaluation for multi-digit numbers:
The above program can be extended for multiple digits by adding a
separator-like space between all elements (operators and operands) of the
given expression.
Below given is the extended program which allows operands to have multiple
digits.
// Stack type
struct Stack {
int top;
unsigned capacity;
int* array;
};
// Stack Operations
struct Stack* createStack(unsigned capacity)
{
struct Stack* stack
= (struct Stack*)malloc(sizeof(struct Stack));
if (!stack)
return NULL;
stack->top = -1;
stack->capacity = capacity;
stack->array
= (int*)malloc(stack->capacity * sizeof(int));
if (!stack->array)
return NULL;
return stack;
}
switch (exp[i]) {
case '+':
push(stack, val2 + val1);
break;
case '-':
push(stack, val2 - val1);
break;
case '*':
push(stack, val2 * val1);
break;
case '/':
push(stack, val2 / val1);
break;
}
}
}
return pop(stack);
}
// Function call
printf("%d", evaluatePostfix(exp));
return 0;
}
Output
757
Time Complexity: O(N)
Auxiliary Space: O(N)
To implement push1():
o First, check whether the top1 is greater than 0
If it is then add an element at the top1 index and decrement top1 by 1
Else return Stack Overflow
To implement push2():
o First, check whether top2 is less than n - 1
If it is then add an element at the top2 index and increment the top2 by 1
Else return Stack Overflow
To implement pop1():
o First, check whether the top1 is less than or equal to n / 2
If it is then increment the top1 by 1 and return that element.
Else return Stack Underflow
To implement pop2():
o First, check whether the top2 is greater than or equal to (n + 1) / 2
If it is then decrement the top2 by 1 and return that element.
Else return Stack Underflow
Below is the implementation of the above approach.
C++Java
#include <bits/stdc++.h>
using namespace std;
class twoStacks {
int* arr;
int size;
int top1, top2;
public:
// Constructor
twoStacks(int n)
{
size = n;
arr = new int[n];
top1 = n / 2 + 1;
top2 = n / 2;
}
Output
C++Java
#include <iostream>
#include <stdlib.h>
class twoStacks {
int* arr;
int size;
int top1, top2;
public:
twoStacks(int n) // constructor
{
size = n;
arr = new int[n];
top1 = -1;
top2 = size;
}
Output
Given an array, print the Next Greater Element (NGE) for every element.
The Next greater Element for an element x is the first greater element on the right side
of x in the array. Elements for which no greater element exist, consider the next greater
element as -1.
Example:
Input: arr[] = [ 4 , 5 , 2 , 25 ]
Output: 4 --> 5
5 --> 25
2 --> 25
25 --> -1
Explanation: except 25 every element has an element greater than them present on the
right side
Input: arr[] = [ 13 , 7, 6 , 12 ]
Output: 13 --> -1
7 --> 12
6 --> 12
12 --> -1
Explanation: 13 and 12 don't have any element greater than them present on the right
side
C++Java
// Simple C++ program to print
// next greater elements in a
// given array
#include <iostream>
using namespace std;
// Driver Code
int main()
{
int arr[] = { 11, 13, 21, 3 };
int n = sizeof(arr) / sizeof(arr[0]);
printNGE(arr, n);
return 0;
}
illustration:
Below is the illustration of the above approach:
Follow the steps mentioned below to implement the idea:
C++Java
// A Stack based C++ program to find next
// greater element for all array elements.
#include <bits/stdc++.h>
using namespace std;
if (s.empty()) {
s.push(arr[i]);
continue;
}
/* Driver code */
int main()
{
int arr[] = { 11, 13, 21, 3 };
int n = sizeof(arr) / sizeof(arr[0]);
printNGE(arr, n);
return 0;
}