DS Module-2
DS Module-2
MODULE-2
QUEUES: Queues, Circular Queues, Using Dynamic Arrays, Multiple Stacks and queues.
LINKED LISTS : Singly Linked, Lists and Chains, Representing Chains in C, Linked
Stacks and Queues, Polynomials
Queues:
A queue is an ordered list in which insertions and deletions take place at different ends. The end at
which new elements are added is called the rear, and that from which old elements are deleted is called
the front. Queues are also known as First-In-First-Out (FIFO) lists.
Example
Initially f =-1 r=-1 queue empty
Element
index [0] [1] [2] [3] [4] [5]
f=-1
Insert 3
Element 3
f=-1
Insert 5
Element 3 5
f=-1
Insert 7
Element 3 5 7
delete
Element 5 7
Deleted item =3
C implementation of queues for an integer array: A queue can be represented by using an array to
hold the elements of the array and to use two variables to hold the position of the first and last element
of the queue.
#define size 10
int q[size];
int front=-1 ,rear=-1;
SHRUTHI U, Dept. of CSE(AIML), RNSIT, Bengaluru. Page 2
Data Structures and Applications(BCS304) Module 2
Insert operation
The insert operation first checks for queue overflow. if the queue is not full it inserts one element into
the queue at the rear.
void insert(int item)
{
if rear==size-1)
printf(“queue overflow”);
else
{
rear++;
q[rear]=item;
}
}
Delete operation: Delete operation checks for queue underflow condition and if the queue is not
empty it will remove the element at the front.
int delete()
{
int itemdel;
if (front ==rear)
{
printf(“queue underflow”);
return(0);
}
else
{
front++
itemdel=q[front];
return(itemdel);
}
}
Display operation: The display operation will display the elements of the queue if the queue is not
empty.
void display(s)
{
if (front==rear)
printf(“queue empty”);
else
{
for(i=front+1;i<=rear;i++)
printf(“%d”,q[i]);
}
}
Disadvantage of linear queue: The following example illustrates the disadvantage of linear queue
Even if the queue is empty since the value of rear= size-1 elements cannot be inserted into the queue.
This is the disadvantage of linear queue.
Example:
Initially when front=0 rear=0 i.e when front == rear the queue is empty now after 6 insertions
are made again fron=0 and rear= 0 that is the queue is full. So, we cannot distinguish between
an empty and a full queue.
To avoid the resulting confusion, the value of the rear is incremented before we check for the
condition front == rear for queue overflow.
#define MAX_QUEUE_SIZE 6
int q[size];
int front=0 ,rear=0;
Insert operation: The insert operation first checks for queue overflow. if the queue is not full it inserts
one element into the queue at the rear.
Delete operation: Delete operation checks for queue underflow condition and if the queue is not
empty it will remove the element at the front.
element deleteq()
{
element item;
if (front == rear)
return
queueEmpty();
front = (front+1) % MAX_QUEUE_SIZE;
return queue[front];
}
To add an element to a full queue, we must first increase the size of this array using a function
such as realloc.
As with dynamically allocated stacks, we use array doubling. However, it isn't sufficient to
simply double array size using realloc.
Consider the full queue . This figure shows a queue with seven elements in an array whose
capacity is 8. To visualize array doubling when a circular queue is used, the array is flattened
out as shown in the array of Figure (b).
To get a proper circular queue configuration, The number of elements copied can be limited to capacity
- 1 by customizing the array doubling code so as to obtain the configuration as shown below.
/* switch to newQueue */
front= 2 * capacity - 1;
rear = capacity - 2;
capacity *= 2;
free(queue);
queue = newQueue;
}
The function copy(a,b,c) copies elements from locations a through b-1 to locations beginning at c
Deques: A deque (pronounced either as deck or dequeue) is a linear list in which elements
can be added or removed at either end but not in the middle. The term deque is a contraction
of the namedouble ended queue.
Representation: It is represented as a circular array deque with pointers left and right, which point to
the two ends of the queue. It is assumed that the elements extend from the left end to the right end in
the array. The term circular comes from the fact that DEQUE[0] comes after DEQUE[n-1] in the array.
Example1:
Left=2 A B C
Right=4
Example2:
Left=5 A B D E
Right=1
[0] [1] [2] [3] [4] [5] [6]
Priority queue: A priority queue is a collection of elements such that each element has been
assigned a priority such that the order in which the elements are deleted and processed comes
from the following rules.
1. An element of higher priority is processed before any element of lower priority.
2. Two elements with the same priority are processed according to the order in which they were
added to the queue.
Example: Time sharing system: programs of higher priority are processed first and programs with the
same priority form a standard queue.
Representation using multiple queue: Use a separate queue for each level of priority. Each queue
will appear in its own circular array and must have its own pair of pointers, front and rear. if each
queue is allocated the same amount of space, a two dimensional array can be used instead of the linear
arrays.
Example : Consider the queue given below with the jobs and its priorities and its representation. A
job with priority 1 is considered to have the highest priority
J1 1
J2 1
J3 2
J4 4
J5 4
J6 6
Front rear 1 2 3 4 5 6
1 1 2 1 J1 J2
2 2 3 2 J3
3 3
4 4 5 4 J4 J5
5 5
6 6 6 6 J6
Delete operation
Algorithm:
1. Find the smallest k such that front[k]!=rear[k] ie Find the first non empty queue
2. Delete the process at the front of the queue
3. Exit
Insert operation
Algorithm: this algorithm adds an ITEM with priority number P to a priority queue maintained by a
two dimensional array
1. Inset ITEM as the rear element in row P-1 of queue
2. exit
if there is a single stack, the starting point is top=-1 and maximum size is SIZE-1
if there are two stacks to be represented in a single array then we use stack [0] for the bottom
element of the first stack, and stack[MEMORY_SIZE - 1] for the bottom element of the second
stack. The first stack grows toward stack[MEMORY_SIZE - 1] and the second grows toward
stack[0]. With this representation, we can efficiently use all the available space.
Representing more than two stacks within the same array poses problems since we no longer
have an obvious point for the bottom element of each stack. Assuming that we have n stacks,
we can divide the available memory into n segments. This initial division may be done in
proportion to the expected sizes of the various stacks, if this is known. Otherwise, we may
divide the memory into equal segments.
Assume that i refers to the stack number of one of the n stacks.To establish this stack, we must
create indices for both the bottom and top positions of this stack.The convention we use is that
o bottom [i], 0 ≤ i < MAX_STACKS, points to the position immediately to the left of the
bottom element of stack i.
o top[i], 0 ≤ i < MAX_STACKS points to the top element.
o Stack i is empty if bottom[i] = top[i].
To divide the array into roughly equal segments we use the following code:
Stack i can grow from bottom[i] + 1 to bottom [i + 1 ] before it is full. Boundary for the last stack,
boundary [n] is set to MEMORY_SIZE- 1
Initial configuration of the stack is shown below m is the size of the memory
element pop(int i)
{
if (top[i] == bottom[i])
return stackEmpty(i);
return stack[top[i]--];
}
Mazing Problem
Representation: Maze is represented as a two dimensional array in which zeros represent the open
paths and ones the barriers. The location in the maize can be determined by the row number and
column number Figure below shows a simple maze.
if the position is on the border then there are less than eight directions. To avoid checking for border
conditions the maze can be surrounded by a border of ones. Thus an m*p maize will require an
(m+2)*(p+2) array the entrance is at position [1][1] and exit is at [m][p]. The possible direction to
move can be predefined in an array move as shown below where the eight possible directions are
numbered from 0 to 7. for each direction we indicate the vertical and horizontal offset.
Offset move[8];
Table of moves: The array moves is initialized according to the table given below.
As we move through the maze, we may have the choice of several directions of movement. Since we
do not know the best choice we save our current position and arbitrarily pick a possible move. By
saving the current position we can return to it and try another path. A second two dimensional array
can be maintained to keep track of the visited locations. The maze can be implemented by making use
of a stack where the element is defined as follows.
Linked List
Introduction
Linked list is a collection of zero or more nodes ,where each node has some information. Given the
address of the first node, any node in the list can be obtained. Every node consis of two parts one is
the information part and the other is the address of the next node. The pointer of the last node
contains a special value called NULL.
Representation of linked list: Each item in the list is called a node and contains two fields
Information/Data field - The information field holds the actual elements in the list
Link field- The Link field contains the address of the next node in the list
To create a linked list of integers the node can be defined as follows using a self referential
structure.
struct Node
{
int info;
struct Node * link;
};
typedef struct Node NODE;
After the node is created we have to create a new empty list as follows
node * first=NULL;
The pointer first stores the address of the first node in the list. With this information we
will be able to access the location of all the other nodes in the list.
To obtain a node we use the statement
First=(node*) malloc(sizeof(node)
To place the information 5 ,we can use the statement Firs->info= 5;
As there are no other nodes in the list the link part can be made NULL as follow
First->link=NULL
First 5
The maintenance of linked lists in memory assumes the possibility of inserting new nodes
into the lists and hence requires some mechanism which provides unused memory space
for the new nodes. Similarly a mechanism is required which makes the deleted node
available for future use.
Together with the linked list in memory, a special list is maintained which consist of
unused memory cells.
This list which has its own pointer is called the list of available space or the free storage
list or the free pool. Such a list is also called AVAIL
Instead of using the malloc function the following getnode() function can be used to get a new
node
NODE * getNode(void)
{
/* provide a node for use */NODE
* new;
if (avail)
{
new = avail;
avail = avail→link;
return new;
}
else
{
new=( NODE *)malloc(sizeof(NODE));
return new;
}
}
Instead of the free function the following retnode function can be used
Garbage collection
Suppose some memory space becomes reusable because a node is deleted from a list or an
entire list is deleted from a program, we can make this space to be available for future use.
One way is to immediately reinsert the space into the free storage list.
This is done when a list is implemented by linear arrays. But this method may be too time
consuming for the operating system of the computer. So an alternate method is devised.
The operating system of a computer may periodically collect all the deleted space on to the
free storage list this technique si called garbage collection
temp->link=NULL;
if (first==NULL)
first=temp;
else
{
cur=first
while(cur->link!=NULL)cur=cur-
>link;
cur->link=temp
}
}
}
SHRUTHI U Dept. of CSE(AIML), RNSIT, Bengaluru. Page 16
Data Structures and Applications(BCS304) Module 2
Delete the nodes from a linked list pointed by first whose information part is specified is item
cur=first;
while (cur!=NULL)
{
if (cur->info==item)
{
prev->link=cur->link;
free(cur); return(first);
}
else
{
prev=cur;
cur=cur->link;
}
}
printf(“node with item not found”)
return(first);
}
Delete the NODE present at location loc, the NODE that precedes is present at location locp. if
there is only one NODE then locp=NULL
if (first==NULL)
printf(“list is empty);
else
{
cur=first
while(cur!=NULL)
{
printf(“%d \t”, cur->info);cur=cur-
>link;
}
}
if (first==NULL)
{
printf(“list is empty);
return(0)
}
cur=first
while(cur!=NULL)
{
count++;
cur=cur->link;
}
return(count)
}
retrun;
}
cur =cur->link;
}
}
printf(“search unsuccessfull”);
}
printf("queue overflow\n");
return(first)
}
temp->info=item;
temp->link=NULL;
if (front==NULL)
{
rear=temp;
front=temp;
}
else
{
rear->link=temp;
rear=temp;
}
}
Function deletes the NODE in the front and returns the item
int del_front(NODE * front)
{
NODE cur;int
itemdel;
if(front==NULL)
{
printf("Queue underflow\n");
return front;
}
cur=front;
itemdel=cur->info;
front=front->link;
free(cur);
return(itemdel);
}
top[MAX_STACKS]
We assume that the initial condition for the stacks is: top[i] = NULL, 0≤i < MAX_STACKS
and the boundary condition is: top[i] = NULL if the ith stack is empty
Function push creates a new NODE, temp, and inserts the NODE in front of the ith stack.
Function pop returns the top element and changes top to point to the address contained in its
link field.
int pop(int i)
{/* remove top element from the ith stack */int
itemdel;
Stack * temp;
if (top[i]==NULL) return
stackEmpty();
temp = top[i];
itemdel = temp→data;
top[i] = top[i]→link;
free(temp);
return item;
}
We assume that the initial condition for the queues is: front[i] = NULL, ,rear[i]=NULL0 ≤ i
< MAX_QUEUES
and the boundary condition is: front[i] = NULL iff the ith queue is empty
{
front[i] = temp;
rear[i] = temp;
}
else
{
rear[i]→link = temp;
rear[i]=temp;
}
}
Function deleteq deletes the item in the front of the ith queue
int deleteq(int i)
{/* delete an element from queue i */Queue *
temp;
int itemdel
if (front[i]==NULL) return
queueEmpty();
temp = front[i];
itemdel = temp→data;
front[i]= front[i]→link;
free(temp);
return itemdel;
}
A singly linked list in which the last NODE has a null link is called a chain. if the link field of the
last NODE points to the first NODE in the list, then such a linked list is called a circular list.
last
By keeping a pointer at the last instead of the front we can now insert easily at the front and end of
the list
if (last==NULL)
{
/* list is empty, change last to point to new entry */ last =
new;
last→link = last;
}
else
{
/* list is not empty, add new entry at front */
new→link = last→link;
last→link = new;
}
}
if (last==NULL)
{
/* list is empty, change last to point to new entry */ last =
new;
last→link = last;
}
else
{
/* list is not empty, add new entry at front */
New→link = last→link;
last→link = New;
last=New;
}
}
count++;
temp = temp→link;
}
return count;
}
A header linked list is a linked list which always contains a special NODE, called the header NODE,
at the beginning of the list. There are two types of header list.
Note: Unless stated it is assumed that the linked list is circular header list.
Polynomials Polynomial
Representation
We should be able to represent any number of different polynomials as long as memory is available.
In general, A polynomial is represented as :
A(x)= am-1xm-1 + ............ a0x0
where the ai are nonzero coefficients and the ei are nonnegative integer exponents such that
e m-1 > em-2 >......................> e1 > e0 ≥ 0.
We represent each term as a NODE containing coefficient and exponent fields, as well as a pointer
to the next term. Assuming that the coefficients are integers, the type declarations are:
int expon;
struct polyNode * link;
};
typedef struct polyNode POLY;
Consider the polynomials a = 3x14 + 2x8 + 1x+2 and b = 8x12- 3x10 + 10x5 +3 It can be represented as
follows
a 3 14 2 8 1 1 2 0
b 8 12 3 10 10 5 3 0
Adding Polynomials
To add two polynomials, we examine their terms starting at the NODEs pointed to by a and b.
1. if a→expon = b→expon, we add the two coefficients a→coef + b→coef and create a new
term for the result c. a = a→link; b = b→link;
2. if a→expon < b→expon, then we create a duplicate term of b, attach this term to the
result,called c, and b = b→link;
3. if a→expon > b→expon, then we create a duplicate term of a, attach this term to the result,
called c, and a = a→link;
POLY *Pointer padd(POLY * a, POLY * b) /* return a polynomial which is the sum of a andb */
{
POLY * c,*tempa, *tempb,*lastc;int sum;
c= (POLY*)malloc(sizeof(POLY))c-
>link=NULL
tempa=a
tempb=blastc=c;
while (tempa!=NULL && tempb!=NULL)
{
switch (COMPARE(tempa→expon, tempb→expon))
{
case -1: lastc=attach(tempb→coef, tempb→expon,lastc);
tempb = tempb→link;
break;
return(c);
}
POLY *Pointer padd(POLY * a, POLY * b) /* return a polynomial which is the sum of a andb */
{
POLY * c,*tempa, *tempb,*lastc;int sum;
c= (POLY*)malloc(sizeof(POLY))
c->link=c
SHRUTHI U Dept. of CSE(AIML), RNSIT, Bengaluru. Page 30
Data Structures and Applications(BCS304) Module 2
tempa=a->link;
tempb=b->link;
lastc=c;
while (tempa!=a && tempb!=b)
{
switch (COMPARE(tempa→expon, tempb→expon))
{
case -1: lastc=attach(tempb→coef, tempb→expon,lastc);
tempb = tempb→link;
break;
lastc->link=c
return(c);
}
temp→coef = c;
temp→expon = e;
temp->link =rear->link;
rear->link=temp;
rear=temp;
return(rear);
}
if m > 0 and n > 0, the while loop is entered. Each iteration of the loop requires O(1) time.At each
iteration, either a or b moves to the next term or both move to the next term. Since the iteration
terminates when either a or b reaches the end of the list, therefore, the number of iterations is bounded
by m + n - 1.
The time for the remaining two loops is bounded by O(n + m). The first loop can iterate m times and
the second can iterate n times. So, the asymptotic computing time of this algorithm is O(n +m).