DS Unit 2 Material
DS Unit 2 Material
Primary Operations:
• Push - Inserting/Adding a new element at the top of the stack is referred
as Push operation in stack.
• Pop – Removing/ Deleting an element at the top of the stack is referred as Pop
operation in stack.
• Peek- get the top data element of the stack, without removing it.
Stack implementation using Array
Push Operation:
Steps:
1. Check if the stack is full or not.(Overflow condition)
2. If the stack is full, then print error of overflow and exit the program.
3. If the stack is not full, then increment the top and add the element.
void push(int element)
{
// Check stack overflow
if (top >= SIZE-1)
{
printf("Stack Overflow, can't add element to stack");
return;
}
top++;
// Push element in stack
stack[top] = element;
printf("Data pushed to stack");
}
POP operation
Deleting the Top element (last inserted) from the stack.
1. Check if the stack is empty or not. (Under flow condition)
2. If the stack is empty, then print error of underflow and exit the program.
3. If the stack is not empty, then decrement the top.
Applications of Stack
• Expression Conversion
• Expression Evaluation
• Parsing well formed parenthesis
• Decimal to binary conversion
• Reversing a string
• Storing function calls
Arithmetic expression
Expression:
An expression is a collection of operators and operands that represents a specific
value.
Operator is a symbol which performs a particular task like arithmetic operation or
logical operation or conditional operation etc., eg., + - / % * ^
Operands are the values on which the operators can perform the task. eg., a + b (a
and b are operands)
Expression Types
Based on the operator position, expressions are divided into THREE types. They are
as follows...
Infix Expression (A + B)
Postfix Expression (AB+)
Prefix Expression (+AB)
Infix Expression
In infix expression, operator is used in between the operands.
The general structure of an Infix expression is as follows
Need for expression conversion (from infix to postfix)
Infix expressions are readable and solvable by humans.
We can easily distinguish the order of operators, and also can use the parenthesis to
solve that part first during solving mathematical expressions.
The computer cannot differentiate the operators and parenthesis easily, that's
why postfix conversion is needed.
Precedence of operators
Highest to lowest
Exponentiation ^ (Right->Left)
Multiplication/division *, / (Left->Right)
Addition/subtraction +, - (Left->Right)
For operators of same precedence, the left-to-right rule applies
Example
Consider the following Infix Expression...
(A+B)*(C-D)
The given infix expression can be converted into postfix expression using Stack data
Structure as follows...
Evaluating Postfix Expression
Evaluation rule of a Postfix Expression :
1. While reading the expression from left to right, push the element in the stack if it is
an operand.
2. Pop the two operands from the stack, if the element is an operator and then evaluate
it.
3. Push back the result of the evaluation. Repeat it till the end of the expression.
Compute the following
Balanced Parenthesis in C
Given an expression containing only ‘(‘, ’)’, ’{‘, ‘}’, ‘[‘, ‘]’ , check whether the expression is
balanced or not.
An expression is balanced if each opening bracket is closed by the same type of closing
bracket in the exact same order.
The idea is to use the LIFO functionality of the stack. As a stack is LIFO data structure, we
will remove every opening bracket (‘(‘, ‘{‘, ‘[‘), whenever we encounter the same type of
closing bracket (‘)’, ‘}’, ‘]’).
If for any opening bracket we don’t have a closing bracket, the opening bracket will remain
in the stack forever.
Similarly if we only have a closing bracket without a preceding opening bracket we will just
push the closing bracket into the stack and it will remain there forever.
So, at last after traversing the whole expression, if the stack is empty then it will mean that
the given expression is balanced, otherwise if the stack contains elements that would mean
the given expression was unbalanced.
Else if current character is a closing bracket and of the top of the stack contains an opening
bracket of the same type then remove the top of the stack: Else, push ‘ch’ to the stack
“({[]})”
({[]}) Push
( {[]}) Push
({ []}) Push
({ }) Pop
( ) Pop
Empty
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.
The above figure shows that return addresses appear in the Stack in the reverse order in which
the functions were called.
After each function is completed, the pop operation is performed, and execution continues at
the address removed from the Stack. Thus the program that calls several functions in succession
can be handled optimally by the stack data structure.
Control returns to each function at a correct place, which is the reverse order of the calling
sequence.
Queue ADT
What is a Queue?
Queue is a linear data structure in which the insertion and deletion operations are
performed at two different ends.
In a queue data structure, adding and removing elements are performed at two
different positions.
The insertion is performed at one end and deletion is performed at another end.
In a queue data structure, the insertion operation is performed at a position which is
known as 'rear' and the deletion operation is performed at a position which is known
as 'front'.
In queue data structure, the insertion and deletion operations are performed based
on FIFO (First In First Out) or LILO (Last In Last out) principle.
Queue Implementation
Queue data structure can be implemented in two ways. They are as follows
Using Array
Using Linked List
Conditions to check while inserting and deleting
Enqueue C Routine
void EnQueue (int X)
{
if ( Rear = = size - 1)
print (" Full Queue !!!!. Insertion not possible");
else if (Front == -1 && Rear = = - 1)
{
Front = Front + 1;
Rear = Rear + 1;
Queue[Rear] = X;
}
else
{
Rear = Rear + 1;
Queue[Rear] = X;
}
}
Display C Routine
void display(){
if ( Front = = - 1 && Rear == -1)
printf("\nQueue is Empty!!!");
else{
int i;
printf("\nQueue elements are:\n");
for(i=front; i<=rear; i++)
printf("%d\t",queue[i]);
}
}
In above example, the last inserted node is 50 and it is pointed by 'rear' and the first
inserted node is 10 and it is pointed by 'front'. The order of elements inserted is 10,
15, 22 and 50.
Structure Definition for Queue using Linked List
struct Node
{
int data;
struct Node *next;
}*front = NULL,*rear = NULL;
Dequeue Routine
void dequeue()
{
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
front = front -> next;
printf("\nDeleted element: %d\n", temp->data);
free(temp);
}
}
Display Operation using Linked List
display() - Displaying the elements of Queue
We can use the following steps to display the elements (nodes) of a queue...
Step 1 - Check whether queue is Empty (front == NULL).
Step 2 - If it is Empty then, display 'Queue is Empty!!!' and terminate the function.
Step 3 - If it is Not Empty then, define a Node pointer 'temp' and initialize
with front.
Step 4 - Display 'temp → data --->' and move it to the next node. Repeat the same
until 'temp' reaches to 'rear' (temp → next != NULL).
Display C Routine
void display()
{
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
while(temp->next != NULL){
printf("%d",temp->data);
temp = temp -> next;
}
}
}
Operations on DEQUE
There are four basic operations,
Insertion at rear end
Insertion at front end
Deletion at front end
Deletion at rear end
Operations on DEQUE
Else we decrement front and insert the element. Since we are using circular array, if
front is equal to 0 then instead of decreasing it by 1 we make it equal to SIZE-1.
Routine for Insertion at front
void insertfront(int key)
{
//If queue is empty
if(front == -1)
front = rear = 0;
else if(front == 0)
front = SIZE-1;
else
--front;
arr[front] = key;
Delete at rear
Check if the queue is empty. If its not then we delete the last element by following the given
conditions :
• If only one element is present we make front and rear equal to -1.
• Else we decrement rear. If rear is equal to 0 then instead of decreasing it by 1 we
make it equal to SIZE-1.
Applications of Queue
1. Serving requests on a single shared resource, like a printer, CPU task scheduling etc.
2. In real life scenario, Call Center phone systems uses Queues to hold people calling
them in an order, until a service representative is free.
3. Handling of interrupts in real-time systems. The interrupts are handled in the same
order as they arrive i.e First come first served.
• This situation also says that Queue is Full and we cannot insert the new element
because 'rear' is still at last position.
• In the above situation, even though we have empty positions in the queue we can not
make use of them to insert the new element.
• This is the major problem in a normal queue data structure. To overcome this problem
we use a circular queue data structure.
Circular Queue
• Circular Queue is also a linear data structure, which follows the principle
of FIFO(First In First Out), but instead of ending the queue at the last position, it
again starts from the first position after the last, hence making the queue behave like a
circular data structure.
Applications of Queue Data Structures
Queue is used when things don’t have to be processed immediately, but have to
be processed in First In First Out order like Breadth First Search.
This property of Queue makes it also useful in following kind of scenarios.
1) When a resource is shared among multiple consumers.
Examples include CPU scheduling, Disk Scheduling.
2) When data is transferred asynchronously (data not necessarily received at
same rate as sent) between two processes. Examples include IO Buffers,
pipes, file IO, etc.
3) Handling of interrupts in real-time systems. The interrupts are handled in
the same order as they arrive i.e First come first served.