II Sem DS Unit I
II Sem DS Unit I
II Sem DS Unit I
Non primitive data structures are further classified into two types.
a) Linear data structure- In a linear data structure, the memory representation of
the elements of the data structure is continuous.
The different types of linear data structures are arrays, stacks, queues and linked
lists.
b) Non-linear data structure: In a non-linear data structure, the memory
representation of the elements of the data structures is non-continuous. The
elements are stored in a parent child relationship, where parent element is called
as root node and child element is called as leaf node.
The different types of non-linear data structures are graphs and trees.
Abstract Data Type: Abstract Data type (ADT) is a type (or class) for objects
whose behavior is defined by a set of values and a set of operations. The definition of
ADT only mentions what operations are to be performed but not how these
operations will be implemented. It does not specify how data will be organized in
memory and what algorithms will be used for implementing the operations. It is
called “abstract” because it gives an implementation-independent view.
The best algorithm is the one that helps to solve a problem that requires less space in
memory as well as takes less time to generate the output. But in general, it is not
always possible to achieve both of these conditions at the same time.
Asymptotic Notation
Linear arrays:
An array is a collection of variables of the same type that are referred to through a
common name. A specific element in an array is accessed by an index. In C++, all
arrays consist of contiguous memory locations. The lowest address corresponds to the
first element and the highest address corresponds to the last element. Arrays can have
from one to several dimensions.
1. Insertion of an element
2. Deletion of element
3. Searching for an element
4. Sorting the array of elements
5. Merging the array of elements
1. Insertion of element
An element can be inserted to an array either at the beginning of the array, or in a
required position.
Insert element at the beginning of the array.
Algorithm: insertbeg(a,ele) where a is an array of “n” elements and ele is the element to
insert.
Step 1: Start
Step 2 : for i=n to 0
Step 3: a[i+1]=a[i]
Step 4: a[0]=ele
Step 5: print “array elements after inserting”
Step 6: for i=0 to n-1
Step 7: print a[i]
Step 8: End
4
b) Binary Search: Find the middle element in the array. If the search key element is
matching with the element present in the middle position, the print element is
found. If the search key element is lesser than the middle element then continue
searching only in left of the middle element else continue searching only in right
of the middle element.
Algorithm Binary_Search(A,n,ele) where A is an array of "n" elements and
ele is the element to be searched.
Step 1: low=0
Step 2: high=n-1
Step 3: loc=-1
Step 4: while(low<=high)
Step 5: mid=(low+high)/2
Step 6: if (ele=a[mid])
Step 7: loc=mid
Goto step 12
7
[end of if step 6]
Step 8: if (ele<a[mid]) then
Step 9: high=mid-1
Step 10: else
Step 11: low=mid+1
[ end of if step 8]
[ end of while step 4]
Step 12: if(loc>=0)
Step 13: print ele "found at location" loc
Step 14: else
Step 15: print ele "not found"
Step 16 : exit
For a binary search to work, it is mandatory for the target array to be sorted. We shall
learn the process of binary search with a pictorial example. The following is our sorted
array and let us assume that we need to search the location of value 31 using binary
search.
Now we compare the value stored at location 4, with the value being searched, i.e. 31.
We find that the value at location 4 is 27, which is not a match. As the value is greater
than 27 and we have a sorted array, so we also know that the target value must be in the
upper portion of the array.
We change our low to mid + 1 and find the new mid value again.
low = mid + 1
mid = low + (high - low) / 2
Our new mid is 7 now. We compare the value stored at location 7 with our target value
31.
The value stored at location 7 is not a match, rather it is more than what we are looking
for. So, the value must be in the lower part from this location.
We compare the value stored at location 5 with our target value. We find that it is a
match.
10
Advantages:
Since the array is sorted, the time taken to search an element is lesser.
Disadvantages:
The array should be in sorted order. Either the array elements have to be entered in
sorted order, or after accepting the array elements they have to be sorted.
Sorting: The process by which the elements are arranged in ascending or descending
order.
There are various sorting techniques. They are
a. Bubble sort
b. Selection sort
c. Insertion sort
d. Merge sort
e. Shell sort
f. Heap sort
g. Quick sort
h. Radix sort
i. Bucket sort
elements is compared and the elements are swapped if they are not in order. This
algorithm is not suitable for large data sets as its average and worst case complexity are
of Ο(n2) where n is the number of items.
If the given array has to be sorted in ascending order, then bubble sort will start by
comparing the first element of the array with the second element, if the first element is
greater than the second element, it will swap both the elements, and then move on to
compare the second and the third element, and so on.
So Bubble sort is also called as "Sorting by exchange".
If we have total n elements, then we need to repeat this process for n-1 times.
It is known as bubble sort, because with every complete iteration the largest element in
the given array, bubbles up towards the last place or the highest index, just like a water
bubble rises up to the water surface.
Sorting takes place by stepping through all the elements one-by-one and comparing it
with the adjacent element and swapping them if required.
Example:
}
Analysis of Bubble sort, Selection sort, Insertion sort
The first pass of the algorithm results in n-1 comparisons and in the worst case may
result in n-1 exchanges also. The second pass results in n-2 comparisons and in the
worst case may result in n-2 exchanges. Contnuing the analysis we observe that as the
iterations or passes increases, the comparisons and exchanges decreases. Finally, the
total number of comparisons will be equal to
=(n-1)+(n-2)+(n-3)+........+2+1
=(n)*(n-1)/2
=O(n2)
Selection Sort: Selection sort is a sorting algorithm that selects the smallest element
from an unsorted list in each iteration and places that element at the beginning of the
unsorted list.
12 1 0 8 3
A[0] A[1] A[2] A[3]] A[4]
12 1 0 8 3
A[0] A[1] A[2] A[3]] A[4]
Small=12
Loc=0
Step 2: Compare all the array elements with Small, if there is any element that is
smaller than Small, then swap Small and the smallest element. In the above
example, 0 is the smallest element, so swap 12 and 0. After swapping the array
elements are
14
0 1 12 8 3
A[0] A[1] A[2] A[3]] A[4]
Step 3: The next element A[1]is taken as the smallest element. So Small is 1 and
loc=1.
0 1 12 8 3
A[0] A[1] A[2] A[3]] A[4]
Small=1
Loc=1
The array elements are scanned from A[2] and if any element is smaller than
Small i.e. 1, then Small and the smallest element are swapped. In the above
example, there is no smallest element compared to Small, so no swapping takes
place.
0 1 12 8 3
A[0] A[1] A[2] A[3]] A[4]
Small=12
Loc=2
Step 5: The array elements are scanned from A[3] and if any element is smaller
than Small i.e. 12, then Small and the smallest element are swapped. In the above
example, 3 is smallest element compared to Small, so swap 12 and 3, After
swapping the array elements are.
0 1 3 8 12
A[0] A[1] A[2] A[3]] A[4]
0 1 3 8 12
A[0] A[1] A[2] A[3]] A[4]
Small=8
Loc=3
15
The array elements are scanned from A[4] and if any element is smaller than
Small i.e. 8, then Small and the smallest element are swapped. In the above
example there are no smaller elements smaller than 8, so no elements are swapped.
0 1 3 8 12
A[0] A[1] A[2] A[3]] A[4]
Algorithm:
Program
#include<iostream.h>
#include<conio.h>>
void selectionsort(int *, int);
int main()
{
int n, arr[15],i;
cout<<"Enter the number of elements"<<endl;
cin>>n;
cout<<"Enter the array numbers"<<endl;
for(i=0;i<n;i++)
cin>>arr[i];
16
Insertion Sort:
Insertion sort is a simple sorting algorithm that works similarly to the way you sort
playing cards in your hands. The array is virtually split into a sorted and an unsorted
part. Values from the unsorted part are picked and placed at the correct position in the
sorted part.
12 11 13 5 6
First Pass:
• Initially, the first two elements of the array are compared in insertion sort.
12 11 13 5 6
• Here, 12 is greater than 11 hence they are not in the ascending order and
12 is not at its correct position. Thus, swap 11 and 12.
• So, for now 11 is stored in a sorted sub-array.
11 12 13 5 6
Second Pass:
• Now, move to the next two elements and compare them
11 12 13 5 6
• Here, 13 is greater than 12, thus both elements seems to be in ascending
order, hence, no swapping will occur. 12 also stored in a sorted sub-array
along with 11
Third Pass:
• Now, two elements are present in the sorted sub-array which are 11 and 12
• Moving forward to the next two elements which are 13 and 5
11 12 13 5 6
• Both 5 and 13 are not present at their correct place so swap them
11 12 5 13 6
• After swapping, elements 12 and 5 are not sorted, thus swap again
11 5 12 13 6
• Here, again 11 and 5 are not sorted, hence swap again
5 11 12 13 6
• Here, 5 is at its correct position
Fourth Pass:
18
• Now, the elements which are present in the sorted sub-array are 5,
11 and 12
• Moving to the next two elements 13 and 6
5 11 12 13 6
• Clearly, they are not sorted, thus perform swap between both
5 11 12 6 13
• Now, 6 is smaller than 12, hence, swap again
5 11 6 12 13
• Here, also swapping makes 11 and 6 unsorted hence, swap again
5 6 11 12 13
• Finally, the array is completely sorted.
Algorithm:
Algorithm insertionsort(arr,n) arr is a one-dimensional array of “n” elements and
n is the number of elements.
Program
#include<iostream.h>
#include<conio.h>
void insertionsort(int*, int);
int main()
19
{
int arr[10],i,n;
cout<<"How many elements to be stored"<<endl;
cin>>n;
cout<<"Enter the array elements"<<endl;
for(i=0;i<n;i++)
cin>>arr[i];
cout<<"Unsorted array elements:"<<endl;
for(i=0;i<n;i++)
cout<<arr[i]<<endl;
insertionsort(arr,n);
getch();
return 0;
}
void insertionsort(int arr[10], int n)
{
int i,key,j;
for(i=1;i<n;i++)
{
key=arr[i];
j=i-1;
while(j>=0 && arr[j]>key)
{
arr[j+1]=arr[j];
j--;
}
arr[j+1]=key;
}
cout<<"Sorted array elements"<<endl;
for(i=0;i<n;i++)
cout<<arr[i]<<endl;
20
STACKS
A stack is a linear data structure in which an element may be inserted or deleted only at
one end, called the top of the stack. This means that the last item to be added to a stack
is the first item to be removed. Accordingly, stacks are also called last-in first-out
(LIFO) lists.
The figure below shows the array representation of a stack in computer memory.
STACK
MAXSTK 5
Top 30
20
10
Here, STACK is a linear array and TOP is a variable that contains the location
of the top element of the stack. The variable MAXSTK gives the maximum number of
elements that can be held by the stack.
Operations on Stack
• PUSH operation
21
(a) Before inserting an element in a stack, we check whether the stack is full.
(b) If we try to insert the element in a stack, and the stack is full, then
the overflow condition occurs.
(c) When we initialize a stack, we set the value of top as -1 to check that the
stack is empty.
(d) When the new element is pushed in a stack, first, the value of the top gets
incremented, i.e., top=top+1, and the element will be placed at the new
position of the top.
(e) The elements will be inserted until we reach the max size of the stack.
POP operation
(f) Before deleting the element from the stack, we check whether the stack is
empty.
(g) If we try to delete the element from the empty stack, then
the underflow condition occurs.
(h) If the stack is not empty, we first access the element which is pointed by
the top
22
This algorithm deletes the top element of STK and assigns it to the variable ITEM.
Step 1. [Stack has an item to be removed?]
If TOP = 0 then
Print “Stack Underflow”
Return.
[End of step 1 IF structure]
Step 2. ITEM = STK[TOP] [Assigns TOP element to ITEM]
Step 3. TOP = TOP – 1 [Decreases TOP by 1]
Step 4. Return
#include<iostream.h>
#include<conio.h>
#include<process.h>
#define STACKSIZE 5
class Stack
{
private:
int stk[STACKSIZE],top;
public:
Stack()
{
top=-1;
}
void push(int);
int pop();
void display();
};
void Stack::push(int item)
{
if(top==STACKSIZE-1)
{
cout<<"stack overflow";
return;
}
stk[++top]=item;
}
int Stack::pop()
24
{
if(top==-1)
{
cout<<"stack underflow";
return -1;
}
return stk[top--];
}
void Stack::display()
{
int i;
if(top==-1)
{
cout<<"stack underflow";
return;
}
cout<<"Stack elements"<<endl;
for(i=top;i>=0;i--)
{
cout<<stk[i]<<"\t";
}
}
int main()
{
int ch,item;
Stack s;
clrscr();
for(;;)
{
cout<<endl<<"Stack operations"<<endl;
cout<<"1.PUSH"<<endl;
cout<<"2.POP"<<endl;
cout<<"3.DISPLAY"<<endl;
cout<<"4.EXIT"<<endl;
cout<<"Enter your choice"<<endl;
cin>>ch;
switch(ch)
{
case 1:cout<<"Enter item : ";
cin>>item;
s.push(item);
25
break;
case 2:item=s.pop();
if(item!=-1)
cout<<"Popped item is "<<item;
break;
case 3:s.display();
break;
default:exit(0);
}
}
}
Applications of Stack
1. Conversion of expressions
Mathematical expressions in a program are written in infix form. These
expressions will be converted into equivalent machine instructions by the compiler
using stacks. An infix expression can be converted to prefix or postfix expression
using stacks.
2. Evaluation of expressions
An arithmetic expression represented in the form of either prefix, or postfix
expression can be easily evaluated.
3. Recursion
A function which calls itself is called a recursive function. Solutions to problems
such as tower of Hanoi, tree manipulations etc. can be implemented very efficiently
using recursion.
Conversion of Expressions
The sequence of operators and operands arranged as per the syntax of the language is
called an expression. An arithmetic expression can be represented in one of the
following ways.
1. Infix expression
2. Prefix expression
3. Postfix expression
Infix expression: The most common expression is infix expression. In an infix
expression, the operator symbol is placed between its two operands.
Ex: A + B C – D
26
Prefix expression refers to the notation in which the operator symbol is placed
before its two operands. Prefix expression is also known as Polish expression named
after the Polish mathematician Jan Lukasiewicz.
Ex: +AB -CD
Postfix expression refers to the notation in which the operator symbol is placed
after its two operands. Postfix expression is also known as reverse Polish expression.
Ex: AB+ CD-
(T4 + F)
27
T4F+
B*C has multiplication operator, having more precedence than + convert B*C to BC*.
Now the expression becomes
A+BC*
(AB+)/(CD-)
In normal algebra we use the infix notation like a+b*c. The corresponding postfix
notation is abc*+. The algorithm for the conversion is as follows :
stack. Repeat this step as long as stack is not empty and topStack has
precedence over the character.
▪ (After all characters are scanned, we have to add any character that the
stack may have to the Postfix string.) If stack is not empty add topStack to
Postfix string and Pop the stack. Repeat this step as long as stack is not
empty.
▪ Return the Postfix string.
Example :
Let us see how the above algorithm will be implemented using an example.
Initially the Stack is empty and our Postfix string has no characters. Now, the first
character scanned is 'a'. 'a' is added to the Postfix string. The next character scanned is
'+'. It being an operator, it is pushed to the stack.
Postfix String
Stack
Next character scanned is 'b' which will be placed in the Postfix string. Next character is
'*' which is an operator. Now, the top element of the stack is '+' which has lower
precedence than '*', so '*' will be pushed to the stack.
Postfix String
Stack
29
The next character is 'c' which is placed in the Postfix string. Next character scanned is
'-'. The topmost character in the stack is '*' which has a higher precedence than '-'. Thus
'*' will be popped out from the stack and added to the Postfix string. Even now the stack
is not empty. Now the topmost element of the stack is '+' which has equal priority to '-'.
So pop the '+' from the stack and add it to the Postfix string. The '-' will be pushed to the
stack.
Postfix String
Stack
Next character is 'd' which is added to Postfix string. Now all characters have been
scanned so we must pop the remaining elements from the stack and add it to the Postfix
string. At this stage we have only a '-' in the stack. It is popped out and added to the
Postfix string. So, after all characters are scanned, this is how the stack and Postfix
string will be :
Postfix String
Stack
End result :
Initially the Stack is empty and our Postfix string has no characters. Now, the first
character scanned is '('. '(' is added to the stack. The next character scanned is 'A'. It
being an operator, it is pushed to the postfix string.
30
Postfix string
(
Stack
+
Postfix string
(
Stack
AB
+
Postfix string
(
Stack
The next character is ‘)’, so pop all the elements until you get left parentheses.
AB+
Postfix string
Stack
31
AB+
Postfix string
/
Stack
AB+
(
/ Postfix string
Stack
AB+C
/ Postfix String
Stack
The next character is -, the scanned character is having less precedence than the
character at the top of the stack, so push it to stack.
AB+C
-
/ Postfix String
Stack
32
AB+CD
-
Postfix String
/
Stack
The next character is ‘)’, so pop all the operators from the stack and add to the postfix
string, and the stack is empty.
AB+CD-/
Postfix String
Stack
The precedence values of symbols in the stack and input expression are given in the
table below.
#include<iostream.h>
#include<conio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
float compute(char symbol,float op1,float op2)
{
34
switch(symbol)
{
case '+':return op1+op2;
case '-':return op1-op2;
case '*':return op1*op2;
case '/':return op1/op2;
case '^':return pow(op1,op2);
}
}
int main()
{
float s[20],op1,op2,res;
int top=-1,i,len;
char postfix[30],symbol;
clrscr();
cout<<"enter a postfix expression:"<<endl;
cin>>postfix;
len=strlen(postfix);
for(i=0;i<len;i++)
{
symbol=postfix[i];
if(isdigit(symbol))
{
s[++top]=symbol-'0';
}
else
{
op2=s[top--];
op1=s[top--];
res=compute(symbol,op1,op2);
s[++top]=res;
}
}
res=s[top];
cout<<"result="<<res<<endl;
getch();
return 0;
35
}
Recursion
5! =5 * 4!
=5*4*3!
=5*4*3*2!
=5*4*3*2*1!
=5*4*3*2*1*0!
=120
In general the solution for finding the factorial can be written as,
36
/* C++ program to find the factorial of a given number using recursive function */
#include<iostream.h>
#include<conio.h>
int main()
{
int fact(int);
int n;
cout<<“Enter a number : ”;
cin>>n;
cout<<“Factorial of the number is ”<<fact(n); // Function call
getch();
return 0;
}
int fact(int n)
{
if (n==0) //base case
{
return 1;
}
return n*fact(n-1); //recursive case
}
#include<iostream.h>
37
#include<conio.h>
int sum(int);
int main()
{
int n;
cout<<“Enter the number : ”;
cin>>n;
cout<<“Sum of numbers from 1 to N is”<<sum(n); // Function call
getch();
return 0;
}
int sum(int m)
{
if (m==1) //base case
return 1;
return m+sum(m-1); //recursive case
}
Nesting of Functions
#include<stdio.h>
#include<conio.h>
int GCD(int,int); //Function prototype of GCD
int LCM(int ,int);
int main()
{
int a,b,lcm;
clrscr();
cout<<“Enter the two numbers : ”;
cin>>a>>b;
lcm=LCM(a,b); //Function call
cout<<“The LCM of 2 numbers is ”<<lcm;
getch();
return 0;
}
//Function definition
38
{
int gcd;
while(m!=n)
{
if (m>n)
m=m-n;
else
n=n-m;
}
gcd=m;
return(gcd);
}
In this example the main program calls the function LCM with two arguments a and b
which are taken by x and y in LCM().
The LCM( ) function in turn requires GCD of two numbers to find lcm.So it calls GCD(
) function with arguments x and y which are taken as m and n by GCD( ) .
GCD() calculates gcd and sends it to LCM() .LCM() used the value to calculate lcm
and returns it to main().
This process of a sub function calling another sub function is called nesting of
functions.
Fibonacci sequence
The Fibonacci sequence (usually denoted by F0, F1, F2, F3, … ) is as follows
0, 1, 1, 2, 3, 5, 8, 11, 13, …
That is F0 = 0 and F1= 1 and each succeeding term is the sum of the two preceding
terms. A formal definition of this function is as follows:
39
int main()
{
int n;
clrscr();
cout<<“Enter n : “;
cin>>n;
cout<<“fib(“<<n<<”) =”<<fib(n);
getch();
return 0;
}
Towers of Hanoi
Suppose three pegs, labeled A, B, and C, are given, and suppose on peg A there are
placed a finite number of disks say n with decreasing size. The object of the game is to
move the disks from peg A to peg C using peg B as auxiliary. The rules of the game are
as follows:
1. Only one disk may be moved at a time. Specifically, only the top disk on any
peg may be moved to any other peg.
2. At no time can a larger disk be placed on a smaller disk.
Return
[End of step 1 IF structure]
2. [Move N - 1 disks from peg BEG to peg AUX]
call Tower(N – 1, BEG, END, AUX)
3. Write: Move disk from BEG to END
4. [Move N - 1 disks from peg AUX to peg END]
call Tower(N – 1, AUX, BEG, END
5. Return
One can view the solution as a divide-and-conquer algorithm, since the solution for n
disks is reduced to a solution for n – 1 disks and a solution for n = 1 disk.
#include<iostream.h>
#include<conio.h>
{
if(n == 1)
{
cout<<"Move disk from "<<BEG<<" to "<<END<<endl;
return;
}
/* Move n-1 disks from BEG to AUX */
tower(n-1, BEG, END, AUX);
int main()
{
int n;
clrscr();
cout<<"Enter the number of disks : ";
cin>>n;
tower(n,'A','B','C');
getch();
return 0;
}
QUEUES
A queue is a linear list of elements in which deletions can take place at one end, called
the front, and insertions can take place only at the other end, called rear. Queues are
also called first-in first-out (FIFO) lists, since the first element in a queue will be the
first element out of the queue.
Queues may be represented in the computer memory in various ways, usually by means
of one-way lists or linear arrays. Figure below shows the array representation of queues.
1 2 3 4 5
FRONT: 1 10 20 30
REAR: 3
QSIZE: 5 QUEUE
QUEUE is a linear array and FRONT and REAR are variables containing the location
of the front element and rear element of the queue. QSIZE specifies the maximum
42
number of elements that can be stored in the queue. The condition FRONT = NULL
will indicate that the queue is empty. In the above queue, there are three elements. The
position of the first element is 1 indicated by FRONT=1 and the position of the last
element is 3 indicated by REAR=3.
Whenever an element is deleted from the queue, the FRONT is increased by 1. This can
be implemented by the assignment
FRONT=FRONT + 1
Similarly, whenever an element is added to the queue, the value of REAR is increased
by 1. This can be implemented by the assignment
REAR = REAR + 1
This means that after QSIZE insertions, the rear element of the queue will occupy
QUEUE[QSIZE].
Suppose that our queue contains only one element, i.e., suppose that
FRONT = REAR
Types of Queues
Operations on Queues
In an ordinary queue, deletions can take place at one end, called the front, and
insertions can take place only at the other end, called rear.
Here Q is a queue consisting of QSIZE elements. FRONT and REAR are pointers
pointing to the front and rear elements of a queue. This algorithm inserts ITEM at the
rear end of the queue.
Here Q is a queue consisting of N elements. FRONT and REAR are pointers pointing to
the front and rear elements of the queue. This algorithm deletes an item at the rear end
and assigns it to ITEM.
1. [Queue underflow/]
If FRONT = -1 Then
Write “Queue underflow”
Return
[End of If]
44
#include<iostream.h>
#include<conio.h>
#include<process.h>
#define QSIZE 5
class Queue
{
private:
int q[QSIZE],front,rear;
public:
Queue()
{
front=-1;
rear=-1;
}
{
front=0;
rear=0;
}
else
{
rear++;
}
q[rear]=item;
}
int Queue::qDeleteFront()
{
int item;
if(front == -1)
{
return -1
}
item=q[front];
if(front==rear)
{
front=0;
rear=-0;
}
else
{
front++;
}
return item;
}
void Queue::qDisplay()
{
int i;
if(front==-1)
{
cout<<"Queue underflow"<<endl;
exit(1);;
}
cout<<endl<<"Queue elements"<<endl;
for(i=front;i<=rear;i++)
{
cout<<q[i]<<"\t";
46
}
}
int main()
{
int item,ch;
Queue q1;
clrscr();
while(1)
{
cout<<endl<<"Queue operations"<<endl;
cout<<"1.Insert"<<endl;
cout<<"2.Delete"<<endl;
cout<<"3.Display"<<endl;
cout<<"4.Exit"<<endl;
cout<<"Enter your choice : ";
cin>>ch;
switch(ch)
{
case 1: cout<<"Enter item : ";
cin>>item;
q1.qInsertRear(item);
break;
case 2: item=q1.qDeleteFront();
if(item==-1)
cout<<"Queue underflow"<<endl;
else
cout<<"Deleted item is "<<item;
break;
case 3: q1.qDisplay();
break;
case 4: exit(0);
}
}
}
30 40 50
f r
In the above queue, the rear pointer ‘r’ points to the last element in the queue.
Though, free memory is available at the front end of the queue, insertion into the queue
47
is denied because the value of rear pointer ‘r’ is equal to QSIZE (Queue size). We
cannot access the free memory available at the front end of the queue. This is a
disadvantage. This can be overcome by shifting the elements to the front end of the
queue. But shifting the elements consumes processor time and is not recommended.
This disadvantage can be overcome by implementing circular queue using an array.
Circular Queues
A circular queue implemented using array makes efficient use of memory. In a circular
queue, the rear end of the queue may be followed by the front end of the queue. The
pictorial representation of a circular queue and its equivalent representation using an
array are given bellow.
In the figure above, the circular queue contains only one element. So, f = r = 1. We
know that elements are inserted only at the rear end and deleted at the front end. So,
before insertion, the value of the r should be 0. So, initially when queue is empty, f = 0,
r = 0.
Figure below shows queue after inserting 20, 30, 40, and 50 into the queue above.
48
Figure below shows queue after deleting first two elements, 10 and 20.
49
Suppose we want to insert an element ITEM into a queue at the time the time the queue
does occupy the last part of the array, ie when r = QSIZE then, since the queue is
circular, Q[1] comes after Q[N] in the array. So we insert ITEM into the queue by
assigning ITEM to Q[1]. Specifically, instead of increasing r to N + 1, we reset r = 1
and then assign
Q[r] = ITEM
Suppose that our queue contains only one element, i.e suppose that f = r ≠ 0
f = 0 and r = 0
Figure below shows queue after inserting 60 into the above queue.
Note: To implement a circular queue, we will use a variable called count that contains
the number of elements in the queue at a given instant. The variable count is
incremented by 1 after inserting an element, and decremented by 1 after deleting an
element from the circular queue.
This algorithm inserts ITEM at the rear end of the circular queue.
This algorithm deletes an element from the front end of the queue and assigns it to
ITEM.
public:
CirQueue()
{
front=-1;
rear=-1;
}
void cqInsertRear(int item);
int cqDeleteFront();
void cqDisplay();
};
void CirQueue::cqInsertRear(int item)
{
if((front == 0 && rear == QSIZE - 1) || (front == rear+1))
{
cout<<"Queue overflow"<<endl;
exit(1);
}
if(front == -1)
{
front=0;
rear=0;
}
else if (rear == QSIZE-1)
{
rear = 0;
}
else
{
rear++;
}
q[rear]=item;
}
int CirQueue::cqDeleteFront()
{
int item;
if(front == -1)
{
cout<<"Queue underflow"<<endl;
exit(1);
}
item=q[front];
if(front==rear)
{
53
front=-1;
rear=-1;
}
else if (front == QSIZE-1)
{
front = 0;
}
else
{
front++;
}
return item;
}
void CirQueue::cqDisplay()
{
int i;
if(front==-1)
{
cout<<"Queue underflow"<<endl;
exit(1);
}
cout<<"Queue elements"<<endl;
if(front<=rear)
{
for(i=front;i<=rear;i++)
{
cout<<q[i]<<"\t";
}
}
else
{
for(i=front;i<QSIZE;i++)
{
cout<<q[i]<<"\t";
}
for(i=0;i<=rear;i++)
{
cout<<q[i];
}
}
}
int main()
54
{
int item,ch;
CirQueue q1;
clrscr();
while(1)
{
cout<<endl<<"Circular Queue operations"<<endl;
cout<<"1.Insert"<<endl;
cout<<"2.Delete"<<endl;
cout<<"3.Display"<<endl;
cout<<"4.Exit"<<endl;
cout<<"Enter your choice : ";
cin>>ch;
switch(ch)
{
case 1: cout<<"Enter item : ";
cin>>item;
q1.cqInsertRear(item);
break;
case 2: item=q1.cqDeleteFront();
cout<<"Deleted item is "<<item;
break;
case 3: q1.cqDisplay();
break;
case 4: exit(0);
}
}
}
A deque is a linear list in which the elements can be added and deleted from both ends
of the queue.
Operations on deques
There are two variations of a deque. – namely, an input-restricted deque and out-put
restricted deque. These deques are intermediate between a deque and a queue. An input-
restricted deque is a deque which allows insertions at only one end of the list but allows
deletions at both the ends of the queue. An output-restricted queue allows deletions at
only one end of the queue but allows insertions at both ends of the queue.
We have already discussed insertion at the rear end, deletion from the front end and
display operations of ordinary queues the same algorithms and C functions holds good
for deques also. We shall now discus the other two operations.
Priority Queues
A priority queue is a collection of elements such that each element has been assigned a
priority and such that the order in which elements are deleted and processed follow the
rules given below: