Data Structures Module 2
Data Structures Module 2
Module - II
Linear Data Structures and their Sequential Storage Representation
2.1 Stack
A Stack is data structure in which addition of new element or deletion of existing element always
takes place at a same end. This end is known as the top of the stack. That means that it is possible
to remove elements from a stack in reverse order from the insertion of elements into the stack.
One other way of describing the stack is as a last in, first out (LIFO) abstract data type and linear
data structure. Pictorial representation of the same is shown below
PUSH POP
40 TOP
[3]
30
[2]
20
[1]
[0] 10
int stack[SIZE]; stack [] is an array of integer type with a predefined size SIZE .
Implementation of basic functions goes as below:
Push Function in C
void push()
{
int n;
printf("\n Enter item in stack");
scanf("%d",&n);
if(top==size-1)
{
printf("\nStack is Full");
}
else
{
top=top+1;
stack[top]=n;
}
}
Pop Function in C
void pop()
{
int item;
if(top==-1)
{
printf("\n Stack is empty");
}
else
{
item=stack[top];
printf("\n item popped is=%d", item);
top--;
}
}
#include <stdio.h>
#include <stdlib.h>
struct node
{
int info;
struct node *ptr;
}*top,*top1,*temp;
void push(int data);
void pop();
void display();
void destroy();
void main()
{
int no, ch, e;
printf("\n 1 - Push");
printf("\n 2 - Pop");
printf("\n 3 - Exit");
printf("\n 4 - Dipslay");
printf("\n 5 - Destroy stack");
top=NULL;
while (1)
{
printf("\n Enter choice : ");
scanf("%d", &ch);
switch (ch)
{
case 1:
top1 = top1->ptr;
printf("\n Popped value : %d", top->info);
free(top);
top = top1;
}
void destroy()
{
top1 = top;
while (top1 != NULL)
{
top1 = top->ptr;
free(top);
top = top1;
top1 = top1->ptr;
}
free(top1);
top = NULL;
printf("\n All stack elements destroyed");
}
Polish notation (PN), also known as normal Polish notation (NPN), Polish prefix notation or
simply prefix notation is a form of notation for logic, arithmetic, and algebra. Its distinguishing
feature is that it places operators to the left of their operands
3 2 -3 2
3 * 2+5 +* 3 25
(12 - 3 ) /3 / -12 3 3
Reverse Polish Notation (RPN), sometimes referred to as postfix notation, is a way of writing
mathematical expressions where each operand is preceded by the two operators it applies to and
looks something like 2 5 3 + * instead of (5 + 3 ) * 2
Well, it turns out that it is easier/more efficient for computers to parse an RPN expression than an
infix one. In fact, if you look at the code underlying most calculators you'd find that most of
them take the infix expressions that we write and first convert them to postfix before evaluating
them. Meta calculator's graphing and scientific calculators both do exactly this.
Examples
3-2 32-
3 * 2+5 532*+
(12 - 3 ) /3 12 3 - 3 /
Infix notation is easy to read for humans, whereas pre-/postfix notation is easier to parse for a
machine. The big advantage in pre-/postfix notation is that there never arise any questions like
operator precedence.
For example, consider the infix expression 1 # 2 $ 3. Now, we don't know what those operators
mean, so there are two possible corresponding postfix expressions: 1 2 # 3 $ and 1 2 3 $ #.
Without knowing the rules governing the use of these operators, the infix expression is
essentially worthless.
Or, to put it in more general terms: it is possible to restore the original (parse) tree from a
pre-/postfix expression without any additional knowledge, but the same isn't true for infix
expressions.
15 7 + 1 1 3 + 2 + 1 1 =
15 7 2 3+2+11=
15 5 3+2+11=
3 3+2+11=
9 +2+11=
9 +22 =
9 4 =
Answer is 5
A/B^C-D
Parse the expression from left to right. Since ^ has precedence than other
operators in the expression, consider its operands and convert it into
immediate postfix form.
Since, / has higher precedence, consider its operands and convert it into its
immediate postfix notation. i.e.,AE1/-D. Now say the converted portion as E2
and rewrite the expression
E2-D
ABC^/D-
Thats it!!
Computer converts the same expression using stack and here comes one of the main applications
of stack. The below shown tabular conversion would best explain the overall process.
Current
Expression Stack Output Comment
Symbol
Topmost element.
Step 2: Remove Topmost Operator From
Stack and Print it
Example:
Let the given expression be 2 3 1 * + 9 -. We scan all elements one by one.
Try it yourself
Parse the expression from left to right. Since ^ has precedence than other
Since, / has higher precedence, consider its operands and convert it into its
immediate postfix notation. i.e., /AE1-D. Now say the converted portion as E2
E2-D
-/A^BCD
Thats it!!
2.8 Recursion
A function that calls itself is known as recursive function. And, this technique is
known as recursion.
The recursion continues until some condition is met to prevent it. To prevent infinite
recursion, if...else statement (or similar approach) can be used where one branch
makes the recursive call and other doesn't.
An example program in C
#include <stdio.h>
int sum(int n);
int main()
{
int number, result;
printf("Enter a positive integer: ");
scanf("%d", &number);
result = sum(number);
printf("sum=%d", result);
}
int sum(int n)
{
if (n!=0)
return n + sum(n-1); // sum() function calls itself
else
return n;
}
Initially, the sum() is called from the main() function with number passed as an
argument.
Suppose the value of n is 3 initially. During next function call, 2 is passed to
the sum() function. In next function call, 1 is passed to the function. This process
continues until n is equal to 0.
When n is equal to 0, there is no recursive call and the sum of integers is returned to
the main() function.
#include <stdio.h>
int factorial(int);
void main()
int result,num;
scanf("%d",&num);
result=factorial(num);
printf("%d",result);
if(ele==0)
return 1;
else
return (ele*factorial(ele-1));
Output
Factorial of 6 = 720
Suppose the user entered 6. Initially, the multiplyNumbers() is called from the main() function
with 6 passed as an argument. Then, 5 is passed to the multiplyNumbers() function from the
same function (recursive call). In each recursive call, the value of argument nis decreased by 1.
When the value of n is less than 1, there is no recursive call.
60
G.C.D of 366 and 60 is 6.
In the above program, towers () is the function that shows its execution in a recursive manner.
OK, now let us see the problem constraints of Tower of Hanoi and let us try to understand how it
can be addressed non-programmatically.
Problem: Move all the disks over to the rightmost tower, one at a time, so that they end up in the
original order on that tower. You may use the middle tower as temporary storage, but at no time
during the transfer should a larger disk be on top of a smaller one.
3
2
1
3
2
1
What you are observing is the problem (in the upper part) and its solution (in the lower part). If
we manually try to work it out keeping all the constraints in mind, the sequence of steps that one
would perform for 3 disks:
O K.. now that you got an idea of how this problem can be solved manually. But, we being
programmers should find out solution with computer as a tool. Lets do that..
A small note to you all, it would be better if you take a copy (print or handwritten) of the
program shown above in your hand for analysis purpose otherwise you need to scroll several
times.
3, A, C, B Pushed to
Stack
n f t a TOP
Pushed to
2, A, B, C
Stack
3, A, C, B TOP
Not stack
contents n f t a
Once again, the condition is checked i.e., if(num==1) ? its not again.
One more time recursive call takes place:
towers(num - 1, frompeg, auxpeg, topeg); i.e., num-1 = 1; frompeg=A; auxpeg=C; topeg=B;
Now, these parameter values will be mapped to the parameters in (1)
Resulting into as below,
num = num-1 = 1; frompeg = frompeg = A; topeg = auxpeg = C; auxpeg = topeg = B;
Now the stack content would look like this
Pushed to
1, A, C, B
Stack
2, A, B, C TOP
3, A, C, B
n f t a
This time the condition satisfies i.e., num =1 now. Top of the stack will be popped as recursion
gets a break. The statement inside will get executed and the following statement would get
printed
2, A, B, C Pushed to
Stack
3, A, C, B TOP
n f t a
Figure X
Pushed to
3, A, C, B Stack
TOP
n f t a
Now with the popped parameter values, second function call invokes the function.
towers(num - 1, auxpeg, topeg, frompeg); num-1 = 1; auxpeg = C; topeg = B; frompeg = A;
[referring to stack top of figure X ]
Now, the above parameter values will be mapped to the parameters in (1)
Resulting into as below,
num = num-1 = 1; frompeg = auxpeg = C; topeg = topeg = B; auxpeg = frompeg = A;
1, C, B, A Pushed to
Stack
3, A, C, B TOP
n f t a
Since the condition if(num == 1) satisfies, the print statement would get executed by taking the
popped parameters.
Hence, we see
Move disk 1 from peg C to peg B; num=1, frompeg = C, topeg = B
Stack contents now,
Pushed to
3, A, C, B Stack
TOP
n f t a
Figure Y
Finally, the stack top is popped and the parameters are used for executing the print statement
outside the condition. Stack is empty now.
n f t a
Now, the above parameter values will be mapped to the parameters in (1)
Resulting into as below,
num = num-1 = 2; frompeg = auxpeg = B; topeg = topeg = C; auxpeg = frompeg = A;
Now the stack content would look like this
Pushed to
2, B, C, A Stack
TOP
n f t a
Mr. Nischaykumar Hegde, CSE, VCET, Puttur
Data Structures and Applications (15CS33)
The condition will not hold because num = 2. Now, the first recursive call invokes the function
by supplying parameters onto the stack.
towers(num - 1, frompeg, auxpeg, topeg); i.e., num-1 = 1; frompeg=B; auxpeg=C; topeg=A;
Now, the above parameter values will be mapped to the parameters in (1)
Resulting into as below,
num = num-1 = 1; frompeg = frompeg = B; topeg = auxpeg = C; auxpeg = topeg = A;
Now the stack content would look like this
Pushed to
2, B, C, A Stack
TOP
Gets n f t a
Popped 1, B, A, C
Figure Z
The if condition holds, hence print statement executes popping the stack.
{
if (top == NULL)
{
top =(struct node *)malloc(1*sizeof(struct node));
top->ptr = NULL;
top->info = data;
}
else
{
temp =(struct node *)malloc(1*sizeof(struct node));
temp->ptr = top;
temp->info = data;
top = temp;
}
}
/* Display stack elements */
void display()
{
top1 = top;
if (top1 == NULL)
{
printf("Stack is empty");
return;
}
while (top1 != NULL)
{
printf("%d ", top1->info);
top1 = top1->ptr;
}
}
/* Pop Operation on stack */
void pop()
{
top1 = top;
if (top1 == NULL)
{
printf("\n Error : Trying to pop from empty stack");
return;
}
else
top1 = top1->ptr;
printf("\n Popped value : %d", top->info);
free(top);
top = top1;
}
void destroy()
{
top1 = top;
while (top1 != NULL)
{
top1 = top->ptr;
free(top);
top = top1;
top1 = top1->ptr;
}
free(top1);
top = NULL;
printf("\n All stack elements destroyed");
}
2.11 Queues
Queue is an abstract data structure, somewhat similar to Stack. In contrast to Stack, queue is
opened at both end. One end is always used to insert data, that end is referred to as REAR and
the operation is called enqueue and the other end referred to as FRONT, is used to remove data
and the operation is called dequeue. Queue follows First-In-First-Out methodology, i.e., the data
item stored first will be accessed first.
A real world example of queue can be a single-lane one-way road, where the vehicle enters first,
exits first. More real-world example can be seen as queues at ticket windows & bus-stops.
Queue [6]: 10 20 30 40 50
REAR
FRONT
The representation of queue is using arrays. Initially, FRONT & REAR indices are initialized to
-1. As elements are inserted, rear is incremented by 1 and as elements are deleted, front is
incremented by 1. Shaded cells in the above figure are empty. If an element is added, it sits in the
immediate next cell to rear and rear shifts right by one cell (i.e., rear++). Similarly if an element
is deleted, front shifts right by one cell (i.e. front++).
Applications of Queue
Queue, as the name suggests is used whenever we need to have any group of objects in an order
in which the first one coming in, also gets out first while the others wait for their turn, like in the
following scenarios:
1. Serving requests on a single shared resource, like a printer, CPU task scheduling etc.
2. In real life, Call Center phone systems will use 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, First come first served.
Array Implementation of a Queue
#include <stdio.h>
#define MAX 50
int queue_array[MAX];
int rear = - 1;
int front = - 1;
main()
{
int choice;
while (1)
{
printf("1.Insert element to queue \n");
printf("2.Delete element from queue \n");
printf("3.Display all elements of queue \n");
printf("4.Quit \n");
printf("Enter your choice : ");
scanf("%d", &choice);
switch (choice)
{
case 1:
insert();
break;
case 2:
delete();
break;
case 3:
display();
break;
case 4:
exit(1);
default:
printf("Wrong choice \n");
} /*End of switch*/
} /*End of while*/
} /*End of main()*/
insert()
{
int add_item;
if (rear == MAX - 1)
printf("Queue Overflow \n");
else
{
if (front == - 1)
/*If queue is initially empty */
front = 0;
printf("Inset the element in queue : ");
scanf("%d", &add_item);
rear = rear + 1;
queue_array[rear] = add_item;
}
} /*End of insert()*/
delete()
{
if (front == - 1 || front > rear)
{
printf("Queue Underflow \n");
return ;
}
else
{
printf("Element deleted from queue is : %d\n", queue_array[front]);
front = front + 1;
}
} /*End of delete() */
display()
{
int i;
if (front == - 1)
printf("Queue is empty \n");
else
{
printf("Queue is : \n");
for (i = front; i <= rear; i++)
printf("%d ", queue_array[i]);
printf("\n");
}
}
In a standard queue data structure re-buffering problem occurs for each de queue operation. To
solve this problem by joining the front and rear ends of a queue to make the queue as a circular
queue.
4. Elements are added at the rear end and the elements are deleted at front end of the
queue
5. Both the front and the rear pointers points to the beginning of the array.
6. It is also called as Ring buffer.
Basic Operations
1. Insert / enqueue add an item to the rear of the queue.
int Q_full();
int Q_Empty();
void insert();
void delet();
void display();
int main()
{
printf("1.Insert\n2.Delete\n3.Display\n4.Exit\n");
while(1)
{
printf("Enter your choice\n");
scanf("%d",&ch);
switch(ch)
{
case 1: if(Q_full())
printf("Priority Queue is Full\n");
else
insert();
break;
case 2: if(Q_Empty())
printf("Priority Queue Empty\n");
else
delet();
break;
case 3: if(Q_Empty())
printf("Priority Queue Empty\n");
else
display();
break;
case 4: exit(0);
}
}
}
void insert()
{
int ele;
printf("Enter the element\n");
scanf("%d",&item);
if(front==-1)
front++;
j=rear;
while(j>=0 && item<queue[j])
{
queue[j+1]=queue[j];
j--;
}
queue[j+1]=item;
rear++;
}
int Q_full()
{
if(rear==SIZE-1)
return 1;
else
return 0;
}
void delet()
{
printf("The item deleted is %d",queue[front]);
front++;
}
int Q_Empty()
{
if((front==-1)||(front>rear))
return 1;
else
return 0;
}
void display()
{
printf("Elements of Priority Queue...");
for(i=front;i<=rear;i++)
printf("%d\t",queue[i]);
}
RADAR inserted R A D A R
Rear Front
Verification R A D A R