Unit - 4
Unit - 4
Stack
4.1 Introduction to Stack: Definition, Stack as an ADT, Operations on Stack-(Push, Pop),
Stack Operations Conditions - Stack Full / Stack Overflow, Stack Empty /Stack Underflow.
4.2 Stack Implementation: using Array and Representation Using Linked List.
Applications of Stack: Reversing a List, Polish Notations, Conversion of Infix to Postfix
Expression, Evaluation of Postfix Expression.
4.4 Recursion: Definition and Applications.
What is a Stack?
A Stack is a linear data structure that holds a linear, ordered sequence of elements. It is an
abstract data type. A Stack works on the LIFO process (Last In First Out), i.e., the element that
was inserted last will be removed first. To implement the Stack, it is required to maintain a
pointer to the top of the Stack, which is the last element to be inserted because we can access the
elements only on the top of the Stack.
The abstract datatype is special kind of datatype, whose behavior is defined by a set of values
and set of operations. The keyword “Abstract” is used as we can use these datatypes, we can
perform different operations. But how those operations are working that is totally hidden from
the user. The ADT is made of with primitive datatypes, but operation logics are hidden.
Here we will see the stack ADT. These are few operations or functions of the Stack ADT.
Example:
Working of Stack:
Stack works on the LIFO pattern. As we can observe in the below figure there are five memory
blocks in the stack; therefore, the size of the stack is 5.
Suppose we want to store the elements in a stack and let's assume that stack is empty. We have
taken the stack of size 5 as shown below in which we are pushing the elements one by one until
the stack becomes full.
Since our stack is full as the size of the stack is 5. In the above cases, we can observe that it goes
from the top to the bottom when we were entering the new element in the stack. The stack gets
filled up from the bottom to the top.
When we perform the delete operation on the stack, there is only one way for entry and exit as
the other end is closed. It follows the LIFO pattern, which means that the value entered first will
be removed last. In the above case, the value 5 is entered first, so it will be removed only after
the deletion of all the other elements.
○ push(): When we insert an element in a stack then the operation is known as a push. If
the stack is full then the overflow condition occurs.
○ pop(): When we delete an element from the stack, the operation is known as a pop. If the
stack is empty means that no element exists in the stack, this state is known as an
underflow state.
○ isEmpty(): It determines whether the stack is empty or not.
○ isFull(): It determines whether the stack is full or not.'
○ peek(): It returns the element at the given position.
○ count(): It returns the total number of elements available in a stack.
○ change(): It changes the element at the given position.
○ display(): It prints all the elements available in the stack.
i) Push() Operation in Stack Data Structure: Adds an item to the stack. If the stack is full,
then it is said to be an Overflow condition.
● 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.
Removes an item from the stack. The items are popped in the reversed order in which they are
pushed. If the stack is empty, then it is said to be an Underflow condition.
● Before popping the element from the stack, we check if the stack is empty .
● If the stack is empty (top == -1), then Stack Underflows and we cannot remove any
element from the stack.
● Otherwise, we store the value at top, decrement the value of top by 1 (top = top – 1)
and return the stored top value.
iii) Top() or Peek() Operation in Stack Data Structure: Returns the top element of the stack.
v) isFull() Operation in Stack Data Structure: Returns true if the stack is full, else false.
1. A Stack can be used for evaluating expressions consisting of operands and operators.
2. Stacks can be used for Backtracking, i.e., to check parenthesis matching in an expression.
3. It can also be used to convert one form of expression to another form.
4. It can be used for systematic Memory Management.
Advantages of Stack:
1. A Stack helps to manage the data in the ‘Last in First out’ method.
2. When the variable is not used outside the function in any program, the Stack can be used.
3. It allows you to control and handle memory allocation and deallocation.
4. It helps to automatically clean up the objects.
Disadvantages of Stack:
1. It is difficult in Stack to create many objects as it increases the risk of the Stack overflow.
2. It has very limited memory.
3. In Stack, random access is not possible.
In array implementation, the stack is formed by using the array. All the operations regarding the
stack are performed using arrays. Lets see how each operation can be implemented on the stack
using array data structure.
Adding an element onto the stack (push operation):
Adding an element into the top of the stack is referred to as push operation. Push operation
involves following two steps.
1. Increment the variable Top so that it can now refere to the next memory location.
2. Add element at the position of incremented top. This is referred to as adding new element
at the top of the stack.
Stack is overflown when we try to insert an element into a completely filled stack therefore, our
main function must always avoid stack overflow condition.
Algorithm:
begin
if top = n then stack full
top = top + 1
stack (top) : = item;
end
Time Complexity : O(1)
The underflow condition occurs when we try to delete an element from an already empty stack.
Algorithm :
begin
if top = 0 then stack empty;
item := stack(top);
top = top - 1;
end;
Time Complexity : o(1)
Algorithm :
Begin
if top = -1 then stack empty
item = stack[top]
return item
End
Time complexity: o(n)
#include <stdio.h>
int stack[100],i,j,choice=0,n,top=-1;
void push();
void pop();
void show();
void main ()
{
printf("Enter the number of elements in the stack ");
scanf("%d",&n);
printf("*********Stack operations using array*********");
printf("\n----------------------------------------------\n");
while(choice != 4)
{
printf("Chose one from the below options...\n");
printf("\n1.Push\n2.Pop\n3.Show\n4.Exit");
printf("\n Enter your choice \n");
scanf("%d",&choice);
switch(choice)
{
case 1:
{
push();
break;
}
case 2:
{
pop();
break;
}
case 3:
{
show();
break;
}
case 4:
{
printf("Exiting....");
break;
}
default:
{
printf("Please Enter valid choice ");
}
};
}
}
void push ()
{
int val;
if (top == n )
printf("\n Overflow");
else
{
printf("Enter the value?");
scanf("%d",&val);
top = top +1;
stack[top] = val;
}
}
void pop ()
{
if(top == -1)
printf("Underflow");
else
top = top -1;
}
void show()
{
for (i=top;i>=0;i--)
{
printf("%d\n",stack[i]);
}
if(top == -1)
{
printf("Stack is empty");
}
}
Instead of using array, we can also use linked list to implement stack. Linked list allocates the
memory dynamically. However, time complexity in both the scenario is same for all the
operations i.e. push, pop and peek.
In linked list implementation of stack, the nodes are maintained non-contiguously in the memory.
Each node contains a pointer to its immediate successor node in the stack. Stack is said to be
overflown if the space left in the memory heap is not enough to create a node.
The top most node in the stack always contains null in its address field. Lets discuss the way in
which, each operation is performed in linked list implementation of stack.
void push ()
{
int val;
struct node *ptr =(struct node*)malloc(sizeof(struct node));
if(ptr == NULL)
{
printf("not able to push the element");
}
else
{
printf("Enter the value");
scanf("%d",&val);
if(head==NULL)
{
ptr->val = val;
ptr -> next = NULL;
head=ptr;
}
else
{
ptr->val = val;
ptr->next = head;
head=ptr;
}
printf("Item pushed");
}
}
Check for the underflow condition: The underflow condition occurs when we try
to pop from an already empty stack. The stack will be empty if the head pointer of
the list points to null.
Adjust the head pointer accordingly: In stack, the elements are popped only from
one end, therefore, the value stored in the head pointer must be deleted and the
node must be freed. The next node of the head node now becomes the head node.
C implementation:
void pop()
{
int item;
struct node *ptr;
if (head == NULL)
{
printf("Underflow");
}
else
{
item = head->val;
ptr = head;
head = head->next;
free(ptr);
printf("Item popped");
}
}
void display()
{
int i;
struct node *ptr;
ptr=head;
if(ptr == NULL)
{
printf("Stack is empty\n");
}
else
{
printf("Printing Stack elements \n");
while(ptr!=NULL)
{
printf("%d\n",ptr->val);
ptr = ptr->next;
}
}
}
Menu Driven program in C implementing all the stack operations using linked list :
#include <stdio.h>
#include <stdlib.h>
void push();
void pop();
void display();
struct node
{
int val;
struct node *next;
};
struct node *head;
void main ()
{
int choice=0;
printf("\n*********Stack operations using linked list*********\n");
printf("\n----------------------------------------------\n");
while(choice != 4)
{
printf("\n\nChose one from the below options...\n");
printf("\n1.Push\n2.Pop\n3.Show\n4.Exit");
printf("\n Enter your choice \n");
scanf("%d",&choice);
switch(choice)
{
case 1:
{
push();
break;
}
case 2:
{
pop();
break;
}
case 3:
{
display();
break;
}
case 4:
{
printf("Exiting....");
break;
}
default:
{
printf("Please Enter valid choice ");
}
};
}
}
void push ()
{
int val;
struct node *ptr = (struct node*)malloc(sizeof(struct node));
if(ptr == NULL)
{
printf("not able to push the element");
}
else
{
printf("Enter the value");
scanf("%d",&val);
if(head==NULL)
{
ptr->val = val;
ptr -> next = NULL;
head=ptr;
}
else
{
ptr->val = val;
ptr->next = head;
head=ptr;
}
printf("Item pushed");
}
}
void pop()
{
int item;
struct node *ptr;
if (head == NULL)
{
printf("Underflow");
}
else
{
item = head->val;
ptr = head;
head = head->next;
free(ptr);
printf("Item popped");
}
}
void display()
{
int i;
struct node *ptr;
ptr=head;
if(ptr == NULL)
{
printf("Stack is empty\n");
}
else
{
printf("Printing Stack elements \n");
while(ptr!=NULL)
{
printf("%d\n",ptr->val);
ptr = ptr->next;
}
}
}
Recursion
What is Recursion?
Recursion is defined as a process that calls itself directly or indirectly and the corresponding
function is called a recursive function.
Properties of Recursion:
Recursion has some important properties. Some of which are mentioned below:
Types of Recursion:
1. Direct recursion: When a function is called within itself directly it is called direct
recursion. This can be further categorised into four types:
● Tail recursion,
● Head recursion,
● Tree recursion and
● Nested recursion.
2. Indirect recursion: Indirect recursion occurs when a function calls another function
that eventually calls the original function and it forms a cycle.
Applications of Recursion:
Recursion is used in many fields of computer science and mathematics, which includes:
● Searching and sorting algorithms: Recursive algorithms are used to search and sort
data structures like trees and graphs.
● Mathematical calculations: Recursive algorithms are used to solve problems such as
factorial, Fibonacci sequence, etc.
● Compiler design: Recursion is used in the design of compilers to parse and analyze
programming languages.
● Graphics: many computer graphics algorithms, such as fractals and the Mandelbrot
set, use recursion to generate complex patterns.
● Artificial intelligence: recursive neural networks are used in natural language
processing, computer vision, and other AI applications.
Advantages of Recursion:
● Recursion can simplify complex problems by breaking them down into smaller, more
manageable pieces.
● Recursive code can be more readable and easier to understand than iterative code.
● Recursion is essential for some algorithms and data structures.
● Also with recursion, we can reduce the length of code and become more readable and
understandable to the user/ programmer.
Disadvantages of Recursion:
● Recursion can be less efficient than iterative solutions in terms of memory and
performance.
● Recursive functions can be more challenging to debug and understand than iterative
solutions.
● Recursion can lead to stack overflow errors if the recursion depth is too high.