0% found this document useful (0 votes)
30 views41 pages

DS Unit 2 Material

Good..
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
30 views41 pages

DS Unit 2 Material

Good..
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 41

Unit II-Linear Data Structures

Stack ADT – Definition


Stack is a linear data structures. It is an ordered list in which insertions and deletions
are made at one end, called the top.
It follows the principle called First- In-Last Out (FILO) or Last-in-First-Out (LIFO)
Examples: CD stack, pile of plates, stack of coins, Deck of Cards etc.,

Stack ADT – Examples


Real time applications of Stack

Operations performed on Stack


A stack is a special case of ordered list. i.e., it is an ordered list with some restrictions on the
way in which we perform various operations on a list.
Stack can be implemented using Array and Linked List.

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.

POP operation using Array


void pop()
{
if(top==-1) // stack underflow
{
printf("\nStack is empty!!");
}
else
{
printf("\nDeleted element is %d",stack[top]);
top=top-1;
}
}
POP operation

Peek operation using Array


Peek operation : returns the value of the top most element of the stack
C routine:
int peek(int stack[])
{
if(top == -1)
{
printf("\n STACK IS EMPTY");
return -1;
}
else
return (stack[top]);
}
Display operation
void display(int stack[])
{
int i;
if(top == -1)
printf("\n STACK IS EMPTY");
else {
for(i = top;i >= 0;i--)
printf("\n%d",stack[i]);
}

Stack using Linked List


• The limitation in case of Stack using array is that we need to define the size at the
beginning of the implementation.
• This makes our Stack static.
• It can also result in "Stack overflow" if we try to add elements after the array is full.
• So, to alleviate this problem, we use linked list to implement the Stack.

Structure definition of Stack using Linked List


struct stack
{
int data;
struct stack *next;
} *top;
Push operation using Linked list
Push routine
void push(int value)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
if(top == NULL)
newNode->next = NULL;
else
newNode->next = top;
top = newNode;
}
POP operation
POP routine
void pop()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{
struct Node *temp = top;
top = temp->next;
free(temp);
}
}
PEEK Operation
void peek()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{

printf("\n Top element: %d", top->data);


}
}
Display
void display()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{
struct Node *temp = top;
while(temp != NULL)
{
printf("%d”,temp->data);
temp = temp -> next;
}
}}

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.

Infix to Postfix Conversion using Stack Data Structure


To convert Infix Expression into Postfix Expression using a stack data structure, use the
following steps...
1. Read all the symbols one by one from left to right in the given Infix Expression.
2. If the reading symbol is operand, then directly print it to the result (Output).
3. If the reading symbol is left parenthesis '(', then Push it on to the Stack.
4. If the reading symbol is right parenthesis ')', then Pop all the contents of stack until
respective left parenthesis is popped and print each popped symbol to the result.
5. If the reading symbol is operator (+ , - , * , / etc.,), then
if priority of operator in stack is greater than the priority of incoming operator then pop that
operator from stack and place it in the postfix expression (output).
Repeat this step till we get the operator in the stack which has greater priority than the
incoming operator.
• otherwise push the incoming operator onto the stack.
6. Pop and output the operators until the stack is empty

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

A B + C –B A + C for A =1, B =2 and C =3


7 5 2 + *4 1 5 - /-
562+* 84

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.

Approach – Check for Balanced Brackets in an Expression using Stack

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.

Algorithm to Check Balanced Parentheses in C using Stack

1. Initialize an empty stack.

2. Iterate i from 0 to length(expression).

 Store the current character in a variable ‘ch’.

 If stack is empty: Push ‘ch’ to the stack

 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

3. If the stack is empty, return true, else false.


Input:

“({[]})”

Stack Expression Action

({[]}) Push

( {[]}) Push

({ []}) Push

({[ ]}) Pop

({ }) Pop

( ) Pop

Empty

As the stack is empty, hence the given expression was balanced.


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.

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 Data Structures


Basic Operations
enqueue() − Insert / add an item to the queue.
dequeue() − Delete / remove an item from the queue.

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 Algorithm (insertion) (Using Array)

Step 1 - Check whether queue is FULL. (rear == SIZE-1)


Step 2 - If it is FULL, then display "Queue is FULL!!! Insertion is not
possible!!!" and terminate the function.
Step 3 - If it is NOT FULL, then increment rear value by one (rear++) and
set queue[rear] = value.

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;
}
}

deQueue Algorithm (Using Array)


Step 1 - Check whether queue is EMPTY. (front & rear == -1)
Step 2 - If it is EMPTY, then display "Queue is EMPTY!!! Deletion is not
possible!!!" and terminate the function.
Step 3 - If it is NOT EMPTY, Then check whether both front and rear are
equal, if it equal then set front = rear = -1
Else , then increment the front value by one (front++).
Dequeue C Routine
void DeQueue ()
{
if ( Front = = - 1 && Rear == -1)
print (" Empty Queue !. Deletion not possible " );
else if( Front = = Rear )
{
Front = - 1;
Rear = - 1;
}
else
{
Front = Front + 1 ;
}
}

Display (Using Array)


Step 1 - Check whether queue is EMPTY.
Step 2 - If it is EMPTY, then display "Queue is EMPTY!!!" and terminate the
function.
Step 3 - If it is NOT EMPTY, then display the elements from front to rear

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]);
}
}

Queue Using Linked List


 The major problem with the queue implemented using an array is, It will work for an
only fixed number of data values.
 Queue using an array is not suitable when we don't know the size of data which we
are going to use.
 A queue data structure can be implemented using a linked list data structure. The
queue which is implemented using a linked list can work for an unlimited number of
values.
 That means, queue using linked list can work for the variable size of data.
 The Queue implemented using linked list can organize as many data values as we
want.
In linked list implementation of a queue, the last inserted node is always pointed by
'rear' and the first node is always pointed by 'front'.

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;

Enqueue operation using Linked List


enQueue(value) - Inserting an element into the Queue
We can use the following steps to insert a new node into the queue...
Step 1 - Create a newNode with given value and set 'newNode → next' to NULL.
Step 2 - Check whether queue is Empty (rear == NULL)
Step 3 - If it is Empty then, set front = newNode and rear = newNode.
Step4 -If it is Not Empty then, set rear → next = newNode and rear = newNode.
Enqueue Routine
void enqueue(int value)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode -> next = NULL;
if(front == NULL)
front = rear = newNode;
else{
rear -> next = newNode;
rear = newNode;
}
printf("\nInsertion is Success!!!\n");
Dequeue operation using Linked List
deQueue() - Deleting an Element from Queue
We can use the following steps to delete a node from the queue...
Step 1 - Check whether queue is Empty (front == NULL).
Step 2 - If it is Empty, then display "Queue is Empty!!! Deletion is not
possible!!!" and terminate from the function
Step 3 - If it is Not Empty then, define a Node pointer 'temp' and set it to 'front'.
Step 4 - Then set 'front = front → next' and delete 'temp' (free(temp)).

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;
}
}
}

Double Ended Queue (DEQUE)


Double Ended Queue Data structure
Double Ended Queue is also a Queue data structure in which the insertion and
deletion operations are performed at both the ends (front and rear).
We can insert at both front and rear positions and can delete from both front and rear
positions.
Types of Double Ended Queue
Double Ended Queue can be represented in TWO ways, those are as follows...
Input Restricted Double Ended Queue
Output Restricted Double Ended Queue
Input Restricted Double Ended Queue
In input restricted double-ended queue, the insertion operation is performed at only
one end and deletion operation is performed at both the ends.

Output Restricted Double Ended Queue


In output restricted double ended queue, the deletion operation is performed at only
one end and insertion operation is performed at both the ends.

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

Insert Elements at Front


If the queue is empty then initialize front and rear to 0. Both will point to the first
element.

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;

Insert Elements at rear


If the queue is empty then initialize front and rear to 0. Both will point to the first
element.
Else we increment rear and insert the element. Since we are using circular array, if
rear is equal to SIZE-1 then instead of increasing it by 1 we make it equal to 0.

Routine for Insertion at rear


void insertrear(int key)
{

//If queue is empty


if(front == -1)
front = rear = 0;
//If rear points to the last element
else if(rear == SIZE-1)
rear = 0;
else
++rear;
arr[rear] = key;
}

Delete front Element


In order to do this, we first check if the queue is empty. If its not then delete the front
element by following the given conditions :
If only one element is present make front and rear equal to -1.
Else we increment front. If front is equal to SIZE-1 then instead of increasing it by 1
we make it equal to 0.

Routine for Deletion at front


void deletefront()
{
if(empty())
{
printf("UNDERFLOW");
}
else
{
//If only one element is present
if(front == rear)
front = rear = -1;
//If front points to the last element
else if(front == SIZE-1)
front = 0;
else
++front;
}
}

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.

Routine for Delete at rear


void deleterear()
{
if(empty())
{
printf("UNDERFLOW");
}
else
{
//If only one element is present
if(front == rear)
front = rear = -1;
//If rear points to the first position element
else if(rear == 0)
rear = SIZE-1;
else
--rear;
}
}

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.

Why we need a circular queue?


• In a normal Queue Data Structure, we can insert elements until queue becomes full.
• But once the queue becomes full, we can not insert the next element until all the
elements are deleted from the queue. For example, consider the queue below
• The queue after inserting all the elements into it is as follows...
Now consider the following situation after deleting three elements from the queue...

• 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.

You might also like