0% found this document useful (0 votes)
2 views

Data Structures Notes

The document covers algorithm analysis, including definitions, properties, and performance analysis through time and space complexity. It details types of time complexities, asymptotic notations, and various data structures, including linear and non-linear types. Additionally, it discusses abstract data types and provides examples of implementing lists using arrays and linked lists.

Uploaded by

adars251
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

Data Structures Notes

The document covers algorithm analysis, including definitions, properties, and performance analysis through time and space complexity. It details types of time complexities, asymptotic notations, and various data structures, including linear and non-linear types. Additionally, it discusses abstract data types and provides examples of implementing lists using arrays and linked lists.

Uploaded by

adars251
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 452

1

UNIT 1:-Algorithm Analysis:-

Mathematical Background, Model, Analysis and Run Time Calculations, Lists: Abstract

Data Types, List using arrays and pointers, Singly Linked, Doubly Linked, Circular Linked

Lists, Polynomial ADT.

Algorithm Definition: - An algorithm is a set of instructions that are used to solve a


problem.

Properties of an algorithm:-

1. Input

2. Output

3. Finiteness

4. Definiteness

5. Effectiveness

1. Input: - An algorithm has zero (0) or more inputs.

2. Output: - An algorithm must produce one (1) or more outputs.

3. Finiteness:-An algorithm must contain a finite number of steps.

4. Definiteness: - Each step of an algorithm must be clear and unambiguous.

5. Effectiveness: - An algorithm should be effective i.e. operations can be performed with the
given inputs in a finite period of time by a person using paper and pencil.

Performance Analysis (or) Analysis of an algorithm:-

Analysis of algorithm is the task of determining how much computing time (Time
complexity) and storage (Space complexity) required by an algorithm.

We can analyze performance of an algorithm using Time complexity and Space complexity.

Time Complexity: - The amount of time that an algorithm requires for its execution is
known as time complexity.
Time complexity is mainly classified into 3 types.
1. Best case time complexity
2. Worst case time complexity
3. Average case time complexity
1. Best case time complexity: - If an algorithm takes minimum amount of time for its
execution then it is called as Best case time complexity.
2

Ex: - While searching an element using linear search, if key element found at first position
then it is best case.

2. Worst case time complexity: - If an algorithm takes maximum amount of time for its
execution then it is called as Worst case time complexity.

Ex: - While searching an element using linear search, if key element found at last position
then it is worst case.

3. Average case time complexity: - If an algorithm takes average amount of time for its
execution then it is called as Average case time complexity.

Ex: - While searching an element using linear search, if key element found at middle position
then it is average case.

There are 2 types of computing times.


1. Compilation time
2. Execution time or run time
 The time complexity is generally calculated using execution time or run time.
 It is difficult to calculate time complexity using clock time because in a multiuser
system, the execution time depends on many factors such as system load, number of
other programs that are running.
 So, time complexity is generally calculated using Frequency count (step count) and
asymptotic notations.

Frequency count (or) Step count:-


It denotes the number of times a statement to be executed.
Generally count value will be given depending upon corresponding statements.
 For comments, declarations the frequency count is zero (0).
 For Assignments, return statements the frequency count is 1.
 Ignore lower order exponents when higher order exponents are present.
 Ignore constant multipliers.

Ex: - A sample program to calculate time complexity of sum of cubes of ‘n’ natural numbers.

int sum(int n) --------------------0


{ --------------------0
int i,s; --------------------0
s=0; --------------------1
for(i=1;i<=n;i++) --------------------(n+1)
s=s+i*i*i; --------------------n
return s; --------------------1
}
______
2n+3
______
So, time complexity=O(n)
3

General rules or norms for calculating time complexity:-

Rule 1:-The running time of for loop is the running time of statements in for loop.
Ex:- for(i=0;i<n;i++) -----------(n+1)
s=s+i; ------------ n
__________
2n+1
__________
So, time complexity=O(n)

Rule 2:-The total running time of statements inside a group of nested loops is the product of
the sizes of all loops.
Ex:- for(i=0;i<n;i++) ---------------- (n+1)
for(j=0;j<n;j++) ---------------- n(n+1)
c[i][j]=a[i][j]+b[i][j]; ------------- (n*n)
_____________
2n2+2n+1
_____________
So, time complexity=O(n2)

Rule 3:-The running time is the maximum one.


Ex:- for(i=0;i<n;i++) ----------- (n+1)
a[i]=0; ----------- n
for(i=0;i<n;i++) ------------ (n+1)
for(j=0;j<n;j++) ------------ n(n+1)
a[i]=a[i]+a[j]+i+j; ------------- n2
____________
2n2+4n+2
____________
So, time complexity=O(n2)

Rule 4:- if-else


if(cond)
s1
else
s2
Here running time is maximum of running times of s1 and s2.
Ex:- int sum(int n) -------------- 0
{ --------------- 0
int s,i; -------------- 0
s=0; -------------- 1
if(n<=0) -------------- 1
return n; ------------- 1
else ------------- 0
{ ------------- 0
for(i=0;i<n;i++) -------- n+1
4

s=s+i; ---------- n
return s; ----------- 1
}
}
____________
2n+5
__________________
So, time complexity=O(n)

Asymptotic notations:-
Asymptotic notations are used to calculate time complexity of algorithm.
Using asymptotic notations we can calculate best case, worst case and average case time
complexity.
There are 5 types of time complexities.
1. Big Oh notation (O)
2. Big Omega notation (Ω)
3. Theta notation (θ)
4. Little Oh notation (o)
5. Little omega notation (ω)

1. Big Oh notation (O):-


 It is a method of representing upper bound of algorithm’s running time.
 Using Big Oh notation we can calculate maximum amount of time taken by an
algorithm for its execution.
 So, Big Oh notation is useful to calculate worst case time complexity.

Definition: - Let f(n) , g(n) be 2 non negative functions. Then f(n)=O(g(n)), if there are 2
positive constants c, n0 such that f(n)<=c*g(n) ∀ n>=n0.

Graphical representation of Big Oh notation(O):-

Ex:- f(n)=3n+2
g(n)=n;
To show f(n)=O(g(n))
f(n)<=c*g(n) , c>0, n0>=1
5

3n+2<=c*n
Let C=4, n0=1 then 5<=4 wrong
n0=2 then 8<=8 correct
n0=3 then 11<=12 correct
n0=4 then 14<=16 correct
So, n0>=2
Hence, 3n+2<=4*n ∀ n>=2

2. Big Omega notation (Ω):-

 It is a method of representing lower bound of algorithm’s running time.


 Using Big Omega notation we can calculate minimum amount of time taken by an
algorithm for its execution.
 So, Big Omega notation is useful to calculate best case time complexity.

Definition: - Let f(n) , g(n) be 2 non negative functions. Then f(n)= Ω (g(n)), if there are 2
positive constants c, n0 such that f(n)>=c*g(n) ∀ n>=n0.

Graphical representation of Big Omega notation(Ω)

f(n)= Ω (g(n))

Ex:- f(n)=3n+2
g(n)=n;
To show f(n)= Ω(g(n))
f(n)>=c*g(n) , c>0, n0>=1
3n+2>=c*n
6

Let C=1, n0=1 then 5>=1 correct


n0=2 then 8>=2 correct
n0=3 then 11>=3 correct

So, n0>=1
Hence, 3n+2>=1*n ∀ n>=1

3. Theta notation (θ):-

 It is a method of representing an algorithm’s running time between upper bound and


lower bound.
 Using Theta notation we can calculate average amount of time taken by an algorithm
for its execution.
 So, Theta notation is useful to calculate average case time complexity.

Definition: - Let f(n) , g(n) be 2 non negative functions. Then f(n)= θ (g(n)), if there are 3
positive constants c1, c2,n0 such that c1*g(n)<=f(n)<=c2*g(n) ∀ n>=n0.

Graphical representation of Theta notation(O):-

Ex:- f(n)=3n+2
g(n)=n;
To show f(n)= θ(g(n))
c1*g(n)<=f(n)<=c2*g(n), c1>0,
c2>0
n0>=1
c1*n<=3n+2<= c2*n
c2=4 , c1=1,
Let n0=1 then 1<=5<=4 wrong
Let n0=2 then 2<=8<=8 correct
Let n0=3 then 3<=11<=12 correct
Let n0=4 then 4<=14<=16 correct
So, n0>=2
Hence, 1*n<=3n+2<=*4n n>=2

4. Little oh notation(o):-
7

Let f(n), g(n) be 2 non negative functions then f(n)=o(g(n)) such that

5. Little Omega notation(ω):-


Let f(n), g(n) be 2 non negative functions then f(n)=o(g(n)) such that

Various types of computing times or typical growth rates:-

O(1)  constant computing time


O(n)  linear computing time
O(n2) Quadratic computing time
O(n3) Cubic computing time
O(2n) Exponential computing time
O(log n)  Logarithmic computing time
O(n log n) Logarithmic computing time

N n2 n3 2n logn nlogn

1 1 1 2 0 0

2 4 8 4 1 2

4 16 64 16 2 8

8 64 512 256 3 24

The relation among various computing times is,


O(logn)<O(n)<O(nlogn)<O(n2)<O(2n)<O(n3)

Space complexity:- The amount of space required by an algorithm for its execution.
Space complexity S(P) can be calculated as S(P)=C+SP
Where, S(P) is space complexity of a program or problem
C means constant or fixed part
SP means variable part
Constant part denotes memory (space) required by variables.
Variable part depends on problem instance.

Definitions of a Data Structure:-

Data Structure is a way of organizing data in a computer so that we can perform


operations on these data in an effective way.

Data structure is a data organization, management & storage format that enables
efficient access & modification.
8

A Data structure is a particular way of storing & organizing data in a computer


memory so that it can be used efficiently.

To develop a program we should select an appropriate data structure.

Classification of Data Structures:

Types of Data Structures:- Data Structures are mainly classified into 2 types.

1. Linear Data Structures

2. Non Linear Data Structures

1.Linear Data Structures:- In linear data structures all the elements are organized in linear
(sequential) fashion.
9

Eg:- Arrays, stacks, queues and linked list.

2.Non Linear Data Structures:- In Non linear data structures all the elements are organized
in non linear (random) fashion.

Eg:- Trees, Graphs.

Abstract Data Types:-

An abstract data type (ADT) is a set of operations. An ADT specifies what is to be


done but how it is done is not specified i.e. all the implementation details are hidden.

The basic idea is that the implementation of these operations is written once in the
program and any other part of the program that needs to perform an operation on the ADT
can do so by calling the appropriate function.

For Example, stack ADT have operations like push, pop but stack ADT doesn’t
provide any implementation details regarding operations.

List ADT:- List is basically a collection of elements. For example list is of the following
form

A1,A2,A3,- - - - -,An where A1 is the first element of the list

An is the last element of the list.

The size of the list is ‘n’. If the list size is zero (0) then the corresponding list is called as
empty list.

Implementation of list:- List is implemented in 2 ways.

1. Implementation of list using arrays

2. Implementation of list using Linked list (or) Implementation of list using pointers

1.Implementation of list using arrays:- An array can be defined as a collection of


similar( homogeneous ) data type elements and all the elements will be stored in
contiguous ( Adjacent ) memory locations.

Array Operations:- We can perform mainly the following operations on arrays.

1. Create

2. Display (or) Traversing (or) Printing

3. Insertion

4. Deletion
10

5. Updation

6. Searching

7. Sorting

8. Merging

C program to implement various operations on list using Arrays:-

#include<stdio.h>
#include<conio.h>
int n, a[20];
void create();
int insert(int,int);
void display();
void find(int);
int del(int);
void update(int,int);
void count();
int main()
{
int choi,ele,pos,val;
clrscr();
printf("enter no ele in list \n");
scanf("%d",&n);
while(1)
{
clrscr();
printf("\t\t List Adt using arrays \n");
printf("1.create \n");
printf("2.insert \n");
printf("3.display \n");
printf("4.find \n");
printf("5.delete \n");
printf("6.update\n");
printf("7.count\n");
printf("8.exit \n");
11

printf("enter choice \n");


scanf("%d",&choi);
switch(choi)
{
case 1:create();
break;
case 2:printf("enter at what position u want to enter\n");
scanf("%d",&pos);
printf("enter at value u want to insert\n");
scanf("%d",&val);
n=insert(pos,val);
break;
case 3:display();
break;
case 4:printf("enter ele to be searched\n");
scanf("%d",&val);
find(val);
break;

case 5:printf("enter at what position u want to enter\n");


scanf("%d",&pos);
n=del(pos);
break;
case 6:printf("enter pos to upadte\n");
scanf("%d",&pos);
printf("enter value to upadate\n");
scanf("%d",&val);
update(pos,val);
break;
case 7:count();
break;

case 8:exit(0);
break;
}
}

}
void create()
{
int i;
for(i=0;i<n;i++)
12

{
printf("enter ele of list\n");
scanf("%d",&a[i]);
}
}
int insert(int pos,int val)
{
int i;
for(i=n-1;i>=pos;i--)
a[i+1]=a[i];
a[pos]=val;
return n+1;
}
void display()
{
int i;
for(i=0;i<n;i++)
printf("\nele of list are =%d",a[i]);
getch();
}
int del(int pos)
{
int i;
for(i=pos;i<n-1;i++)
{
a[i]=a[i+1];
}
return n-1;
}
void find(int val)
{
int i,flag=0;
for(i=0;i<n;i++)
{
if(val==a[i])
{
flag=1;
break;
}
}
if(flag==1)
{
13

printf("ele found in list at=%d",i);


getch();
}
else
printf("ele not found");

}
void update(int pos,int val)
{
a[pos]=val;
}
void count()
{
printf("no of ele in the list =%d",n);
getch();
}

Advantages of Arrays:-

1. Array conations a collection of similar data type elements.

2. Array allows random access of elements i.e. an array element can be randomly
accessed using index value. Arrays are simple and easy to implement.

3. Arrays are suitable when the number of elements are already known.

4. Arrays can be used to implement data structures like stacks, queues and so on.

Drawbacks of Arrays:-

1. We must know in advance regarding how many elements are to be stored in array.

2. Arrays uses static memory allocation i.e. memory will be allocated at compilation
time. So it’s not possible to change size of the array at run time.

3. Since array size is fixed, if we allocate more memory than required then memory
space will be wasted.

4. If we allocate less memory than required it will creates a problem.

5. The main drawback of arrays is insertion and deletion operations are expensive.
14

6. For example to insert an element into the array at beginning position, we need to shift
all array elements one position to the right in order to store a new element at
beginning position.

a[0] a[1] a[2] a[3] a[4]

11 22 33 44 55 Let value=100

a[0] a[1] a[2] a[3] a[4] a[5]

100 11 22 33 44 55

7. For example to delete an element from the array at beginning position , we need to
shift all array elements one position to its left.

a[0] a[1] a[2] a[3] a[4]

11 22 33 44 55

a[0] a[1] a[2] a[3]

22 33 44 55

Because of the above limitations simple arrays are not used to implement lists.

2. Implementation of list using Linked list (or) Implementation of list using pointers:-

Linked list is a collection of nodes which are not necessary to be in adjacent memory
locations.

Each node contains 2 fields.

1. Data Field

2. Next field ( or ) pointer field ( or ) address field

Data field:- Data field contains values like 9, 6.8, ‘a’ , “ramu” , 9849984900

Next field:- It contains address of its next node. The last node next field contains NULL
which indicates end of the linked list.

Diagrammatic representation of linked list:-

10 2000 30 NULL
15

20 3000

1000 2000 3000

Types of linked list:-

1. Single linked list (or) singly linked list

2. Circular linked list (or) circular single linked list

3. Double linked list

4. Circular double linked list

1. Single linked list (or) singly linked list:-

A single linked list is a collection of nodes which are not necessary to be in adjacent memory
locations.

Each node contains 2 fields.

1. Data Field

2. Next field ( or ) pointer field ( or ) address field

Data field:- Data field contains values like 9, 6.8, ‘a’ , “ramu” , 9849984900

Next field:- It contains address of its next node. The last node next field contains NULL
which indicates end of the linked list.

Diagrammatic representation of linked list:-

10 2000 20 3000 30 NULL

1000 2000 3000

It is called as single linked list because each node contains a single link which points to its
next node.

Single Linked list operations:-

We can perform mainly the following operations on single linked list.

1. Creation

2. Display

3. Inserting an element into the list at begin position

4. Inserting an element into the list at end position


16

5. Inserting an element into the list at specified position

6. Deleting an element from the list at begin position

7. Deleting an element from the list at end position

8. Deleting an element from the list at specified position

9. Counting the number of elements or nodes

10. Sorting a list

11. Reversing a list

12. Merging of 2 lists

C program to implement various operations on list using Linked list:- ( OR )

C program to implement various operations on list using Pointers:-

#include<stdio.h>
#include<stdlib.h>
struct node
{
int info;
struct node *next;
};
struct node *start;
void insend(int ele);
void insbeg(int ele);
void insmiddle(int ele);
void delend();
void delbeg();
void delmiddle();
void traverse();
void main()
{
int ele,choice;
start=NULL;
while(1)
{
clrscr();
printf("\t\t Single List Operations \n");
printf("1.insbeg\n");
printf("2.insend\n");
printf("3.insmiddle\n");
printf("4.delbeg\n");
printf("5.delend\n");
printf("6.traverse\n");
printf("7.delmiddle\n");
17

printf("8.exit\n");
printf("enter choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1: printf("\n enetr ele ");
scanf("%d",&ele);
insbeg(ele);
break;
case 2:printf("\n enetr ele ");
scanf("%d",&ele);
insend(ele);
break;
case 3:printf("\n enetr ele ");
scanf("%d",&ele);
insmiddle(ele);
break;
case 4:delbeg();
break;
case 5:delend();
break;
case 6:traverse();
break;
case 7:delmiddle();
break;
case 8:exit(0);
}
}
}
void insbeg(int ele)
{
struct node *temp;
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
if(start==NULL)
{
temp->next=NULL;
start=temp;
}
else if(start->next==NULL)
{
temp->next=start;
start=temp;
}
else
{
temp->next=start;
start=temp;
}
18

}
void insmiddle(int ele)
{
struct node *temp,*p;int pos,count=0;
if(start==NULL)
{
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
temp->next=NULL;
start=temp;
}
else
{
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
printf("\n enter position");
scanf("%d",&pos);
p=start;
while(p!=NULL)
{
if(pos==count)
break;
count=count+1;
p=p->next;
}
temp->next=p->next;
p->next=temp;
}

}
void insend(int ele)
{
struct node *temp,*p;
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
temp->next=NULL;
if(start==NULL)
{
start=temp;
}
else if(start->next==NULL)
{
start->next=temp;
}
else
{
p=start;
while(p->next!=NULL)
{
19

p=p->next;
}
p->next=temp;
}
}
void delbeg()
{
struct node *temp;
if(start==NULL)
printf("\n list is empty");
else if(start->next==NULL)
{
temp=start;
start=NULL;
free(temp);
}
else
{
temp=start;
start=start->next;
free(temp);
}
}
void delend()
{
struct node *temp,*p;
if(start==NULL)
printf("\n list is empty");
else if(start->next==NULL)
{
temp=start;
start=NULL;
free(temp);
}
else
{
p=start;
while(p->next->next!=NULL)
{
p=p->next;
}
temp=p->next;
p->next=NULL;
free(temp);
}
}
void traverse()
{
struct node *p;
20

if(start==NULL)
printf("\n list is empty");
else
{
p=start;
printf("Elements of Single Linked List are\n\n");
while(p!=NULL)
{
printf("%d->",p->info);
p=p->next;
}
}
getch();
}
void delmiddle()
{
}

Advantages of linked list:- Or Advantages of Single linked list

1. Linked list is a dynamic data structure i.e. memory will be allocated at run time. So,
no wastage of memory.
2. It is not necessary to know in advance regarding the number of elements to be stored
in the list.
3. Insertion and deletion operations are easier, as shifting of values is not necessary.
4. Different types of data can be stored in data field of a node.
5. The memory allocation need not be in contiguous locations.
6. Linear data structures such as stacks, queues are easily implemented using linked list.

Disadvantages or limitations of single linked list:-

1. Random access of elements in not possible in single linked list i.e. we can’t access a
particular node directly.
2. Binary search algorithm can’t be implemented on a single linked list.
3. There is no way to go back from one node to its previous node i.e. only forward
traversal is possible.
4. Extra storage space for pointer is required.
5. Reversing single linked list is difficult.

Circular linked list:-


Circular linked list is a linear data structure which contains a collection
of nodes where each node contains 2 fields.
1. Data field:- It contains a value which may be an integer, float, char and string.
2. Next field:- It contains address of its next node. The last node next field contains address
of first node.
21

Diagrammatic representation of circular linked list:-

Circular Linked list operations:- or Circular Linked list ADT

We can perform mainly the following operations on circular linked list.

1. Creation

2. Display

3. Inserting an element into the list at begin position

4. Inserting an element into the list at end position

5. Inserting an element into the list at specified position

6. Deleting an element from the list at begin position

7. Deleting an element from the list at end position

8. Deleting an element from the list at specified position

9. Counting the number of elements or nodes

C program to implement circular linked list:-

#include<stdio.h>
22

#include<stdlib.h>
struct node
{
int info;
struct node *next;
};
struct node *start;
void insend(int ele);
void insbeg(int ele);
void insmiddle(int ele);
void delend();
void delbeg();
void delmiddle();
void traverse();
void main()
{
int ele,choice;
start=NULL;
while(1)
{
clrscr();
printf("\t\t Circular Linked List operations\n");
printf("1.insbeg\n");
printf("2.insend\n");
printf("3.insmiddle\n");
printf("4.delbeg\n");
printf("5.delend\n");
printf("6.traverse\n");
printf("7.delmiddle\n");
printf("8.exit\n");
printf("enter choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:printf("\n enter ele ");
scanf("%d",&ele);
insbeg(ele);
break;
case 2:printf("\n enetr ele ");
scanf("%d",&ele);
insend(ele);
break;
case 3:printf("\n enetr ele ");
scanf("%d",&ele);
insmiddle(ele);
break;
case 4:delbeg();
break;
case 5:delend();
23

break;
case 6:traverse();
break;
case 7:delmiddle();
break;
case 8:exit(0);
}
}
}
void insbeg(int ele)
{
struct node *temp,*p;
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
if(start==NULL)
{
start=temp;
start->next=start;
}
else if(start->next==start)
{
temp->next=start;
start->next=temp;
start=temp;
}
else
{
p=start;
while(p->next!=start)
{
p=p->next;
}
p->next=temp;
temp->next=start;
start=temp;
}
}
void insmiddle(int ele)
{
struct node *temp,*p;int pos,count=0;
if(start==NULL)
{
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
start=temp;
temp->next=start;
}
else
{
24

temp=(struct node*)malloc(sizeof(struct node));


temp->info=ele;
printf("\n enter position");
scanf("%d",&pos);
p=start;
do
{
count++;
if(pos==count)
break;
p=p->next;

}while(p!=start);
temp->next=p->next;
p->next=temp;
}
}
void insend(int ele)
{
struct node *temp,*p;
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
if(start==NULL)
{
start=temp;
temp->next=start;
}
else if(start->next==start)
{
start->next=temp;
temp->next=start;
}
else
{
p=start;
while(p->next!=start)
{
p=p->next;
}
p->next=temp;
temp->next=start;
}
}
void delbeg()
{
struct node *temp,*p;
if(start==NULL)
printf("\n list is empty");
else if(start->next==start)
25

{
temp=start;
start=NULL;
free(temp);
}
else
{
p=start;
temp=start;
while(p->next!=start)
{
p=p->next;
}
start=start->next;
p->next=start;
free(temp);
}
}
void delend()
{
struct node *temp,*p;
if(start==NULL)
printf("\n list is empty");
else if(start->next==start)
{
temp=start;
start=NULL;
free(temp);
}
else
{
p=start;
while(p->next->next!=start)
{
p=p->next;
}
temp=p->next;
p->next=start;
free(temp);
}
}
void traverse()
{
struct node *p;
if(start==NULL)
printf("\n list is empty");
else
{
p=start;
26

do
{
printf("\n elements are %d",p->info);
p=p->next;
}while(p!=start);
}
getch();
}
void delmiddle()
{
struct node *temp,*p;
int pos,count=0;
if(start==NULL)
{
printf("\n list is empty");
}
else if(start->next==start)
{
temp=start;
start=NULL;
free(temp);
}
else if(start->next->next==start)
{
temp=start->next;
free(temp);
start->next=start;
}
else
{
printf("\n enter pos");
scanf("%d",&pos);
p=start;
while(p->next!=start)
{
count=count+1;
if(count==pos)
break;
temp=p;
p=p->next;
}
temp->next=p->next;
free(p);
}
}
27

Advantages of circular linked list:- It saves time when we have to go to the first node from
the last node. But in double linked list we have to go through in between nodes.

Limitations of circular linked list:- It is not easy to reverse circular linked list.

Double linked list:-


Double linked list is a linear data structure which contains a collection of
nodes, where each node contains 3 fields.
1. prev field:- It is a pointer field which contains the address of its previous node. The first
node prev field contains NULL value.
2. data field :- It contains a value which may be an integer, float, char and string.
3. next field:-It is a pointer field which contains the address of its next node. The last node
next field contains NULL value.

In a single linked list only forward traversal is possible i.e. we can traverse from left to right
only where as in a double linked list both forward traversal(Left to right) as well as backward
traversal(Right to left) is possible.

C program to implement double linked list:-


#include<stdio.h>
#include<stdlib.h>
struct node
{
struct node *prev;
int info;
struct node *next;
};
struct node *start;
void insend(int ele);
void insbeg(int ele);
void insmiddle(int ele);
void delend();
void delbeg();
void delmiddle();
void traverse();
void disbackwards();
void main()
{
28

int ele,choice;
start=NULL;
while(1)
{
clrscr();
printf("\t\t Double List operations\n");
printf("1.insbeg\n");
printf("2.insend\n");
printf("3.insmiddle\n");
printf("4.delbeg\n");
printf("5.delend\n");
printf("6.traverse\n");
printf("7.delmiddle\n");
printf("8.displaybackwards\n");
printf("9.exit\n");
printf("enter choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:printf("\n enetr ele ");
scanf("%d",&ele);
insbeg(ele);
break;
case 2:printf("\n enetr ele ");
scanf("%d",&ele);
insend(ele);
break;
case 3:printf("\n enetr ele ");
scanf("%d",&ele);
insmiddle(ele);
break;
case 4:delbeg();
break;
case 5:delend();
break;
case 6:traverse();
break;
case 7:delmiddle();
break;
case 8:disbackwards();
break;

case 9:exit(0);
}
}
}
void insbeg(int ele)
{
struct node *temp;
29

temp=(struct node*)malloc(sizeof(struct node));


temp->info=ele;
temp->prev=NULL;
if(start==NULL)
{
temp->next=NULL;
start=temp;
}
else if(start->next==NULL)
{
temp->next=start;
start->prev=temp;
start=temp;
}
else
{
temp->next=start;
start->prev=temp;
start=temp;
}
}
void insmiddle(int ele)
{
struct node *temp,*p;int pos,count=0;
if(start==NULL)
{
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
temp->prev=NULL;
temp->next=NULL;
start=temp;
}
else
{
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
printf("\n enter position");
scanf("%d",&pos);
p=start;
while(p!=NULL)
{
count++;
if(pos==count)
break;
p=p->next;

}
temp->next=p->next;
temp->prev=p;
30

temp->next->prev=temp;
p->next=temp;
}

}
void insend(int ele)
{
struct node *temp,*p;
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
temp->next=NULL;
if(start==NULL)
{
start=temp;
temp->prev=NULL;
}
else if(start->next==NULL)
{
temp->prev=start;
start->next=temp;
}
else
{
p=start;
while(p->next!=NULL)
{
p=p->next;
}
temp->prev=p;
p->next=temp;
}
}
void delbeg()
{
struct node *temp;
if(start==NULL)
printf("\n list is empty");
else if(start->next==NULL)
{
temp=start;
start=NULL;
free(temp);
}
else
{
temp=start;
start=start->next;
start->prev=NULL;;
free(temp);
31

}
}
void delend()
{
struct node *temp,*p;
if(start==NULL)
printf("\n list is empty");
else if(start->next==NULL)
{
temp=start;
start=NULL;
free(temp);
}
else
{
p=start;
while(p->next->next!=NULL)
{
p=p->next;
}
temp=p->next;
p->next=NULL;
free(temp);
}
}
void traverse()
{
struct node *p;
if(start==NULL)
printf("\n list is empty");
else
{
p=start;
while(p!=NULL)
{
printf(" %d->",p->info);
p=p->next;
}
}
getch();
}
void disbackwards()
{
struct node *p;
if(start==NULL)
printf("\n list is empty");
else
{
p=start;
32

while(p->next!=NULL)
{
p=p->next;
}
while(p !=NULL)
{
printf(" %d-->",p->info);
p=p->prev;
}
}
getch();
}
void delmiddle()
{
struct node *temp,*p;
int pos,count=0;
if(start==NULL)
{
printf("\n list is empty");
}
else if(start->next==NULL)
{
temp=start;
start=NULL;
free(temp);
}
else if(start->next->next==NULL)
{
temp=start->next;
free(temp);
start->next=NULL;
}
else
{
printf("\n enter pos");
scanf("%d",&pos);
p=start;
while(p!=NULL)
{
count=count+1;
if(count==pos)
break;
p=p->next;
}
temp=p->prev;
temp->next=p->next;
free(p);
}
}
33

Advantages of double linked list:-

1. We can traverse in both directions i.e. from left to right as well as from right to left.
2. It is easy to reverse a double linked list.

Limitations of double linked list:-

1. It requires more memory because one extra field (prev) is required.


2. Insertion and deletion operations takes more time because more operations are required.

Circular double linked list:-


It is a linear data structure which is a combination of circular single linked list and
double linked list.
Circular double linked list is a collection of nodes, where each node contains 3 fields.
1. prev field:- It is a pointer field which contains the address of its previous node. The first
node prev field contains address of last node.
2. data field :- It contains a value which may be an integer, float, char and string.
3. next field:-It is a pointer field which contains the address of its next node. The last node
next field contains address of first node.

Diagrammatic representation of circular double linked list:-

Linked list applications:-


1.Polynomials can be represented and various operations can be performed on polynomials
using linked lists.
2.Sparse matrix can be represented using linked list.
3.Various details like student details, customer details , product details and so on can be
implemented using linked list.

Polynomial ADT:- Polynomial is a collection of terms where each term contains coefficient
and exponent.
34

Ex:- 8x6+5x4+2x+9
Where 8,5,2 and 9 are coefficients while 6,4,1 and 0 are exponents.
We can perform various operations on polynomial such as addition, subtraction,
multiplication and division.

C program to implement polynomial ADT:-


#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct node
{
int coeff, expo;
struct node *next;
};
struct node *s1=NULL,*s2=NULL,*s3=NULL;
void createp1();
void createp2();
void traversep1();
void traversep2();
void polyadd();
void createp3(int,int);
void traversep3();
int main()
{
clrscr();
int choice,ele;
while(1)
{
35

clrscr();
printf("\t\t Polynomial Adt Operations \n");
printf("1.create poly1 \n");
printf("2.create poly2 \n");
printf("3.traverselist1\n");
printf("4.traverselist2\n");
printf("5.polynomial add\n");
printf("6.treaverse list \n");
printf("7.exit\n");
printf("enter choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:createp1();
break;
case 2:createp2();
break;
case 3:traversep1();
getch();
break;
case 4:traversep2();
getch();
break;
case 5:polyadd();
break;
case 6:traversep3();
getch();
break;
case 7:exit(0);
}
}
}
void createp1()
{
struct node *temp,*p;
char ch;
do
{
temp=(struct node*)malloc(sizeof(struct node));
printf("enter coeff & expo values of a first polynomial term");
scanf("%d%d",&temp->coeff,&temp->expo);
temp->next=NULL;
if(s1==NULL)
s1=temp;
else if(s1->next==NULL)
s1->next=temp;
else
{
p=s1;
36

while(p->next!=NULL)
p=p->next;
p->next=temp;
}
printf("do you want another term(y/n)\n");
fflush(stdin);
scanf("%c",&ch);
}while(ch=='y');
}
void createp2()
{
struct node *temp,*p;
char ch;
do
{
temp=(struct node*)malloc(sizeof(struct node));
printf("enter coeff & expo values of a second polynomial term\n");
scanf("%d%d",&temp->coeff,&temp->expo);
temp->next=NULL;
if(s2==NULL)
s2=temp;
else if(s2->next==NULL)
s2->next=temp;
else
{
p=s2;
while(p->next!=NULL)
p=p->next;
p->next=temp;
}
printf("do you want another term(y/n)\n");
fflush(stdin);
scanf("%c",&ch);
}while(ch=='y');
}
void traversep1()
{
struct node *p;
if(s1==NULL)
printf("s1 list is empty\n");
else
{
p=s1;
while(p!=NULL)
{
if(p->coeff>0)
printf("+%dx^%d",p->coeff,p->expo);
else
printf("%dx^%d",p->coeff,p->expo);
37

p=p->next;
}
}
}
void traversep2()
{
struct node *p;
if(s2==NULL)
printf("s1 list is empty\n");
else
{
p=s2;
while(p!=NULL)
{
if(p->coeff>0)
printf("+%dx^%d",p->coeff,p->expo);
else
printf("%dx^%d",p->coeff,p->expo);
p=p->next;
}
}
}
void polyadd()
{
struct node *p1,*p2;
int coef_sum;
p1=s1;
p2=s2;
while(p1!=NULL & p2!=NULL)
{
if(p1->expo==p2->expo)
{
coef_sum=p1->coeff+p2->coeff;
createp3(coef_sum,p1->expo);
p1=p1->next;
p2=p2->next;
}
else if(p1->expo>p2->expo)
{
createp3(p1->coeff,p1->expo);
p1=p1->next;
}
else
{
createp3(p2->coeff,p2->expo);
p2=p2->next;
}
}
if(p1==NULL)
38

{
while(p2!=NULL)
{
createp3(p2->coeff,p2->expo);
p2=p2->next;
}
}
else if(p2==NULL)
{
while(p1!=NULL)
{
createp3(p1->coeff,p1->expo);
p1=p1->next;
}
}
}
void traversep3()
{
struct node *p;
if(s3==NULL)
printf("s1 list is empty\n");
else
{
p=s3;
while(p!=NULL)
{
if(p->coeff>0)
printf("+%dx^%d",p->coeff,p->expo);
else
printf("%dx^%d",p->coeff,p->expo);
p=p->next;
}
}
}
void createp3(int c,int e)
{
struct node *temp,*p;
temp=(struct node*)malloc(sizeof(struct node));
temp->coeff=c;
temp->expo=e;
temp->next=NULL;
if(s3==NULL)
s3=temp;
else if(s3->next==NULL)
s3->next=temp;
else
{
p=s3;
while(p->next!=NULL)
39

p=p->next;
p->next=temp;
}
}
STACKS and Queues

Unit-II: Stacks and Queues


Stacks: The Stack: Definition, operations, implementation using arrays, linked list and Stack
applications: Infix to postfix expression conversion, Evaluation of Postfix expressions,
balancing the symbols.Queue: definition, operations, implementation using arrays, linked
list&it’sApplications.Circular queue: definition&its operations, implementation, Dequeue:
definition & its types, implementation.

Learning Material
Stack is a linear data structure.
Stack can be defined as a collection of homogeneous elements, where insertion and
deletion operations takes place at only one end called TOP.
The insertion operation is termed as PUSH and deletion operation is termed as POP
operation.
The PUSH and POP operations are performed at TOP of the stack.
An element in a stack is termed as ITEM.
The maximum number of elements that stack can accommodate is termed as SIZE of
the stack.
Stack Pointer ( SP ) always points to the top element of the stack.
Stack follows LIFO principle. i.e. Last In First Out i.e. the element which is inserted
last into the stack will be deleted first from the stack.

Diagram of a stack







Stack Operations:- We can perform mainly 3 operations on stack.


1.Push :- Inserting an element into the stack
2.Pop:- Deleting an element from the stack
3.Peep:- Displaying top most element of the stack

Representation of stack
There are two ways of representation of a stack.
1. Array representation of a stack.
2. Linked List representation of a stack.

1. Array representation of a stack.


First we have to allocate memory for array.
Starting from the first location of the memory block, items of the stack can be stored in
sequential fashion.

Stack Intial Condition:


Initial condition of stack implemented using arrays is top=-1

Stack Full condition or overflow condition


Trying to PUSH an item into full stack is known as Stack overflow.
Stack overflow condition is top = = ARRAYSIZE - 1

Stack underflow or Empty condition


Trying to POP an item from empty stack is known as Stack
underflow. Stack underflow condition is top = = -1

Algorithm Stack_PUSH(item)
Input: item is new item to push into stack
Output: pushing new item into stack at top whenever stack is not full.
1. if(top == ARRAYSIZE-1)
a) print(stack is full, not possible to perform push operation)
2. else
a) top=top+1
b) read item
c) s[top]=item

End
Stack_PUSH
Algorithm Stack_POP( )
Input: Stack with some elements.
Output: item deleted at top most end.
1. if(top ==-1)
a) print(stack is empty not possible to pop)
2. else
a) item=s[top]
b) top=top-1
c) print(deleted item)
End Stack_POP
C Program to implement stack using arrays
#include<stdio.h>
#include<conio.h>
void push(int ele );
void pop( );
void peep( );
void display( );
int stack[10], top= -1, ele;
void main( )
{
int choi, ele;
clrscr( );
while(1)
{
printf(" \t\t Stack ADT Using Arrays\n");
printf("\n1.Push \n2.Pop \n3.Peep \n4.Display \n5.Exit");
printf("\nEnter your choice");
scanf("%d",&choi);
switch(choi)
{
case 1: printf("\nEnter the element to be inserted into the stack");
scanf("%d",&ele);
push(ele );
break;
case 2: pop( );
break;
case 3: peep( );
break;
case 4: display( );
break;
case 5: exit(0);
}
}
getch( );
}

void push( int ele)


{
if( top==9)
printf("\nStack is full");
else
{
top++;
stack[top]=ele;
}
}

void pop( )
{
if( top==-1)
printf("\nStack is empty");
else
{
ele=stack[top];
printf("\nThe deleted element from the stack is %d",ele);
top--;
}
}

void peep( )
{
if( top == -1)
printf("\nStack is empty");
else
{
ele=stack[top];
printf("\nThe top most element of the stack is %d",ele);
}
}

void display( )
{
int i;
if( top==-1)
printf("\nStack is empty");
else
{
printf("\nThe elements of the stack are:\n");
for(i=top;i>=0;i--)
printf("%d\n",stack[i]);
}
}

2. Linked List representation of a stack

 The array representation of stack allows only fixed size of stack. i.e. static memory
allocation only. 

 To overcome the static memory allocation problem, linked list representation of stack
is preferred. 

 In linked list representation of stack, each node has two parts. One is data field is for
the item and link field points to next node. 

 Empty stack condition is 


top = = NULL

 Full condition is not applicable for Linked List representation of stack. Because
here memory is dynamically allocated. 

 In linked List representation of stack, top pointer always points to top most node only.
i.e. first node in the list. 

Linked list representation of stack:-


C program to implement stacks using linked list:-
#include<stdio.h>
#include<stdlib.h>
struct node
{
int info;
struct node *next;
};
struct node *top=NULL;
void push(int ele);
void pop();
void display();
void main()
{
int ele,choice;
while(1)
{
clrscr();
printf("\t\t Stack ADT using Linked List \n");
printf("1.push\n");
printf("2.pop\n");
printf("3.display\n");
printf("4.exit\n");
printf("enter choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:printf("\n enetr ele ");
scanf("%d",&ele);
push(ele);
break;
case 2:pop();
break;
case 3:display();
break;
case 4:exit(0);
}
}
}
void push(int ele)
{
struct node *temp;
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
if(top==NULL)
{
temp->next=NULL;
top=temp;

}
else
{
temp->next=top;
top=temp;
}
}
void pop()
{
struct node *temp;
if(top==NULL)
printf("\n stack is empty");
else if(top->next==NULL)
{
temp=top;
top=NULL;
free(temp);
}
else
{
temp=top;
top=top->next;
free(temp);
}
getch();
}
void display()
{
struct node *p;
if(top==NULL)
printf("\n stack is empy");
else
{
p=top;
while(p!=NULL)
{
printf("\n elements are %d",p->info);
p=p->next;
}
}
getch();

Arithmetic expressions or Algebraic expressions:-

An expression is a combination of operands and operators.

Eg. c= a + b

In the above expression a, b, c are operands and +, = are called as operators.

We have 3 notations for the expressions.

i. Infix notation
ii. Prefix notation
iii. Postfix notation

Infix notation: Here operator is present between two


operands. eg. a + b

The format for Infix notation as follows


<operand> <operator> <operand>

Prefix notation: Here operator is present before two operands.


eg. + a b

The format for Prefix notation as follows


<operator> <operand> <operand>

Postfix notation: Here operator is present after two operands.


eg. a b +

The format for Postfix notation as follows


<operand> <operand> <operator>

Applications of stack

1. Infix to postfix conversion


2. Evaluation of postfix expression
3. Balancing symbols or delimiter matching

1. Infix to postfix conversion

While conversion of infix expression to postfix expression, we must follow the precedence
(priority) of the operators.

Operator priority

( 0
+ - 1
*/% 2
^ or $ 3
To convert an infix expression to postfix expression, we can use one stack.
Within the stack, we place only operators and left parenthesis only. So stack used in
conversion of infix expression to postfix expression is called as operator stack.

Algorithm Conversion of infix to postfix

Input: Infix expression.

Output: Postfix expression.

1. Perform the following steps while reading of infix expression is not over
a) if symbol is left parenthesis then push symbol into stack.
b) if symbol is operand then add symbol to post fix expression.
c) if symbol is operator then check stack is empty or not.
i) if stack is empty then push the operator into stack.
ii) if stack is not empty then check priority of the operators.
(I) if priority of current operator > priority of operator present at top of
stack then push operator into stack.
(II) else if priority of operator present at top of stack >= priority of
current operator then pop the operator present at top of stack and add
popped operator to postfix expression (go to step I)
d) if symbol is right parenthesis then pop every element form stack up corresponding
left parenthesis and add the poped elements to postfix expression.
2. After completion of reading infix expression, if stack not empty then pop all the items
from stack and then add to post fix expression.
End conversion of infix to postfix
Infix Expression: A+ (B*C-(D/E^F)*G)*H, where ^ is an exponential operator

C Program to convert infix to postfix expression:-

#define SIZE 50 /* Size of Stack */


#include <ctype.h>
char s[SIZE];
int top=-1; /* Global declarations */
void push(char elem)
{ /* Function for PUSH operation */
s[++top]=elem;
}

char pop()
{ /* Function for POP operation */
return(s[top--]);
}

int pr(char elem)


{ /* Function for precedence of operators*/
switch(elem)
{
case '#': return 0;
case '(': return 1;
case '+':
case '-': return 2;
case '*':
case '/': return 3;
}
}

void main()
{ /* Main Program */
char infx[50],pofx[50],ch,elem;
int i=0,k=0;
printf("\n\nRead the Infix Expression ? ");
scanf("%s",infx);
push('#');
while( (ch=infx[i++]) != '\0')
{
if( ch == '(')
push(ch);
else if(isalnum(ch))
pofx[k++]=ch;
else if( ch == ')')
{
while( s[top] != '(')
pofx[k++]=pop();
elem=pop(); /* Remove ( */
}
else
{ /* Operator */
while( pr(s[top]) >= pr(ch) )
pofx[k++]=pop();
push(ch);
}
} //end of while
while( s[top] != '#') /* Pop from stack till empty */
pofx[k++]=pop();
pofx[k]='\0'; /* Make pofx as valid string */
printf("\n\nGiven Infix Expn: %s Postfix Expn:
%s\n",infx,pofx);
}

2.Evaluation of postfix expression


 To evaluate a postfix expression we use one stack. 

 For Evaluation of postfix expression, in the stack we can store only operand. So stack
used in Evaluation of postfix expression is called as operand stack. 
Algorithm PostfixExpressionEvaluation
Input: Postfix expression
Output: Result of Expression
1. Repeat the following steps while reading the postfix expression.
a) if the read symbol is operand, then push the symbol into stack.
b) if the read symbol is operator then pop the top most two items of the stack
and apply the operator on them, and then push back the result to the stack.

2. Finally stack has only one item, after completion of reading the postfix expression.
That item is the result of expression.
End PostfixExpressionEvaluation
Evaluate below postfix expression 234+*6-
C Program to evaluate postfix expression:-
#include<stdio.h>
#include<conio.h>
#include<ctype.h>
int st[100],top=-1;
int cal(char post[]);
void push_item(int);
int pop_item();
void main()
{
char in[50];
int result;
clrscr();
printf("\n \t enter the postfix expression");
gets(in);
result=cal(in);
printf("\n result=%d",result);
getch();
}
void push_item(int it)
{
if(top==99)
printf("stack is overflow\n");
else
{
top++;
st[top]=it;
}
}
int pop_item()
{
int it;
if(top==-1)
printf("stack underflow\n");
else
{
return (st[top--]);
}
}
int cal(char post[])
{
int m,n,x,y,j=0,len;
len=strlen(post);
while(j<len)
{
if(isdigit(post[j]))
{
x=post[j]-'0';
push_item(x);
}
else
{
m=pop_item();
n=pop_item();
switch(post[j])
{
case '+':x=m+n;
break;
case '-':x=m-n;
break;
case '*':x=m*n;
break;
case '/':x=m/n;
break;
}
push_item(x);
}
j++;
}
if(top>0)
{
printf("no of operands are more than operators");
exit(0);
}
else
{
y=pop_item();
return(y);
}
}
3.Balancing Symbols or Delimiter matching :-

The objective of this application is to check the symbols such as parenthesis ( ) , braces { }
, brackets [ ] are matched or not.

Thus every left parenthesis, brace and bracket must have its right counterpart.

Algorithm for Balancing Symbols or Delimiter matching :-

1. Make an empty stack.

2. Read an expression from left to right.

3. If the reading character is opening symbol, then push it into stack.

4. If the reading character is closing symbol and if the stack is empty, then report as
unbalanced expression.

5. If the reading character is closing symbol and if the stack is not empty, then pop the
stack.

6. If the symbol popped is not the corresponding opening symbol, then report as
unbalanced expression.

7. After processing the entire expression and if the stack is not empty then report as
unbalanced expression.

8. After processing the entire expression and if the stack is empty then report as
balanced expression.

C program to implement balancing symbols or delimiter matching:-

#include<stdio.h>
#include<conio.h>
#include<ctype.h>
void push(char);
char pop();
char stack[20];
int top=-1;
main()
{
char expr[20],ch;
int i;
clrscr();
printf("\nEnter an expression\n");
gets(expr);
for(i=0;expr[i]!='\0';i++)
{
ch=expr[i];
if(ch=='(' || ch=='{' || ch=='[')
push(ch);
else if(ch==')')
{
if(top==-1)
{
printf("\nUnbalanced expression");
exit();
}
else if( (ch=pop())!='(')
{
printf("\nUnbalanced expression");
exit();
}
}
else if(ch=='}')
{
if(top==-1)
{
printf("\nUnbalanced expression");
exit();
}
else if( (ch=pop())!='{')
{
printf("\nUnbalanced expression");
exit();
}
}
else if(ch==']')
{
if(top==-1)
{
printf("\nUnbalanced expression");
exit();
}
else if( (ch=pop())!='[')
{
printf("\nUnbalanced expression");
exit();
}
}
}
if(top==-1)
printf("\nBalanced expression");
getch();
}
void push(char x)
{
top++;
stack[top]=x;
}
int pop()
{
return stack[top--];
}
QUEUES

Queue is a linear Data structure.


Definition: Queue is a collection of homogeneous data elements, where insertion operation
is performed at rear end and deletion operation is performed at front end.
The insertion operation in Queue is termed as ENQUEUE.
The deletion operation in Queue is termed as DEQUEUE.

In the Queue the ENQUEUE (insertion) operation is performed at REAR end and
DEQUEUE (deletion) operation is performed at FRONT end.

Queue follows FIFO principle i.e. First In First Out principle i.e. an element First
inserted into Queue, that element only First deleted from Queue.

Diagrammatic Representation of Queue

Representation of Queue
A Queue can be represented in two ways
1. Using arrays
2. Using Linked List

1. Representation of Queue using arrays


A one dimensional array Q[0-SIZE-1] can be used to represent a queue.
Array representation of Queue
In array representation of Queue, two variables are used to indicate two ends of
Queue i.e. front and rear end.
Initial condition: rear=-1 & front=0

Queue overflow: Trying to perform ENQUEUE (insertion) operation in full Queue is known
as Queue overflow.
Queue overflow condition is rear = = ARRAYSIZE-1
Queue Underflow: Trying to perform DEQUEUE (deletion) operation on empty Queue is
known as Queue Underflow.
Queue Underflow condition is rear<front

Operation on Queue
1. ENQUEUE : To insert element in to Queue
2. DEQUEUE : To delete element from Queue
Algorithm Enqueue(item)
Input:item is new item insert in to queue at rear end.
Output:Insertion of new item at rear end if queue is not full.
1.if(rear= = ARRAYSIZE-1)
a) print(queue is full, not possible for enqueue operation)
2.else
i) read element
ii)rear++
iii)queue[rear]=ele

End Enqueue
While performing ENQUEUE operation two situations are occur.
1. if queue is empty, then newly inserting element becomes first element and last
element in the queue. So Front and Rear points to first element in the list.
2. If Queue is not empty, then newly inserting element is inserted at Rear end.

Algorithm Dequeue( )
Input: Queue with some elements.
Output: Element is deleted from queue at front end if queue is not empty.
1.if( rear<front)
a) print(Queue is empty, not possible for dequeue operation)
2.else
i)ele=queue[front]
ii)print(Deleted element from queue is ele)
iii)front++;
End Dequeue

While performing DEQUEUE operation two situations are occur.


1. if queue has only one element, then after deletion of that element Queue becomes
empty. So if front>rear then queue is empty.
2. If Queue has more than one element, then first element is deleted at Front end.

C program to implement Queues using


arrays:-
#include<stdio.h>
#include<conio.h>
#define ARRAYSIZE 5
void enqueue(int ele );
void dequeue( );
void display( );
int queue[SIZE],front=0,rear=-1,ele;
void main( )
{
int ch;
while(1)
{
clrscr();
printf("\t \t Queue ADT using Arrays\n");
printf("\n1.enqueue\n2.dequeue\n3.display");
printf("\nenter your choice");
scanf("%d",&ch);
switch(ch)
{
case 1:printf("\nenter an element to be inserted into the queue");
scanf("%d",&ele);
enqueue(ele );
break;
case 2:dequeue( );
break;
case 3:display( );
break;
default:exit( );
}
}
}

void enqueue(int ele )


{
if(rear==ARRAYSIZE-1)
printf("\nQueue is overflow");
else
{
rear++;
queue[rear]=ele;

}
}

void dequeue( )
{
if(rear<front)
printf("\nQueue is empty");
else
{
ele=queue[front];
printf("\nDeleted element from the queue is %d",ele);
front++;
}
}

void display( )
{
int i;
if(rear<front)
printf("\nQueue is underflow");
else
{
printf("\nThe elements of queue are\n");
for(i=front;i<=rear;i++)
printf("%d\t",queue[i]);
}
}

2. Representation of Queue using Linked List


Array representation of Queue has static memory allocation only.

To overcome the static memory allocation problem, Queue can be represented using
Linked List.
In Linked List Representation of Queue, Front always points to First node in the
Linked List and Rear always points to Last node in the Linked List.

The Linked List representation of Queue stated as follows.


1. Empty Queue condition is
Front = NULL and Rear = NULL
2. Queue full condition is not available in Linked List representation of Queue, because in
Linked List representation memory is allocated dynamically.
3. Queue has only one element
Front = = Rear

Operation on Linked List Representation of Queue


1. ENQUEUE : To insert an element at end
2. DEQUEUE : To delete an element from begining

Linked list Representation of Queues:-


C program to implement queues using linked list:-
/* QUEUE ADT USING LINKED LIST */

#include<stdio.h>
#include<stdlib.h>
struct node
{
int info;
struct node *next;
};
struct node *rear,*front;
void insend(int ele); /* void enqueue(int ele)*/
void delbeg(); /* void dequeue() */
void display();
void main()
{
int ele,choice;
rear=front=NULL;
while(1)
{
clrscr();
printf("\t\t queue list operations\n");
printf("1.enqueue\n");
printf("2.dequeue\n");
printf("3.displau\n");
printf("4.exit\n");
printf("enter choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:printf("\n enetr ele ");
scanf("%d",&ele);
insend(ele);
break;
case 2:delbeg();
break;
case 3:display();
break;
case 4:exit(0);
}
}
}
void insend(int ele)
{
struct node *temp,*p;
temp=(struct node*)malloc(sizeof(struct node));
temp->info=ele;
temp->next=NULL;
if(rear==NULL)
{
rear=temp;
rear=front=temp;
}
else if(rear->next==NULL) /* or else */
{
rear->next=temp;
rear=temp;
}
}
void delbeg()
{
struct node *temp;
if(front==NULL)
printf("\n que is empty");
else if(front->next==NULL)
{
temp=front;
front=NULL;
rear=NULL;
free(temp);
}
else
{
temp=front;
front=front->next;
free(temp);
}
}
void display()
{
struct node *p;
if(front==NULL)
printf("\n list is empty");
else
{
p=front;
while(p!=NULL)
{
printf("%d->",p->info);
p=p->next;
}
}
getch();
}
Types of Queues Or Various Queue Structures
1. Linear Queue
2. Circular Queues
3. DEQue
4. Priority Queue

1. Circular Queues:-
A circular Queue is a linear data structure in which all the locations are treated as circular
such that the first location cqueue[0] follows the last location cqueue[SIZE-1].
Circular queues are mainly useful to overcome the drawbacks of linear queue.

Drawbacks of linear Queue:-

Let queue contains 5 elements.

10 20 30 40 50
0 1 2 3 4

front rear

After performing 2 Dequeue operations, the queue look likes the below

30 40 50
0 1 2 3 4

front rear
If rear reaches to the end of queue, then it’s not possible to insert an element into the
queue even though there is a space at the beginning of the queue.

To solve the above drawback, circular queues are used.

Initial Condtion for Circular Queue is: rear=front=-1

Full Condition for Circular Queue: if(front==((rear+1)%arraysize))

Empty Condition for Circular Queue: rear=front=-1


Algorithm Enqueue()
Step 1. if(front==((rear+1)%arraysize))
print circular Queue is Full
Step 2. else if(rear==-1 && front==-1) //inserting first element
rear++, front++
cq[rear]=ele;
Step 3. else
rear=(rear+1)%size;
cq[rear]=ele;

Algorithm dequeue()
Step 1. if(rear==-1 && front==-1)
print Circular Queue is underflow
Step 2. else if(front==rear) // deleting first element
print cq[front]
rear= front=-1
Step 3. else
front=(front+1)%size;
Circular Queue ADT using Arrays

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define arraysize 5
int a[arraysize],rear=-1,front=-1;
void enque(int ele);
void deque();
void display();
void main()
{
int ele,choice;
clrscr();
while(1)
{
clrscr();
printf("\t\t circular que operations \n");
printf("1.enque \n2.deque \n3.display \n4.exit\n");
printf("enter choice\n");
scanf("%d",&choice);
switch(choice)
{
case 1:printf("enter ele \n");
scanf("%d",&ele);
enque(ele);
break;
case 2:deque();
break;
case 3:display();
break;
case 4:exit(0);
break;
}
}
}
void enque(int ele)
{
if(front==(rear+1)%arraysize)
{
printf("\n Circular que is full");
getch();
}
else if(front==-1 && rear==-1)
{
rear++;
front++;
a[rear]=ele;
}
else
{
rear=(rear+1)%arraysize;
a[rear]=ele;
}
}
void deque()
{

if(front==-1 && rear==-1)


printf("\n circular que is empty ");
else if(front==rear)
{
printf("\n the delted eleis %dfront=%d",a[front],front);
front=rear=-1;
printf("front=%d",front);
}
else
{
printf("\n the delted eleis %d",a[front]);
front=(front+1)%arraysize;
printf("front=%d",front);
}
getch();
}
void display()
{
int i;
if(rear==-1 && front==-1)
{
printf("\n cir que is empty");
}
i=front;
while(i!=rear)
{
printf("%d ",a[i]);
i=(i+1)%arraysize;
}
printf("%d ",a[i]);
getch();
}
2. DEQueue(Double Ended Queue):-
Another variation of queue is known as DEQue.

In DEQueue, ENQUEUE (insertion) and DEQUEUE (deletion) operations can be made at


both front and rear ends.

DEQue is organized from Double Ended Queue.

Insertion Deletion

Deletion Insertion
Front Rear
A DEQue structure
Here DEQue structure is general representation of stack and Queue. In other words, a DEQue
can be used as stack and Queue.

DeQue can be represented in two ways.

1. Using Double Linked List


2. Using a Circular Queue

Here Circular array is popular representation of DEQue.

On DEQue, the following four operations can be performed.


1.insertion at rear end.
2.deletion at front end
3.insertion at front end
4.deletion at rear end
C program to implement double ended queue(Dequeue):-
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define SIZE 5
int dequeue[SIZE];
int front=-1,rear=-1;
void display();
void insertatfront();
void insertatrear();
void deleteatfront();
void deleteatrear();
int ch,value;
void main()
{
clrscr();
while(1)
{
printf("Menu");
printf("\n 1.Insert at Front:");
printf("\n 2.Insert at Rear:");
printf("\n 3.Delete at Front:");
printf("\n 4.Delete at Rear:");
printf("\n 5.Display");
printf("\n 6.Exit");
printf("\nEnter your choice");
scanf("%d",&ch);
switch(ch)
{
case 1:insertatfront();
break;
case 2:insertatrear();
break;
case 3:deleteatfront();
break;
case 4:deleteatrear();
break;
case 5:display();
break;
case 6:exit(1);
}
getch();
}
}
void insertatfront()
{
if(front==0)
{
printf("\n Queue is FULL");
}
else
{
printf("\nenter an element to be inserted into the queue");
scanf("%d",&value);
if(front==-1 && rear==-1)
{
front++;
rear++;
}
else
{
front--;
}
dequeue[front]=value;
}
}
void insertatrear()
{
if(rear==SIZE-1)
{
printf("\n Queue is FULL");
}
else
{
rear=rear+1;
printf("\n Enter a value:");
scanf("%d",&value);
dequeue[rear]=value;
if(front==-1)
front=0;
}
}
void deleteatfront()
{
if(front==-1 || front>rear)
{
printf("\n Queue is EMPTY");
}
else
{
value=dequeue[front];
printf("\n Deleted element from the queue is %d",value);
front++;
}
}
void deleteatrear()
{
if(rear==-1)
{
printf("\n Queue is EMPTY");
}
else
{
value=dequeue[rear];
printf("\n deleted element from the queue is %d",value);
if(front==rear)
front=rear=-1;
else
rear--;
}
}
void display()
{
int i;
if(front==-1)
printf("\nQueue is empty");
else
{
printf("\n The Queue is::");
for(i=front;i<=rear;i++)
{
printf("%d\t ", dequeue[i]);
}
}
}
There are two variations of DEQue known as

1. Input restricted DEQue


2. Output restricted DEQue

1. Input restricted DEQue

Here DEQue allows insertion at one end (say REAR end) only, but allows deletion at
both ends.

Deletion

Deletion Insertion

Front Rear
Input restricted DEQue
2. Output restricted DEQue

Here DEQue allows deletion at one end (say FRONT end) only, but allows insertion
at both ends.
DEQue is organized from Double Ended Queue.

Insertion

Deletion Insertion
Front Rear Output restricted DEQue
UNIT –III

Searching & Sorting

Syllabus:
Searching: Linear and Binary Search.
Sorting: Bubble sort, Insertion Sort, Heap Sort, Merge Sort & Quick Sort.

Learning Material

Searching:
It is a process of verifying whether the searching element is available in the given set
of elements or not.
Types of Searching techniques are:
1. Linear Search
2. Binary Search

1. Linear Search:-
In linear search, search process starts from starting index of array i.e. 0th index
of array and end’s with ending index of array i.e. (n-1)th index. Here searching is
done in Linear fashion (Sequential fashion).

Algorithm linearsearch(a<array>, n, key)


Input: a is an array with n elements, key is the element to be searched.
Output: key element is found in array, if it is available.
1. flag = 0
2. i =0
3. while(i<n)
a) if(a[i] = = key)
i) flag = 1
ii) break
b) end if
c) i = i +1
4. end loop
5. if( flag == 1)
a) print( key element is found in the array)
else
a) print(key element is not found in the array)
6.end if
End linearsearch
C program to implement linear search:-
#include<stdio.h>
#include<conio.h>
main( )
{
int a[10],n,key,index,i,flag=0;
clrscr( );
printf(“\nEnter size of the array”);
scanf(“%d”,&n);
printf(“\nEnter elements of the array”);
for(i=0;i<n;i++)
scanf(“%d”,&a[i]);
printf(“\nEnter key element”);
scanf(“%d”,&key);
for(i=0;i<n;i++)
{
if(a[i]==key)
{
flag=1;
break;
}

}
if(flag==1)
printf(“\nKey element is found”);
else
printf(“\nKey element is not found”);
getch();
}
Analysis of linear search:-
1. Best case time complexity:- In linear search, the best case will occurs if key
element is the first element of the array.
Best case time complexity T(n)=O(1)
2. Worst case time complexity:- In linear search, the worst case will occurs if
key element is the last element of the array.
Worst case time complexity T(n)=O(n)
3. Average case time complexity:- In linear search, the average case will occurs
if key element is in between of the array.
Average case time complexity T(n)=O(n)

2. Binary Search:-
The input to binary search must be in ascending order i.e. set of elements be in
ascending order.
Searching process in Binary search as follows:
First, key element is compared with middle element of array.

If key element is equal to middle element of array then Successful Search.

If key element is less than the middle element of array, then search in LEFT
part. So update high value therefore high=mid-1.

If key element is greater than middle element of array, then search in RIGHT
part. So update low value therefore low=mid+1.

Algorithm binarysearch(a<array>, n, key)


Input: a is an array with n elements, key is the element to be searched.
Output: key element is found in array, if it is available.
1. flag= 0
2. low = 0
3. high = n-1
4.while(low<=high)
a)mid=(low+high )/2.
b) if(key==a[mid])

i) flag=1
ii) break
c) else if(key<a[mid])
i) high = mid - 1
d) else if(key>a[mid])
i) low= mid + 1

5.end loop
6.if(flag==1)
a) print(key element is found)
else
a)print(key element is not found)

End binarysearch

C Program to implement binary search:-

#include<stdio.h>
#include<conio.h>
main( )
{
int n,a[10],i,key,low,mid,high,flag;
clrscr();
printf(“\nEnter size of the array”);
scanf(“%d”,&n);
printf(“\nEnter elements of the array”);
for(i=0;i<n;i++)
scanf(“%d”,&a[i]);
printf(“\nEnter key element”);
scanf(“%d”,&key);
flag=0;
low=0;
high=n-1;
while(low<=high)
{
mid=(low+high)/2;
if(key==a[mid])
{
flag=1;
break;
}
else if(key<a[mid])
high=mid-1;
else
low=mid+1;
}
if(flag==1)
printf(“key element is found”);
else
printf(“key element is not found”);
getch();
}

Analysis of binary search:-

1. Best Case time complexity:- In binary search, the best case will occurs if
key element is array’s middle element.
So, Best case time complexity T(n)=O(1)

2. Worst case time complexity:-In binary search, the worst case will occurs if
key element is either first or last element.

T(n)=T(n/2) + 1

The given array 1 comparison (to compare key element with array’s
Is divided into 2 parts middle element

T(n)=T(n/2)+1 => T(n/21)+1


T(n)=T(n/4)+1+1 => T(n/22)+2
T(n)=T(n/8)+1+1+1 => T(n/23)+3
.
.
.
T(n)=T(n/2k)+k
Let 2k=n
Apply log on both sides then log 2k=log n
Klog 2=logn
k.1=logn
k=logn

T(n)=T(n/2k)+k
=T(n/n)+logn
=T(1)+logn
=logn
So, worst case time complexity=O(logn)

3. Average case tie complexity:- O(logn)


Ex:- Using binary search trace for the key element 89 from the given list of elements
34,23,15,89,74,56,92,78,12,56
Ans)We can implement binary search only when the elements are in ascending
order.
So the given list of elements in ascending order are 12,15,23,34,56,56,74,78,89,92

n=10, low=0, high=n-1


=9

12 15 23 34 56 56 74 78 89 92

0 1 2 3 4 5 6 7 8 9

low high
low<=high
0 <= 9 condition is true
mid=(low+high)/2 =>(0+9)/2 =>9/2 =>4 (int/int=int)
so, mid=4
Compare key element (89) with array’s middle element (a[4] i.e. 56)
89>56 , so low=mid+1
low=4+1
=5

12 15 23 34 56 56 74 78 89 92
0 1 2 3 4 5 6 7 8 9

low high
low<=high
5 <= 9 condition is true
mid=(low+high)/2 =>(5+9)/2 =>14/2 =>7
so, mid=7
compare key element(89) with array’s middle element(a[7] i.e. 78)
89>78 , so low=mid+1
low=7+1
=8

12 15 23 34 56 56 74 78 89 92
0 1 2 3 4 5 6 7 8 9

low
high
low<=high
8 <= 9 condition is true
mid=(low+high)/2 =>(8+9)/2 =>17/2 =>8
so, mid=8
compare key element(89) with array’s middle element(a[8] i.e. 89)
89= =89, so key element is found.

Sorting: Sorting means arranging the elements either in ascending or descending


order.
There are two types of sorting.
1. Internal Sorting.
2. External Sorting.
1. Internal Sorting: For sorting a set of elements, if we use only primary memory
(Main memory), then that sorting process is known as internal sorting. i.e. internal
sorting deals with data stored in computer memory.
2. External Sorting: For sorting a set of elements, if we use both primary memory
(Main memory) and secondary memory, then that sorting process is known as
external sorting. i.e. external sorting deals with data stored in files.

Different types of sorting techniques.

1. Bubble sort
2. Insertion sort
3. Merge sort
4. Quick sort
5. Heap sort
1. Bubble sort:
In bubble sort, in each iteration we compare adjacent elements i.e. ith index
element will be compared with (i+1)th index element, if they are not in
ascending order, then swap them.
After first iteration the biggest element is moved to the last position.
After second iteration the next biggest element is moved to next last but one
position.
In bubble sort for sorting n elements, we require (n-1) passes (or) iterations
Process:
1. In pass1, a[0] and a[1] are compared, then a[1] is compared with a[2], then
a[2] is compared with a[3] and so on. Finally a[n-2] is compared with a[n-1].
Pass1 involves (n-1) comparisons and places the biggest element at the
highest index of the array.
2. In pass2, a[0] and a[1] are compared, then a[1] is compared with a[2], then
a[2] is compared with a[3] and so on. Finally a[n-3] is compared with a[n-2].
Pass2 involves (n-2) comparisons and places the next biggest element at the
next highest index of the array.

3. In pass (n-1), a[0] and a[1] are compared. After this step all the elements of
the array are arranged in ascending order.

C Program to implement bubble sort:-


#include<stdio.h>
#include<conio.h>
void bubble_sort(int [ ],int);
main()
{
int n,a[10],i;
clrscr();
printf("\n Enter size of the array");
scanf("%d",&n);
printf("\n Enter elements of the array");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
bubble_sort(a,n);
printf("\n After sorting the elements of the array are");
for(i=0;i<n;i++)
printf("%d \t",a[i]);
getch();
}

void bubble_sort(int a[ ],int n)


{
int i,j,temp;
for(i=0;i<n-1;i++)
{
for(j=0;j<n-1-i;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}

Analysis of bubble sort:-


1. Best case time complexity:- The best case will occur if array is already in sorted
(Ascending ) order .
T(n)=O(n)
2. Worst case time complexity:-The worst case will occur if the elements are in
descending order.
Iteration No. Of comparisions
st
1 n-1
nd
2 n-2
3rd n-3
th
4 n-4
.
.
.
.
Last iteration 1
Worst case time complexity T(n)=(n-1)+(n-2)+(n-3)+-----------+1
T(n)=n(n-1)/2
T(n)=n2
3.Average case time complexity:- O(n2)

2. Insertion sort

1. Step 1: The second element of an array is compared with the elements that
appears before it (only first element in this case). If the second element is
smaller than first element, second element is inserted in the position of first
element. After first step, first two elements of an array will be sorted.

2. Step 2: The third element of an array is compared with the elements that
appears before it (first and second element). If third element is smaller than
first element, it is inserted in the position of first element. If third element is
larger than first element but, smaller than second element, it is inserted in the
position of second element. If third element is larger than both the elements, it
is kept in the position as it is. After second step, first three elements of an
array will be sorted.

3. Step 3: Similarly, the fourth element of an array is compared with the


elements that appears before it (first, second and third element) and the same
procedure is applied and that element is inserted in the proper position. After
third step, first four elements of an array will be sorted.
C Program to implement Insertion Sort:-

#include<stdio.h>
#include<conio.h>
int a[10],n;
void insertionsort();
void main()
{
int i;
printf("enter size\n");
scanf("%d",&n);
printf("enter elements to sort\n");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
insertionsort();

}
void insertionsort()
{
int i,j,temp;
for(i=1;i<n;i++)
{
temp=a[i];
for(j=i-1;a[j]>temp && j>=0;j--)
{
a[j+1]=a[j];
}
a[j+1]=temp;
}
for(i=0;i<n;i++)
printf("%d ",a[i]);
}

Analysis of insertion sort:-

1.Best case time complexity:- The best case will occurs when the elements are in
ascending order.
Ex:- 10 20 30 40 50
Iteration No. Of comparisions
1st 1
2nd 1
3rd 1
4th 1
.
.
.
.
Last iteration 1
T(n)=1+1+1+1+------+1
=n-1
T(n)=O(n)

2. Worst case time complexity:-The worst case will occur if the elements are in
descending order.
Iteration No. Of comparisions
1st n-4
nd
2 n-3
rd
3 n-2
th
4 n-1
.
.
.
.
Last iteration 1
Worst case time complexity T(n)=(n-4)+(n-3)+(n-2)+-----------+1
T(n)=n(n-1)/2
T(n)=n2

3.Average case time complexity:- O(n2)

3. Merge sort:- Merge sort works on the principle of Divide and Conquer
technique.
Any Divide and Conquer algorithm is implemented using 3 steps.
1.Divide
2.Conquer
3.Combine or merge

1.Divide:- The given array is divided into 2 parts.


2.Conquer:- Each sub array is sorted recursively, so that the first sub array and
second sub array are in sorted order.

3.Combine or merge:- Merge the solutions of 2 sub arrays.

The fundamental operation of this algorithm is merging of 2 sorted lists.


If n=1, there is only one element to sort and the answer is at hand otherwise
recursively sort the first sub array and the second sub array.
This gives 2 sorted lists, which can be merged together using merging algorithm.

Merging algorithm:-
The basic merging algorithm takes 2 input arrays A and B and an
output array C, 3 variables i,j,k which are initially set to the beginning of their
respective arrays.
The smallest of A and B is copied to the array C and the appropriate variables are
incremented.
When either input list is completed, then the remaining elements of other list are
copied to the array C.

Ex:- To sort 8-element array 24 13 26 1 2 27 28 15

1.Divide:- The given array is divided into 2 parts.

List 1:- 24 13 26 1

List 2:- 2 27 28 15

2.Conquer:- Each sub array is sorted recursively, so that the first sub array and
second sub array are in sorted order.

List 1:- 1 13 24 26

List 2:- 2 15 27 28
3.Combine or merge:- Merge the solutions of 2 sub arrays. After merging elements
are
1 2 13 15 24 26 27 28
C Program to implement Merge Sort:-
#include<stdio.h>
#include<conio.h>
int a[20],n;
void merge(int i,int j,int p,int q);
void mergesort(int l,int h);
void main()
{
int i;
clrscr();
printf("enter no array of elements to sort\n");
scanf("%d",&n);
printf("enter array of elements to sort\n");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
mergesort(0,n-1);
printf("elements after mergesort \n");
for(i=0;i<n;i++)
printf("%d ",a[i]);
getch();

}
void mergesort(int low,int high)
{
int mid;
if(low<high)
{
mid=(low+high)/2;
mergesort(low,mid);
mergesort(mid+1,high);
merge(low,mid,mid+1,high);
}
}
void merge(int i,int j,int p,int q)
{
int c[100],k=i,r=i;
while((i<=j) && (p<=q))
{
if(a[i]<a[p])
c[k++]=a[i++];
else
c[k++]=a[p++];
}
while(i<=j)
{
c[k++]=a[i++];
}
while(p<=q)
{
c[k++]=a[p++];
}

for(i=r;i<=q;i++)
a[i]=c[i];

Analysis of Merge Sort:- If n=1, then we can get the solution directly.
So, T(n)=1 if n=1
If n>1, then the given array is divided into 2 parts where each array contains n/2
elements and we need minimum ‘n’ comparisons for merging.
T(n)=2T(n/2) + n if n>1

The given array n comparisons for merging


is divided into 2 parts
Where each array
Contains n/2 elements.

T(n)=2T(n/2)+n => 21 T(n/21)+1.n


T(n)=2[2T(n/4)+n/2]+n
=4T(n/4)+2n => 22T(n/22)+2.n
T(n)=4[2T(n/8)+n/4]+2n
=8T(n/8)+3n => 23T(n/23)+3.n
.
.
.
T(n)=2 T(n/2k)+k.n
k

Let 2k=n
Apply log on both sides then log 2k=log n
Klog 2=logn
k.1=logn
k=logn

T(n)=2kT(n/2k)+k.n
=n.T(n/n)+logn.n
=T(1)+n logn
=1+n logn
=n log n
So, the time complexity of Merge sort in all cases is O(nlogn)

4. Quick Sort :- Quick sort is implemented based on the principle of Divide and
Conquer algorithm.
Divide and conquer algorithm is implemented using 3 steps.
1.Divide 2.Conquer 3.Combine or merge

1.Divide:- Select the first element of an array as pivot element. Divide the given
array 2 parts such that the left part contains the elements which are less than the
pivot element and right part contains the elements which are greater than the pivot
element. This arrangement process is called as Partitioning.

2.Conquer:- The left part and right part will be sorted recursively, so that the
entire array will be in sorted order.

3.Combine:- There is no need of combine or merge step in quick sort.

Procedure for partitioning:-

Process:
1. Initialize pivot element as first element in the array to be sort.
2. Initialize i as starting index of the array to be sort, j as ending index of the
array to be sort.
3. Do the following steps while i < j
1. Repeatedly move the i to right (i.e. increase the i value) while ( a[i] <=
pivot) i.e. all the elements to Left of pivot element are Less than pivot
element.
2. Repeatedly move the j to left (i.e. decrease the j value) while (a[j] > pivot)
i.e. all the elements to Right of pivot element are Greater than pivot
element.
3. If i < j, then swap a[i] and a[j]
4. If i<j is false, then j becomes pivot element position so swap a[j] and a[low].
How to select Pivot element in Quick Sort:-
Quick sort is implemented by selecting an element of an array as pivot element.
There are 3 approaches to select the pivot element.
1. Selecting 1st element as pivot element
2. Selecting a random element as pivot element
3. Selecting Median of 3 elements as pivot element

1. Selecting 1st element as pivot element:- The popular approach is to select first
element as pivot element. This is acceptable if the input is random but if the input is
either in ascending or descending order then it provides a poor partition.

2.Selecting a random element as pivot element:- Another approach is simply


choose the pivot element randomly. On the other hand random number generator is
generally an expensive approach and doesn’t reduce the average running time of the
algorithm.

3. Selecting Median of 3 elements as pivot element:- Another approach is simply


choose median of first, last and middle element of the array as pivot element.

C program to implement Quick Sort:-

#include<stdio.h>
#include<conio.h>
int a[20],n;
int partion(int l,int h);
void quicksort(int low,int high);
void main()
{
int i;
clrscr();
printf("enter no array of elements to sort\n");
scanf("%d",&n);
printf("enter array of elements to sort\n");
for(i=0;i<n;i++)
scanf("%d",&a[i]);
quicksort(0,n-1);
printf("elements after quicksort \n");
for(i=0;i<n;i++)
printf("%d ",a[i]);
getch();

}
void quicksort(int low,int high)
{
int j;
if(low<high)
{
j=partion(low,high);
quicksort(low,j-1);
quicksort(j+1,high);

}
}
int partion(int l,int h)
{
int i=l,j=h;
int pivot,temp;
pivot=a[i];
while(i<j)
{
while(a[i]<=pivot)
i++;
while(a[j]>pivot)
j--;
if(i<j)
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
temp=a[j];
a[j]=pivot;
a[l]=temp;
return j;
}
Analysis of Quick Sort:-

1.Best Case Time Complexity:- In Quick sort, the best case will occur when we
divide the given array into exactly 2 half’s.

If n=1, then we can get the solution directly.


So, T(n)=1 if n=1
If n>1, then the given array is divided into 2 parts and we need minimum ‘n’
comparisons
T(n)=2T(n/2) + n if n>1
T(n)=2T(n/2)+n => 21 T(n/21)+1.n
T(n)=2[2T(n/4)+n/2]+n
=4T(n/4)+2n => 22T(n/22)+2.n
T(n)=4[2T(n/8)+n/4]+2n
=8T(n/8)+3n => 23T(n/23)+3.n
.
.
.
T(n)=2 T(n/2k)+k.n
k

Let 2k=n
Apply log on both sides then log 2k=log n
Klog 2=logn
k.1=logn
k=logn

T(n)=2kT(n/2k)+k.n
=n.T(n/n)+logn.n
=T(1)+n logn
=1+n logn
=n log n
So, the best case time complexity of Quick sort sort =O(nlogn)

2.Averge Case time complexity=O(nlogn)

3.Worst Case time complexity:- The worst case will occurs when pivot element is
either minimum element or maximum element.

Let ‘i’ be the pivot element (smallest element of the array) and assumes that i index
location is 1.

T(n)=T(i-1)+T(n-i)+n
=T(1-1)+T(n-1)+n
=T(0)+T(n-1)+n
T(n)=T(n-1)+n
T(n-1)=T(n-2)+n-1
T(n-2)=T(n-3)+n-2
.
.
.
.
1 2 3 4. . . . . . . . n=n(n+1)/2=n2

Worst case time complexity of quick sort=O(n2)


Time Complexities of Different Searching and Sorting Algorithms

Time Complexity

Algorithm

Best Average Worst

Linear Search O(1) O(n) O(n)

Binary Search O(1) O( log n) O( log n)


Bubble Sort O(n) O(n2) O(n2)

Insertion Sort O(n) O(n2) O(n2)

Selection Sort O(n2) O(n2) O(n2)

Merge Sort O(n logn) O(n logn) O(n logn)

Quick Sort O(n logn) O(n logn) O(n2

Bucket Sort O(n+b) O(n+b) O(n+b)

Shell Sort O(n2) O(n2) O(n2)


UNIT - IV: TREES

Syllabus:

Trees: Terminology Binary Trees: definition, types of binary trees, Representation,


Implementation (linked list), Tree traversals: Recursive techniques, Expression Tress, Search
Tree: Binary Search Tree-search, insert, Delete, Balanced Tree –Introduction to AVL tree and
Rotations.

Learning Material
Tree is mainly used to represent information level by level.

Tree is a nonlinear data structure.


Definition
A tree T is a finite set of one or more nodes such that:
(i) There is a special node called as root node.
(ii) The remaining nodes are partitioned into n disjoint sets T1, T2, T3,. . . Tn. where n>0 and
each disjoint set is a tree.
T1, T2, T3. . .Tn are called as sub trees.

A
T3
T1 T2
D
B C

H I
E F G

A sample Tree
Basic Terminology:
1. Node: Every element of tree is called as a node. It stores the actual data and links to other
nodes.

Data

Link Link

Structure of a node in Tree

2. Link / Edge / Branch: It is a connection between 2 nodes.

Data

LC RC

Here LC Points To Left Child and RC Points To Right Child.

3. Parent Node: The Immediate Predecessor of a Node is called as Parent Node.

Y Z

Here X is Parent Node to Node Y and Z.

4.Child Node: The Immediate Successor of a Node is called as Child Node.


In the above diagram Node Y and Z are child nodes to node X.

5. Root Node: Which is a specially designated node, and does not have any parent node.
Level
A 0

B C 1

D E F G 2

H
I 3
J K L

In the above diagram node A is a Root Node.

6. Leaf node or terminal node: The node which does not have any child nodes
is called leaf node. In the above diagram node H, I, E, J, K, L and G are Leaf
nodes.

7. Level: It is the rank of the hierarchy and the Root node is present at level 0. If a node is
present at level l then its parent node will be at the level l-1and child nodes will present at
level l+1.

8. Siblings: The nodes which have same parent node are called as siblings.

In the above example nodes B and C are siblings, nodes D and E are siblings, nodes F and G
are siblings, nodes H and I are siblings.

9. Degree of a node: The number of nodes attached to a particular node.

In the above figure degree of A is 2, degree of F is 3.

10. Degree of a tree: The maximum degree of a node is called as degree of tree.

In the above figure degree of tree is 3.


11.Non terminal node or Internal node:The nodes with child nodes are called as non terminal
nodes.
In the above example A,B,C,D,F are internal nodes.
12.Path: It is a sequence of consecutive edges from source to destination nodes.
In the above figure path from A to E is A-B-E.
13.Path length: The number of edges between source and destination nodes.
Ex: A-B-E ( path length is 2)
A-C-F-J ( path length is 3)
14. Height of a node: It is the length of longest path from the node to leaf. Height of leaf node is
zero.

In the above figure, height of A is 3, height of B is 2.

15. Height of a tree:- : It is the length of longest path from the root node to leaf.

In the above figure, height of tree is 3.

16. Depth of a node:- : It is the length of longest path from the root to that node. Depth of root
node is 0.

In the above figure, depth of A is 0, depth of B is 1.

17. Depth of a tree:- : It is the length of longest path from the root node to leaf.

In the above figure, depth of tree is 3.

18. Ancestor and Descendent node:- If there is a path from node A to node B then A is called
as Ancestor of B and B is called as descendent of A.

BINARY TREE: Is a special form of a tree.


Definition: A tree in which every node can have a maximum of two children is called as Binary
Tree.

B C

D E F G

H I J

A sample Binary Tree


TYPES OF BINAERY TREE
1. Full binary tree or Strictly binary tree
2. Complete binary tree
3. Left Skewed binary tree
4. Right Skewed binary tree
5. Balanced binary tree
1. Full Binary Tree: A binary tree is said to be full binary tree, if every node should have
exactly two children or none.
Eg:

B C

D E F G

H I
J K L M N O

A Full Binary Tree

2. Complete Binary Tree: A complete binary tree is a binary tree in which every level
except possibly the last level is completely filled and all nodes are from left to right.
Eg:

B C

D E F G

H I
J K L

A Complete Binary Tree


3. Left Skewed binary tree: A binary tree in which each node is having only left sub trees is
called as left skewed binary tree.

10

20

30

4.Right Skewed binary tree: A binary tree in which each node is having only right sub trees is
called as right skewed binary tree.

10

20

30

6. Balanced Binary Tree:


A binary tree is balanced if height of the tree is O(Log n) where n is number of nodes. For
Example, AVL tree maintain O(Log n) height by making sure that the difference between
heights of left and right sub trees is 1.

Representation of Binary Tree


Binary tree can be represented in two ways.
1. Linear (or) Sequential representation using arrays.
2. Linked List representation using pointers.
1. Linear (or) Sequential representation using arrays :-

1. The ROOT node is at index 0.


2. The left child of tree is stored at 2i +1 where i is the parent index value
3. The right child is stored at 2i+2 location.
.
Eg.

10

20 30

40 50 60 70

80 90
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

10 20 30 40 50 60 70 80 90
Array representation of above binary tree.

Advantages of Linear representation of Binary Tree


1. Any node can be accessed from any other node by calculating the index and this is
efficient from execution point of view.
2. Here only data is stored without any pointers to their successor (or) predecessor.
Disadvantages of Linear representation of Binary Tree
1. Other than full binary tree, majority of entries may be empty.
2. It allows only static memory allocation.

2. Linked List representation of Binary Tree using pointers :-

Linked list representation of assumes structure of a node as shown in the following


figure.

Data

Left Child Right Child

With linked list representation, if one knows the address of ROOT node, then any
other node can be accessed.

Linked List representation of Binary Tree

Advantages of Linked List representation of Binary Tree


1. It allows dynamic memory allocation.
2. We can overcome the drawbacks of linear representation.
Disadvantages of Linked List representation of Binary Tree
1. It requires more memory than linear representation. i.e. Linked list representation
requires extra memory to maintain pointers.

Tree Traversals
It is the process of visiting the nodes of a tree exactly once.
A Binary tree can be traversed in 3 ways.
1. Preorder traversal
2. Inorder traversal
3. Postorder traversal

Ex:- A

B C

D E F G

1. Preorder traversal :-
Here first ROOT node is visited, then LEFT sub tree is visited in Preorder fashion and then
RIGHT sub tree is visited in Preorder fashion.
i.e. ROOT, LEFT, RIGHT

Function:-

void preorder(struct node *temp)


{
if(temp!=NULL)
{
printf("%d \t",temp->key);
preorder(temp->left);
preorder(temp->right);
}
}

Ex:- Preorder traversal for the above binary tree is : A B D E C F G

2. Inorder traversal:-
Here first LEFT sub tree is visited in Inorder fashion, then ROOT node is visited and then
RIGHT sub tree is visited in Inorder fashion
i.e. LEFT, ROOT, RIGHT

Function:-

void inorder(struct node *temp)


{
if(temp!=NULL)
{
inorder(temp->left);
printf("%d \t",temp->key);
inorder(temp->right);
}
}

3. Postorder traversal:-
Here first LEFT sub tree is visited in postorder fashion, then RIGHT sub tree is visited in
postorder fashion and then ROOT node is visited.
i.e. LEFT, RIGHT, ROOT
Eg:

Function:-

void postorder(struct node *temp)


{
if(temp!=NULL)
{
postorder(temp->left);
postorder(temp->right);
printf("%d \t",temp->key);
}
}

Expression trees:- Expression tree is a binary tree in which each internal node represents an
operator and each leaf node represents operand.
Expression trees are constructed based on postfix expression.

Procedure to construct expression tree:-

1. Read postfix expression from left to right.


2. If the reading symbol is an operand then create a one node tree and push a pointer () to it on
to the stack.
3. If the reading symbol is an operator then pop top 2 elements say T1,T2 and create a new tree
whose parent is an operator, where Left, right children are T1,T2 and push a pointer() to it on
to a stack.
Construct expression tree for a b + c d e + * *:-

Since the first two symbols are operands, one-node trees are created and pointers are pushed to
them onto a stack.

The next symbol is a '+'. It pops the top two pointers to the trees, a new tree is formed, and a
pointer to it is pushed onto to the stack.

Next, c, d, and e are read. A one-node tree is created for each and a pointer to the corresponding
tree is pushed onto the stack.
Next '+' is read, pops the top 2 elements and a new tree is formed with + as parent node.

Now, a '*' is read. The last two tree pointers are popped and a new tree is formed with a '*' as the
root.

Forming a new tree with a root

Finally, the last symbol is read. The two trees are merged and a pointer to the final tree remains
on the stack.
Binary Search Trees (BST):-
A Binary Search Tree is a binary tree in which the left sub tree contains the values which are less
than the parent node and the right sub tree contains the values which are greater than the parent
node.
To make the searching process faster, we uses binary search tree.
Binary Search Tree is a combination of binary tree and binary search.

Example of Binary Search Tree:-

Binary Search Tree Operaions:-


1. Insertion
2. Find min
3. Find max
4. Search
5. Delete

1. Insertion:- This Operation is used to insert an element into a Binary Search Tree.

Case 1: When tree is empty


(i) Create new node.

(ii) Make new node’s left and right field as NULL i.e. newleft=newright=NULL.

NULL NULL

(iii) Store value in new node’s data field i.e. newdata=value.

NULL Value NULL

Case 2:- When tree is not empty


(i) Insert value in root left if value<rootdata
(ii) Insert value in root right if value>rootdata

Ex:-Construct binary search tree for the elements 20, 23, 13, 9, 14, 19, 21, 27 and 24.

Insert 20 into the Binary Search Tree.


Tree is not available. So, create root node and place 20 into it.

20

Insert 23 into the given Binary Search Tree. 23 > 20 (data in root). So, 23 needs to be inserted in
the right sub-tree of 20.

20
\
23

Insert 13 into the given Binary Search Tree. 13 < 20(data in root). So, 13 needs to be inserted in
left sub-tree of 20.

20
/ \
13 23
Insert 9 into the given Binary Search Tree.

20
/ \
13 23
/
9

Inserting 14 into the given Binary Search Tree.

20
/ \
13 23
/ \
9 14

Inserting 19 into the given Binary Search Tree.

20
/ \
13 23
/ \
9 14
\
19
Inserting 21 into the given Binary Search Tree.

20
/ \
13 23
/ \ /
9 14 21
\
19
Inserting 27 into the given Binary Search Tree.
20
/ \
13 23
/ \ / \
9 14 21 27
\
19
Inserting 24 into the given Binary Search Tree.
20
/ \
13 23
/ \ / \
9 14 21 27
\ /
19 24
2.Find min:-

This operation returns the address of the smallest element of the tree when the tree is not empty.
To find the minimum element, we start at root node and we go left as long as there is a left child.
The stopping element is the smallest element of the tree.
Function:-

struct node *find_min(struct node *root)


{
if(root==NULL)
return root;
else if(root->left==NULL)
return root;
else
return find_min(root->left);
}

3.Find max:-

This operation returns the address of the biggest element of the tree when the tree is not empty.
To find the maximum element, we start at root node and we go right as long as there is a right
child.
The stopping element is the biggest element of the tree.
Function:-

struct node *find_max(struct node *root)


{
if(root==NULL)
return root;
else if(root->right==NULL)
return root;
else
return find_max(root->right);

}
4.Search:-
Searching for a node is similar to inserting a node. We start from root, and then go left or
right until we find (or not find the node). A recursive definition of search is as follows. If the root
is equal to NULL, then we return root. If the root is equal to key, then we return root.
Otherwise we recursively solve the problem for left sub tree or right, depending on key.
Function:-
struct node * search(struct node *root,int key)
{
if(root==NULL)
return root;
else if(key==root->data)
return root;
else if(key<root->data)
search(root->left,key);
else if(key>root->data)
search(root->right,key);

}
5.Delete:-

There are three different cases that needs to be considered for deleting a node from binary search
tree.

case 1: Delete a leaf node


case 2: Delete a node with one child
case 3: Delete a node with two children.

Case 1: Delete a leaf node:- This case is quite simple. Set corresponding link of the parent

to NULL and dispose that node.

1
65

19 81

15 28 72 94

25 29 96

Deletion of node 29

65

19 81

15 28 72 94

25 96

After deletion of node 29 form given BST


case 2: Delete a node with one child:- In this case, node is deleted from the tree and links
single child (with it's sub tree) directly to the parent of the removed node.

If the node to be deleted is a left child of the parent, then we connect the left pointer of the parent
(of the deleted node) to the single child. Otherwise if the node to be deleted is a right child of the
parent, then we connect the right pointer of the parent (of the deleted node) to single child.

65

19 81

15 28 72 94

25 29 96

Deletion of node 94

65

19 81

15 28 72 96

25 29

After deletion of node 94 form given BST


Case 3: Delete a node with two children:-If the node to be deleted is N, then find the
minimum value in the right sub tree of N and replace deleted node N with minimum value.
65

N
19 81

15 28 72 94

25 29 96

Deletion of node 19

65

25 81

15 28 72 94

29 96

After Deletion of node 19

Function:-
struct node *delete(struct node *root,int ele)
{
struct node *temp;
if(root==NULL)
return root;
else if(ele<root->data) /*if the node to be deleted <root key then it lies in left sub tree*/
root->left=delete(root->left,ele);
else if(ele>root->data)/*If the node to be deleted is > root then it lies in right sub tree*/
root->right=delete(root->right,ele);
else /*If key is same as root key then this is the node to be deleted*/
{
if(root->left==NULL) /* node with only one child or no child */
{
temp=root;
root=root->right;
free(temp);
}
else if(root->right==NULL)
{
temp=root;
root=root->left;
free(temp);
}
else /*node with 2 Childs*/
{
temp=find_min(root->right); /* find min value of right sub tree */
root->data=temp->data; /*replace root node with min value */
root->right=delete(root->right,temp->data); /*remove the node */
}
}
return root;
}
Ex:- Construct BST by inserting 25,10,12,15,39,64,53, and then delete 15, 10, and
insert 45, 23, and 30.
UNIT – V

Graphs

Fundamentals, Representation of graphs,Graph Traversals: BFS, DFS, Minimum cost


spanning tree: Definition, Prim’s Algorithm, Kruskal’s algorithm.
Hashing:Hash Table, Hash Function, Collison resolution Techniques- separate Chaining, open
addressing.

Definition:- Graph is a non linear data structure. A graph consists of 2 sets V, E.

(i) V is a finite set of vertices


(ii) E is a finite set of edges

Vertices are nothing but nodes of the graph where as Edges are nothing but connections between
the nodes.

So, a graph can be represented as G= (V, E).

Eg:-

For the above graph, V= {V1, V2, V3, V4}

E={ (V1, V2), (V1, V3), (V1, V4), (V2, V3), (V3, V4)}

Applications of Graph:-

1. Graphs are used in the design of computer networks.


2. To implement routing system in airlines.
3. To find the shortest path in computer networks.
4. The web pages are connected like a graph in websites.
Graph Terminology:-

1.Digraph (or) Directed graph:- It is a graph such that G=(V, E) where V is a set of vertices and E is a
set of edges with direction.

Eg:-

For the above graph, V= {V1, V2, V3, V4}

E={ (V1, V2), (V1, V3), (V2, V3), (V3, V4), (V4, V1)}

2.Undirected graph:- It is a graph such that G=(V, E) where V is a set of vertices and E is set of edges
with no direction.

Eg:-

For the above graph, V= {V1, V2, V3, V4}

E={ (V1, V2), (V1, V3), (V1, V4), (V2, V3), (V3, V4)}

3.Weighted graph:- It is a graph in which all the edges are labelled with some weights.

Eg:-
4.loop (or) self loop:- If there is an edge whose starting and ending vertices are same i.e. (Vi, Vi)
then that edge is called as Cycle.

Eg:-

In the above graph vertex V4 and V2 has self loop.

5.Cyclic Graph:- A graph that have cycle is called Cyclic graph.

Eg:-

6.Acyclic Graph:-A graph that does not have cycle is called Acyclic graph.

7.Parallel edges:- If there is more than one edge between the same pair of vertices, then they are
known as parallel edges.

Eg:-

In the above graph 2 parallel edges between vertices V1 and V2.

8. Connected Graph:-If there is an edge between every pair of distinct vertices then the graph is
called as Connected Graph.
Eg:-
A

B C

D
Representation of a Graph:- A graph can be represented in 3 ways.

1. Set representation

2. Adjacency matrix representation (or) Array representation

3. Adjacency list representation (or) Linked list representation

1. Set representation:- This is the straight forward method used for representing a graph.

In this method 2 sets are maintained V (Set of Vertices) and E (Set of Edges).

Eg1:-

V= {V1, V2, V3, V4}

E={ (V1, V2), (V1, V4), (V2, V3), (V2, V4), (V3, V4)}

Eg2:-

V= {V1, V2, V3, V4}

E={ (V1, V2), (V1, V4), (V2, V3), (V3, V1), (V4, V3)}

2. Adjacency matrix representation (or) Array representation:-

This representation uses a square matrix of order n*n, where n is number of vertices in graph.

Consider a graph ‘G’ with ‘n’ number of vertices and adjacency matrix “adj”.
If there is an edge present between vertices Vi and Vj then adj[i][j]=1 otherwise adj[i][j]=0.

In an Undirected graph if adj[i][j]=1 then adj[j][i]=1

3. Adjacency list representation (or) Linked list representation:- We uses linked list representation
of graphs when number of vertices of the graph is not known in advance.

Eg1:-

Eg2:-
Graph searching techniques:- There are 2 searching techniques.

1. Breadth First Search (BFS)


2. Depth First Search (DFS)

1.Breadth First Search (BFS):- BFS can be called as Level-by-Level traversal.

Here any vertex at level ‘i’ will be visited only after visiting all the vertices present at the preceding
level i-1.

BFS uses queue data structure for its implementation.

Initially each vertex of the graph is in Unvisited State.

Algorithm:-

Input:- Adjacent matrix of a graph

Output:- BFS traversal of a graph

1. Take any vertex as starting vertex, enqueue that vertex and set its status as visited.

2. While queue is not empty

a) DEQUEUE a vertex V from queue and print it.

b) ENQUEUE all the unvisited adjacent vertices of V into queue and set their status as visited.

C Program to implement BFS:-

#include<stdio.h>

#include<conio.h>

#define MAX 6

main()

{
int visited[MAX]={0};

int adj[MAX][MAX],i,j;

printf("\nenter adjacency matrix\n");

for(i=0;i<MAX;i++)

for(j=0;j<MAX;j++)

scanf("%d",&adj[i][j]);

bfs(adj,visited,0);

getch();

void bfs(int adj[][MAX],int visited[], int start)

int queue[MAX],rear=-1,front=-1,i;

queue[++rear]=start;

visited[start]=1;

while(rear!=front)

start=queue[++front];

printf("%c\t",start+65);

for(i=0;i<MAX;i++)

if(adj[start][i]==1 && visited[i]==0)

queue[++rear]=i;

visited[i]=1;

}
}

2.Depth First Search (DFS):-

The basic principle of DFS is quite sample, visit all the vertices up to the deepest level and so on.

DFS uses stack for its implementation.

Initially all the vertices of the graph are in unvisited state.

Algorithm:-

Input:-Adjacency matrix representation of a graph

Output:- DFS traversal of graph

1. Take any vertex as starting vertex, push that vertex onto a stack and set its status as visited.
2. While stack is not empty
a) pop a vertex V from stack and print it.
b) push all the unvisited neighbours of vertex onto a stack and set their status as visited.

C Program to implement DFS:-

#include<stdio.h>

#include<conio.h>

#define MAX 4

void dfs(int [][],int [],int);

main()

int visited[MAX]={0};

int adj[MAX][MAX],i,j;

printf("\nenter adjacency matrix\n");

for(i=0;i<MAX;i++)

for(j=0;j<MAX;j++)

scanf("%d",&adj[i][j]);

dfs(adj,visited,0);

getch();

}
void dfs(int adj[MAX][MAX],int visited[], int start)

int stack[MAX],top=-1,i;

stack[++top]=start;

visited[start]=1;

while(top!=-1)

start=stack[top--];

printf("%c\t",start+65);

for(i=0;i<MAX;i++)

if(adj[start][i]==1 && visited[i]==0)

stack[++top]=i;

visited[i]=1;

}
/* Binary Tree & its traversals */
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct bt
{
struct bt *left;
int key;
struct bt *right;
}*root=NULL,*new1,*temp;

void create();
void insert(struct bt *,struct bt *);
void inorder(struct bt*);
void preorder(struct bt*);
void postorder(struct bt*);

void create()
{
int ele;
new1=(struct bt*)malloc(sizeof(struct bt));
new1->left=NULL;
new1->right=NULL;
printf("enter data ");
scanf("%d",&ele);
new1->key=ele;
if(root==NULL)
root=new1;
else
insert(root,new1);
}
void insert(struct bt *root,struct bt *new1)
{
char ch;
printf("enter l toinsert left child or r for right child\n");
fflush(stdin);
scanf("%c",&ch);
switch(ch)
{
case 'l':
if(root->left==NULL)
root->left=new1;
else
insert(root->left,new1);
break;
case 'r':if(root->right==NULL)
root->right=new1;
else
insert(root->right,new1);
break;

}
}
void inorder(struct bt *temp)
{
if(temp!=NULL)
{
inorder(temp->left);
printf(" %d",temp->key);
inorder(temp->right);
}
}

void preorder(struct bt *temp)


{
if(temp!=NULL)
{
printf(" %d",temp->key);
preorder(temp->left);
preorder(temp->right);
}
}

void postorder(struct bt *temp)


{

if(temp!=NULL)
{
postorder(temp->left);
postorder(temp->right);
printf(" %d",temp->key);
}
}

void main()
{
int choice,ele;
while(1)
{
clrscr();
printf("\t \t binary Tree");
printf("\n 1.create & insert");
printf("\n 2.ioorder traversal");
printf("\n 3.preorder traversal");
printf("\n 4.post order traversal");
printf("\n 5 exit");
printf("\nenter choic");
scanf("%d",&choice);
switch(choice)
{
case 1:create();
break;
case 2:inorder(root);
getch();
break;
case 3:preorder(root);
getch();
break;
case 4:postorder(root);
getch();
break;

case 5:exit(0);
}
}
}
/*Binary Search Tree Program */
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct bst
{
struct bst *left;
int key;
struct bst *right;
}*root=NULL,*new1,*temp,*parent;
void create();
void display();
struct bst* find(int );
void findmax();
struct bst* findmin(struct bst *q);
void insert(struct bst *,struct bst *);
void inorder(struct bst*);
void delete1(struct bst *,int);
void create()
{
int ele;
new1=(struct bst*)malloc(sizeof(struct bst));
new1->left=NULL;
new1->right=NULL;
printf("enter data ");
scanf("%d",&ele);
new1->key=ele;
if(root==NULL)
root=new1;
else
insert(root,new1);
}
void insert(struct bst *root,struct bst *new1)
{
if(new1->key<root->key)
{
if(root->left==NULL)
root->left=new1;
else
insert(root->left,new1);
}
else if(new1->key>root->key)
{
if(root->right==NULL)
root->right=new1;
else
insert(root->right,new1);
}
}
void display()
{
if(root==NULL)
printf("\n trre is not creayed");
else
inorder(root);
}
void inorder(struct bst *temp)
{
if(temp!=NULL)
{
inorder(temp->left);
printf(" %d",temp->key);
inorder(temp->right);
}
}
struct bst* find(int x)
{
struct bst *par;
int found=0;
temp=root;
if(temp==NULL)
printf("\n element not found");
else
{
while(temp!=NULL)
{
if(temp->key==x)
{
found=1;
break;
}

else if(x<temp->key)
{
parent=temp;
temp=temp->left;
}
else
{
parent=temp;
temp=temp->right;
}
}
if(found==1)
{
printf("node present in bst with key=%d",temp->key);
}
else
printf("node is not present with key=%d",x);
return temp;

}
}
void findmax()
{
temp=root;
while(temp->right!=NULL)
{
temp=temp->right;
}
printf("\n maxium element of bst is%d ",temp->key);
getch();
}
struct bst * findmin(struct bst *p)
{
struct bst *par;
temp=p;
while(temp->left!=NULL)
{
par=temp;
temp=temp->left;
}
printf("\n minium element of bst is%d ",temp->key);
getch();

return par;

}
void delete1(struct bst *temp,int x)
{
struct bst *p,*q;
if(temp==NULL)
printf("bst is empty \n");
else if(temp->left==NULL & temp->right==NULL)
{
free(temp);
root=NULL;
}

while(temp!=NULL)
{
if(temp->key==x)
break;

else if(x<temp->key)
{
parent=temp;
temp=temp->left;
}
else
{
parent=temp;
temp=temp->right;
}

printf("temp =%d",temp->key);
printf("parent =%d",parent->key);
getch();
/* deleting leaf node */

if(temp->left==NULL && temp->right==NULL)


{
if(parent->left ==temp)
parent->left=NULL;
else
parent->right=NULL;
free(temp);
}
/*deleting non leaf node with one right child*/
else if(temp->left==NULL && temp->right!=NULL)
{
if(temp->key>parent->key)
parent->right=temp->right;
else
parent->left=temp->right;
free(temp);
}
/* deleting non leaf node with one left child*/
else if(temp->left!=NULL && temp->right==NULL)
{
if(temp->key<parent->key)
parent->left=temp->left;
else
parent->right=temp->left;
free(temp);
}

/*deleting non leaf node with two children */

else
{
if(temp->left!=NULL && temp->right!=NULL)
{
p=findmin(temp->right);
q=p->left;
temp->key=q->key;
p->left=NULL;
free(q);
}

}
}

void main()
{
int choice,ele;
while(1)
{
clrscr();
printf("\t \t binary search tree");
printf("\n 1.create & insert");
printf("\n 2. display tree");
printf("\n 3.find");
printf("\n 4.find max");
printf("\n 5 find min");
printf("\n 6.delete ");
printf("\n 7.exit");
printf("\nenter choic");
scanf("%d",&choice);
switch(choice)
{
case 1:create();
break;
case 2:display();getch();
break;
case 3:printf("\n enter ele to search");
scanf("%d",&ele);
temp=find(ele);

getch();
break;
case 4:findmax();
break;
case 5:findmin(root);
break;
case 6: printf("\n enter ele to delete");
scanf("%d",&ele);

delete1(root,ele);
break;
case 7:exit(0);
}
}
}

/* Heap Sort using MaxHeapify */


#include<stdio.h>
#include<conio.h>
int a[10];
void Maxheapify(int,int);
void heapsort(int n);
int main()
{
int n,i,j;
clrscr();
printf("enter the number of elemens]n");
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
heapsort(n);
printf("elemensts after sorted\n");
for(i=1;i<=n;i++)
printf("%d ",a[i]);
getch();
return 0;
}
void heapsort(int n)
{
int i,temp;
for(i=n/2;i>=1;i--)
Maxheapify(n,i);
printf("Max Heap elements \n");
for(i=1;i<=n;i++)
printf("%d ",a[i]);
for(i=n;i>=1;i--)
{
temp=a[1];
a[1]=a[n];
a[n]=temp;
n=n-1;
Maxheapify(n,1);
}
}

void Maxheapify(int n, int i)


{
int largest=i,l,r,temp;
l=2*i;
r=2*i+1;
while(l<=n && a[l]>a[largest])
largest=l;
while(r<=n && a[r]>a[largest])
largest=r;
if(largest!=i)
{
temp=a[largest];
a[largest]=a[i];
a[i]=temp;
Maxheapify(n,largest);
}
}
AVL Tree Example:
• Insert 14, 17, 11, 7, 53, 4, 13 into an empty AVL tree

14

11 17

7 53

4
AVL Tree Example:
• Insert 14, 17, 11, 7, 53, 4, 13 into an empty AVL tree

14

7 17

4 11 53

13
AVL Tree Example:
• Now insert 12

14

7 17

4 11 53

13

12
AVL Tree Example:
• Now insert 12

14

7 17

4 11 53

12

13
AVL Tree Example:
• Now the AVL tree is balanced.

14

7 17

4 12 53

11 13
AVL Tree Example:
• Now insert 8

14

7 17

4 12 53

11 13

8
AVL Tree Example:
• Now insert 8

14

7 17

4 11 53

8 12

13
AVL Tree Example:
• Now the AVL tree is balanced.

14

11 17

7 12 53

4 8 13
AVL Tree Example:
• Now remove 53

14

11 17

7 12 53

4 8 13
AVL Tree Example:
• Now remove 53, unbalanced

14

11 17

7 12

4 8 13
AVL Tree Example:
• Balanced! Remove 11

11

7 14

4 8 12 17

13
AVL Tree Example:
• Remove 11, replace it with the largest in its left branch

7 14

4 12 17

13
AVL Tree Example:
• Remove 8, unbalanced

4 14

12 17

13
AVL Tree Example:
• Remove 8, unbalanced

4 12

14

13 17
AVL Tree Example:
• Balanced!!

12

7 14

4 13 17
In Class Exercises
• Build an AVL tree with the following values:
15, 20, 24, 10, 13, 7, 30, 36, 25
15, 20, 24, 10, 13, 7, 30, 36, 25
20

15
15 24
20
10
24

13

20 20

13 24 15 24

10 15 13

10
15, 20, 24, 10, 13, 7, 30, 36, 25

20
13

13 24 10 20

10 15 7 15 24

7 30

13 36

10 20

7 15 30

24 36
15, 20, 24, 10, 13, 7, 30, 36, 25

13 13

10 20 10 20

7 15 30 7 15 24

24 36 30

25 13 25 36

10 24

7 20 30

15 25 36
Remove 24 and 20 from the AVL tree.

13 13

10 24 10 20

7 20 30 7 15 30

15 25 36 25 36

13 13

10 30 10 15

7 15 36 7 30

25 25 36
Trees 4: AVL Trees
• Section 4.4

1
Motivation
• When building a binary search tree, what type of
trees would we like? Example: 3, 5, 8, 20, 18, 13, 22

3
5
13
8
5 20
13
18 3 8 18 22
20

22
2
Motivation
• Complete binary tree is hard to build when we allow
dynamic insert and remove.
– We want a tree that has the following properties
• Tree height = O(log(N))
• allows dynamic insert and remove with O(log(N)) time
complexity.
– The AVL tree is one of this kind of trees.
8
13
5 18
5 20
3 13 20
3 8 18 22 22
3
AVL (Adelson-Velskii and Landis) Trees

• An AVL Tree is a 4
44
binary search tree
2 3
such that for every 17 78
internal node v of T, 1 2 1
32 50 88
the heights of the
1
children of v can differ 48 62
1

by at most 1.

An example of an AVL tree where the


heights are shown next to the nodes:
4
AVL (Adelson-Velskii and Landis) Trees
• AVL tree is a binary search tree with balance condition
– To ensure depth of the tree is O(log(N))
– And consequently, search/insert/remove complexity bound
O(log(N))
• Balance condition
– For every node in the tree, height of left and right subtree can
differ by at most 1

5
Which is an AVL Tree?

6
Height of an AVL tree
• Theorem: The height of an AVL tree storing n keys is O(log
n).
• Proof:
– Let us bound n(h), the minimum number of internal nodes of an
AVL tree of height h.
– We easily see that n(0) = 1 and n(1) = 2
– For h > 2, an AVL tree of height h contains the root node, one AVL
subtree of height h-1 and another of height h-2 (at worst).
– That is, n(h) >= 1 + n(h-1) + n(h-2)
– Knowing n(h-1) > n(h-2), we get n(h) > 2n(h-2). So
n(h) > 2n(h-2), n(h) > 4n(h-4), n(h) > 8n(n-6), … (by
induction),
n(h) > 2in(h-2i)
– Solving the base case we get: n(h) > 2 h/2-1
– Taking logarithms: h < 2log n(h) +2
– Since n>=n(h), h < 2log(n)+2 and the height of an AVL tree is
O(log n)

7
AVL Tree Insert and Remove
• Do binary search tree insert and remove
• The balance condition can be violated
sometimes
– Do something to fix it : rotations
– After rotations, the balance of the whole tree is
maintained

8
Balance Condition Violation
• If condition violated after a node insertion
– Which nodes do we need to rotate?
– Only nodes on path from insertion point to root may have their
balance altered
• Rebalance the tree through rotation at the deepest node with
balance violated
– The entire tree will be rebalanced
• Violation cases at node k (deepest node)
1. An insertion into left subtree of left child of k
2. An insertion into right subtree of left child of k
3. An insertion into left subtree of right child of k
4. An insertion into right subtree of right child of k
– Cases 1 and 4 equivalent
• Single rotation to rebalance
– Cases 2 and 3 equivalent
• Double rotation to rebalance
9
AVL Trees Complexity
• Overhead
– Extra space for maintaining height information at each node
– Insertion and deletion become more complicated, but still
O(log N)
• Advantage
– Worst case O(log(N)) for insert, delete, and search

10
Single Rotation (Case 1)

• Replace node k2 by node k1


• Set node k2 to be right child of node k1
• Set subtree Y to be left child of node k2
• Case 4 is similar

11
Example

• After inserting 6
– Balance condition at node 8 is violated

12
Single Rotation (Case 1)

13
Example
• Inserting 3, 2, 1, and then 4 to 7 sequentially into
empty AVL tree

3
2
2
1 3
1

14
Example (Cont’d)
• Inserting 4
2

1 3

• Inserting 5
2 2

1 3 1 4
4 3 5

5 15
Example (Cont’d)
• Inserting 6 4
2
2 5
1 4
1 3 6
3 5

• Inserting 7 6

4 4

2 5 2 6

6 1 3 5 7
1 3
7 16
Single Rotation Will Not Work for the
Other Case

• For case 2
• After single rotation, k1 still not balanced
• Double rotations needed for case 2 and case 3

17
Double Rotation (Case 2)

• Left-right double rotation to fix case 2


• First rotate between k1 and k2
• Then rotate between k2 and k3
• Case 3 is similar

18
Example
• Continuing the previous example by inserting
– 16 down to 10, and then 8 and 9

• Inserting 16 and 15
4
4
2 6
2 6
1 3 5 15
1 3 5 7
16 7 16

15

19
Example (Cont’d)
• Inserting 14

4 4

2 6 2 7

15 1 3 6 15
1 3 5
7 16 5 14 16

14

• Other cases as exercises

20
Double Rotation (Case 2)

21
Summary
Violation cases at node k (deepest node)
1. An insertion into left subtree of left child of k
2. An insertion into right subtree of left child of k
3. An insertion into left subtree of right child of k
4. An insertion into right subtree of right child of k

Case 1
Case 2

Case 4?
Case 3 22
Implementation of AVL Tree

23
Case 1
Case 2

Case 4
Case 3

24
Single Rotation (Case 1)

25
Double Rotation (Case 2)

26
Review Insertion -- Case 1
h+2

h+1
Height = h

h
h
Before insert

h+2

h+1
h+2
h h+1
h+1 h h h

After rotation
27
After insert
Review Insertion -- Case 2

Determine all heights


Height = h

Before insert

After insert After double rotation


28
Delete -- Case 1
• Consider deepest unbalanced node
– Case 1: Left child’s left side is too high
h+2 – Case 4: Right child’s right side is too high
– The parents may need to be recursively
h+1 rotated
Height = h
h/h-1
h Delete Before Deletion

h+1/h+2

h/h+1
h+1
h-1 h
h/h-1 h/h-1 h-1
h

After single rotation


29
After delete
Delete -- Case 2
• Consider deepest unbalanced node
– Case 2: Left child’s right side is too high
– Case 3: Right child’s left side is too high
– The parents may need to be recursively rotated
Height = h

Determine all heights


Delete
Before Deletion

After Delete After double rotation


30
COMP171
Fall 2005

Binary Trees,
Binary Search Trees
Binary Search Trees / Slide 2

Trees
 Linear access time of linked lists is prohibitive
 Does there exist any simple data structure for
which the running time of most operations (search,
insert, delete) is O(log N)?
Binary Search Trees / Slide 3

Trees
 A tree is a collection of nodes
 The collection can be empty
 (recursive definition) If not empty, a tree consists of
a distinguished node r (the root), and zero or more
nonempty subtrees T1, T2, ...., Tk, each of whose
roots are connected by a directed edge from r
Binary Search Trees / Slide 4

Some Terminologies

 Child and parent


 Every node except the root has one parent
 A node can have an arbitrary number of children
 Leaves
 Nodes with no children
 Sibling
 nodes with same parent
Binary Search Trees / Slide 5

Some Terminologies
 Path
 Length
 number of edges on the path
 Depth of a node
 length of the unique path from the root to that node
 The depth of a tree is equal to the depth of the deepest leaf
 Height of a node
 length of the longest path from that node to a leaf
 all leaves are at height 0
 The height of a tree is equal to the height of the root
 Ancestor and descendant
 Proper ancestor and proper descendant
Binary Search Trees / Slide 6

Example: UNIX Directory


Binary Search Trees / Slide 7

Binary Trees
 A tree in which no node can have more than two children

 The depth of an “average” binary tree is considerably smaller


than N, eventhough in the worst case, the depth can be as large
as N – 1.
Binary Search Trees / Slide 8

Example: Expression Trees

 Leaves are operands (constants or variables)


 The other nodes (internal nodes) contain operators
 Will not be a binary tree if some operators are not binary
Binary Search Trees / Slide 9

Tree traversal
 Used to print out the data in a tree in a certain
order
 Pre-order traversal
 Print the data at the root
 Recursively print out all data in the left subtree
 Recursively print out all data in the right subtree
Binary Search Trees / Slide 10

Preorder, Postorder and Inorder


 Preorder traversal
 node, left, right
 prefix expression
++a*bc*+*defg
Binary Search Trees / Slide 11

Preorder, Postorder and Inorder


 Postorder traversal
 left, right, node
 postfix expression
abc*+de*f+g*+

 Inorder traversal
 left, node, right.
 infix expression
a+b*c+d*e+f*g
Binary Search Trees / Slide 12

 Preorder
Binary Search Trees / Slide 13

 Postorder
Binary Search Trees / Slide 14

Preorder, Postorder and Inorder


Binary Search Trees / Slide 15

Binary Trees
 Possible operations on the Binary Tree ADT
 parent
 left_child, right_child
 sibling
 root, etc
 Implementation
 Because a binary tree has at most two children, we can keep
direct pointers to them
Binary Search Trees / Slide 16

compare: Implementation of a general tree


Binary Search Trees / Slide 17

Binary Search Trees


 Stores keys in the nodes in a way so that searching,
insertion and deletion can be done efficiently.
Binary search tree property
 For every node X, all the keys in its left subtree are smaller
than the key value in X, and all the keys in its right subtree
are larger than the key value in X
Binary Search Trees / Slide 18

Binary Search Trees

A binary search tree Not a binary search tree


Binary Search Trees / Slide 19

Binary search trees


Two binary search trees representing
the same set:

 Average depth of a node is O(log N);


maximum depth of a node is O(N)
Binary Search Trees / Slide 20

Implementation
Binary Search Trees / Slide 21

Searching BST
 If we are searching for 15, then we are done.
 If we are searching for a key < 15, then we
should search in the left subtree.
 If we are searching for a key > 15, then we
should search in the right subtree.
Binary Search Trees / Slide 22
Binary Search Trees / Slide 23

Searching (Find)
 Find X: return a pointer to the node that has key X, or
NULL if there is no such node

 Time complexity
 O(height of the tree)
Binary Search Trees / Slide 24

Inorder traversal of BST


 Print out all the keys in sorted order

Inorder: 2, 3, 4, 6, 7, 9, 13, 15, 17, 18, 20


Binary Search Trees / Slide 25

findMin/ findMax
 Return the node containing the smallest element in
the tree
 Start at the root and go left as long as there is a left
child. The stopping point is the smallest element

 Similarly for findMax


 Time complexity = O(height of the tree)
Binary Search Trees / Slide 26

insert
 Proceed down the tree as you would with a find
 If X is found, do nothing (or update something)
 Otherwise, insert X at the last spot on the path traversed

 Time complexity = O(height of the tree)


Binary Search Trees / Slide 27

delete
 When we delete a node, we need to consider
how we take care of the children of the
deleted node.
 This has to be done such that the property of the
search tree is maintained.
Binary Search Trees / Slide 28

delete
Three cases:
(1) the node is a leaf
 Delete it immediately
(2) the node has one child
 Adjust a pointer from the parent to bypass that node
Binary Search Trees / Slide 29

delete
(3) the node has 2 children
 replace the key of that node with the minimum element at the
right subtree
 delete the minimum element
Has either no child or only right child because if it has a left
child, that left child would be smaller and would have been
chosen. So invoke case 1 or 2.

 Time complexity = O(height of the tree)


Bubble Sort
Merge Sort
Bubble Sort
Sorting
• Sorting takes an unordered collection and
makes it an ordered one.
1 2 3 4 5 6

77 42 35 12 101 5

1 2 3 4 5 6
5 12 35 42 77 101
"Bubbling Up" the Largest Element

• Traverse a collection of elements


– Move from the front to the end
– “Bubble” the largest value to the end using
pair-wise comparisons and swapping

1 2 3 4 5 6

77 42 35 12 101 5
"Bubbling Up" the Largest Element

• Traverse a collection of elements


– Move from the front to the end
– “Bubble” the largest value to the end using
pair-wise comparisons and swapping

1 2 3 4 5 6
42Swap
77 77
42 35 12 101 5
"Bubbling Up" the Largest Element

• Traverse a collection of elements


– Move from the front to the end
– “Bubble” the largest value to the end using
pair-wise comparisons and swapping

1 2 3 4 5 6

42 35Swap35
77 77 12 101 5
"Bubbling Up" the Largest Element

• Traverse a collection of elements


– Move from the front to the end
– “Bubble” the largest value to the end using
pair-wise comparisons and swapping

1 2 3 4 5 6

42 35 12Swap12
77 77 101 5
"Bubbling Up" the Largest Element

• Traverse a collection of elements


– Move from the front to the end
– “Bubble” the largest value to the end using
pair-wise comparisons and swapping

1 2 3 4 5 6

42 35 12 77 101 5

No need to swap
"Bubbling Up" the Largest Element

• Traverse a collection of elements


– Move from the front to the end
– “Bubble” the largest value to the end using
pair-wise comparisons and swapping

1 2 3 4 5 6

42 35 12 77 5 Swap101
101 5
"Bubbling Up" the Largest Element

• Traverse a collection of elements


– Move from the front to the end
– “Bubble” the largest value to the end using
pair-wise comparisons and swapping

1 2 3 4 5 6

42 35 12 77 5 101

Largest value correctly placed


The “Bubble Up” Algorithm

index <- 1
last_compare_at <- n – 1

loop
exitif(index > last_compare_at)
if(A[index] > A[index + 1]) then
Swap(A[index], A[index + 1])
endif
index <- index + 1
endloop
LB

No, Swap isn’t built in.

Procedure Swap(a, b isoftype in/out Num)


t isoftype Num
t <- a
a <- b
b <- t
endprocedure // Swap
Items of Interest
• Notice that only the largest value is
correctly placed
• All other values are still out of order
• So we need to repeat this process

1 2 3 4 5 6

42 35 12 77 5 101

Largest value correctly placed


Repeat “Bubble Up” How Many Times?
• If we have N elements…

• And if each time we bubble an element,


we place it in its correct location…

• Then we repeat the “bubble up”


process N – 1 times.

• This guarantees we’ll correctly


place all N elements.
“Bubbling” All the Elements
1 2 3 4 5 6
42 35 12 77 5 101
1 2 3 4 5 6
35 12 42 5 77 101
1 2 3 4 5 6
N-1

12 35 5 42 77 101
1 2 3 4 5 6
12 5 35 42 77 101
1 2 3 4 5 6
5 12 35 42 77 101
Reducing the Number of Comparisons
1 2 3 4 5 6
77 42 35 12 101 5
1 2 3 4 5 6
42 35 12 77 5 101
1 2 3 4 5 6
35 12 42 5 77 101
1 2 3 4 5 6
12 35 5 42 77 101
1 2 3 4 5 6
12 5 35 42 77 101
Reducing the Number of Comparisons
• On the Nth “bubble up”, we only need to
do MAX-N comparisons.

• For example:
– This is the 4th “bubble up”
– MAX is 6
– Thus we have 2 comparisons to do

1 2 3 4 5 6
12 35 5 42 77 101
Putting It All Together
N is … // Size of Array

Arr_Type definesa Array[1..N] of Num

Procedure Swap(n1, n2 isoftype in/out Num)


temp isoftype Num
temp <- n1
n1 <- n2
n2 <- temp
endprocedure // Swap
procedure Bubblesort(A isoftype in/out Arr_Type)
to_do, index isoftype Num
to_do <- N – 1

loop
exitif(to_do = 0)
index <- 1
loop
exitif(index > to_do)

Outer loop
Inner loop
if(A[index] > A[index + 1]) then
Swap(A[index], A[index + 1])
endif
index <- index + 1
endloop
to_do <- to_do - 1
endloop
endprocedure // Bubblesort
Already Sorted Collections?
• What if the collection was already sorted?
• What if only a few elements were out of place and
after a couple of “bubble ups,” the collection was
sorted?

• We want to be able to detect this


and “stop early”!
1 2 3 4 5 6
5 12 35 42 77 101
Using a Boolean “Flag”

• We can use a boolean variable to determine if any


swapping occurred during the “bubble up.”

• If no swapping occurred, then we know that the


collection is already sorted!

• This boolean “flag” needs to be reset after each


“bubble up.”
did_swap isoftype Boolean
did_swap <- true

loop
exitif ((to_do = 0) OR NOT(did_swap))
index <- 1
did_swap <- false
loop
exitif(index > to_do)
if(A[index] > A[index + 1]) then
Swap(A[index], A[index + 1])
did_swap <- true
endif
index <- index + 1
endloop
to_do <- to_do - 1
endloop
An Animated Example

N 8 did_swap true
to_do 7
index

98 23 45 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap false
to_do 7
index 1

98 23 45 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap false
to_do 7
index 1

Swap

98 23 45 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 1

Swap

23 98 45 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 2

23 98 45 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 2

Swap

23 98 45 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 2

Swap

23 45 98 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 3

23 45 98 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 3

Swap

23 45 98 14 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 3

Swap

23 45 14 98 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 4

23 45 14 98 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 4

Swap

23 45 14 98 6 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 4

Swap

23 45 14 6 98 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 5

23 45 14 6 98 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 5

Swap

23 45 14 6 98 67 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 5

Swap

23 45 14 6 67 98 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 6

23 45 14 6 67 98 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 6

Swap

23 45 14 6 67 98 33 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 6

Swap

23 45 14 6 67 33 98 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 7

23 45 14 6 67 33 98 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 7

Swap

23 45 14 6 67 33 98 42
1 2 3 4 5 6 7 8
An Animated Example

N 8 did_swap true
to_do 7
index 7

Swap

23 45 14 6 67 33 42 98
1 2 3 4 5 6 7 8
After First Pass of Outer Loop

N 8 did_swap true
to_do 7
index 8 Finished first “Bubble Up”

23 45 14 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap false
to_do 6
index 1

23 45 14 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap false
to_do 6
index 1

No Swap

23 45 14 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap false
to_do 6
index 2

23 45 14 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap false
to_do 6
index 2

Swap

23 45 14 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 2

Swap

23 14 45 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 3

23 14 45 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 3

Swap

23 14 45 6 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 3

Swap

23 14 6 45 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 4

23 14 6 45 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 4

No Swap

23 14 6 45 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 5

23 14 6 45 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 5

Swap

23 14 6 45 67 33 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 5

Swap

23 14 6 45 33 67 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 6

23 14 6 45 33 67 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 6

Swap

23 14 6 45 33 67 42 98
1 2 3 4 5 6 7 8
The Second “Bubble Up”

N 8 did_swap true
to_do 6
index 6

Swap

23 14 6 45 33 42 67 98
1 2 3 4 5 6 7 8
After Second Pass of Outer Loop

N 8 did_swap true
to_do 6
index 7 Finished second “Bubble Up”

23 14 6 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap false
to_do 5
index 1

23 14 6 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap false
to_do 5
index 1

Swap

23 14 6 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 1

Swap

14 23 6 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 2

14 23 6 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 2

Swap

14 23 6 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 2

Swap

14 6 23 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 3

14 6 23 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 3

No Swap

14 6 23 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 4

14 6 23 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 4

Swap

14 6 23 45 33 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 4

Swap

14 6 23 33 45 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 5

14 6 23 33 45 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 5

Swap

14 6 23 33 45 42 67 98
1 2 3 4 5 6 7 8
The Third “Bubble Up”

N 8 did_swap true
to_do 5
index 5

Swap

14 6 23 33 42 45 67 98
1 2 3 4 5 6 7 8
After Third Pass of Outer Loop

N 8 did_swap true
to_do 5
index 6 Finished third “Bubble Up”

14 6 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap false
to_do 4
index 1

14 6 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap false
to_do 4
index 1

Swap

14 6 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap true
to_do 4
index 1

Swap

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap true
to_do 4
index 2

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap true
to_do 4
index 2

No Swap

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap true
to_do 4
index 3

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap true
to_do 4
index 3

No Swap

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap true
to_do 4
index 4

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fourth “Bubble Up”

N 8 did_swap true
to_do 4
index 4

No Swap

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
After Fourth Pass of Outer Loop

N 8 did_swap true
to_do 4
index 5 Finished fourth “Bubble Up”

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fifth “Bubble Up”

N 8 did_swap false
to_do 3
index 1

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fifth “Bubble Up”

N 8 did_swap false
to_do 3
index 1

No Swap

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fifth “Bubble Up”

N 8 did_swap false
to_do 3
index 2

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fifth “Bubble Up”

N 8 did_swap false
to_do 3
index 2

No Swap

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fifth “Bubble Up”

N 8 did_swap false
to_do 3
index 3

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
The Fifth “Bubble Up”

N 8 did_swap false
to_do 3
index 3

No Swap

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
After Fifth Pass of Outer Loop

N 8 did_swap false
to_do 3
index 4 Finished fifth “Bubble Up”

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
Finished “Early”

N 8 did_swap false
to_do 3
We didn’t do any swapping,
index 4 so all of the other elements
must be correctly placed.

We can “skip” the last two


passes of the outer loop.

6 14 23 33 42 45 67 98
1 2 3 4 5 6 7 8
Summary
• “Bubble Up” algorithm will move largest
value to its correct location (to the right)
• Repeat “Bubble Up” until all elements are
correctly placed:
– Maximum of N-1 times
– Can finish early if no swapping occurs
• We reduce the number of elements we
compare each time one is correctly placed
LB

Truth in CS Act

• NOBODY EVER USES BUBBLE SORT

• NOBODY

• NOT EVER

• BECAUSE IT IS EXTREMELY INEFFICIENT


Questions?
Mergesort
Sorting
• Sorting takes an unordered collection and
makes it an ordered one.
1 2 3 4 5 6

77 42 35 12 101 5

1 2 3 4 5 6
5 12 35 42 77 101
Divide and Conquer

• Divide and Conquer cuts the problem in


half each time, but uses the result of both
halves:
– cut the problem in half until the problem
is trivial
– solve for both halves
– combine the solutions
Mergesort
• A divide-and-conquer algorithm:
• Divide the unsorted array into 2 halves until the
sub-arrays only contain one element
• Merge the sub-problem solutions together:
– Compare the sub-array’s first elements
– Remove the smallest element and put it into
the result array
– Continue the process until all elements have
been put into the result array

37 23 6 89 15 12 2 19
How to Remember Merge Sort?
That’s easy. Just
remember Mariah
Carey.

As a singing star, Ms.


Carey has perfected
the “wax-on” wave
motion--a clockwise
sweep of her hand
used to emphasize
lyrics.

The Maria
“Wax-on” Angle:
(q,t)
The Siren of Subquadratic Sorts
How To Remember Merge Sort?

(q,t)

Just as Mariah recursively moves


her hands into smaller circles, so
too does merge sort recursively We need two such recursions,
split an array into smaller one for each half of the split
segments. array.
Algorithm
Mergesort(Passed an array)
if array size > 1
Divide array in half
Call Mergesort on first half.
Call Mergesort on second half.
Merge two halves.

Merge(Passed two arrays)


Compare leading element in each array
Select lower and place in new array.
(If one input array is empty then place
remainder of other array in output array)
LB

More TRUTH in CS

• We don’t really pass in two arrays!

• We pass in one array with indicator variables which


tell us where one set of data starts and finishes and
where the other set of data starts and finishes.

• Honest.

s1 f1 s2 f2
LB

Algorithm
Mergesort(Passed an array)
if array size > 1
Divide array in half
Call Mergesort on first half.
Call Mergesort on second half.
Merge two halves.

Merge(Passed two arrays)


Compare leading element in each array
Select lower and place in new array.
(If one input array is empty then place
remainder of other array in output array)
98 23 45 14 6 67 33 42
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23

23

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23

23 98

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98 14

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98 14 45

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98 14 45

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98 14 45

14

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98 14 45

14 23

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98 14 45

14 23 45

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

98 23 45 14

23 98 14 45

14 23 45 98

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14

23 98 14 45

14 23 45 98
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67

23 98 14 45

14 23 45 98
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67

23 98 14 45

14 23 45 98 Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67

23 98 14 45 6

14 23 45 98 Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67

23 98 14 45 6 67

14 23 45 98 Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67

14 23 45 98
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67

14 23 45 98 Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33

14 23 45 98 Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14 23

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14 23 33

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14 23 33 42

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14 23 33 42 45

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14 23 33 42 45 67

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14 23 33 42 45 67 98

Merge
98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

98 23 45 14 6 67 33 42

23 98 14 45 6 67 33 42

14 23 45 98 6 33 42 67

6 14 23 33 42 45 67 98
98 23 45 14 6 67 33 42

6 14 23 33 42 45 67 98
Summary

• Divide the unsorted collection into two

• Until the sub-arrays only contain one


element

• Then merge the sub-problem solutions


together
Questions?
Review?
Linear Search

 The linear search is a sequential search, which uses a loop to


step through an array, starting with the first element.
 It compares each element with the value being searched
for, and stops when either the value is found or the end of
the array is encountered.
 If the value being searched is not in the array, the algorithm
will unsuccessfully search to the end of the array.
Linear Search

 Since the array elements are stored in linear order


searching the element in the linear order make it easy and
efficient.
 The search may be successful or unsuccessfully. That is, if
the required element is found them the search is successful
other wise it is unsuccessfully.
Advantages

 The linear search is simple - It is very easy to understand


and implement
 It does not require the data in the array to be stored in
any particular order
Disadvantages

 It has very poor efficiency because it takes lots of


comparisons to find a particular record in big files
 The performance of the algorithm scales linearly with the
size of the input
 Linear search is slower then other searching algorithms
Linear Search Example

-23 97 18 21 5 -86 64 0 -37

element

Searching for -86.


Linear Search Example

-23 97 18 21 5 -86 64 0 -37

element

Searching for -86.


Linear Search Example

-23 97 18 21 5 -86 64 0 -37

element

Searching for -86.


Linear Search Example

-23 97 18 21 5 -86 64 0 -37

element

Searching for -86.


Linear Search Example

-23 97 18 21 5 -86 64 0 -37

element

Searching for -86.


Linear Search Example

-23 97 18 21 5 -86 64 0 -37

element

Searching for -86: found!


10
Analysis of Linear
Search

How long will our search take?

In the best case, the target value is in the first element of the
array.
So the search takes some tiny, and constant, amount of time.
In the worst case, the target value is in the last element of the
array.
So the search takes an amount of time proportional to the
length of the array.
Analysis of Linear Search

In the average case, the target value is somewhere in the array.


In fact, since the target value can be anywhere in the array, any
element of the array is equally likely.
So on average, the target value will be in the middle of the array.
So the search takes an amount of time proportional to half the
length of the array
Binary Search
The general term for a smart search through sorted data is a
binary search.
1. The initial search region is the whole array.
2. Look at the data value in the middle of the search region.
3. If you’ve found your target, stop.
4. If your target is less than the middle data value, the new search
region is the lower half of the data.
5. If your target is greater than the middle data value, the new
search region is the higher half of the data.
6. Continue from Step 2.
Binary Search Example

-86 -37 -23 0 5 18 21 64 97

low middle high

Searching for 18.


Binary Search Example

-86 -37 -23 0 5 18 21 64 97

low high
middle
Searching for 18.
Binary Search Example

-86 -37 -23 0 5 18 21 64 97

low high
middle
Searching for 18: found!
Time Complexity of Binary Search
How fast is binary search?
Think about how it operates: after you examine a value, you cut the
search region in half.
So, the first iteration of the loop, your search region is the whole
array.
The second iteration, it’s half the array.
The third iteration, it’s a quarter of the array.
...
The kth iteration, it’s (1/2k-1) of the array.
Hashing
Hash Tables

• We’ll discuss the hash table ADT which supports only a subset of the
operations allowed by binary search trees.
• The implementation of hash tables is called hashing.
• Hashing is a technique used for performing insertions, deletions and finds
in constant average time (i.e. O(1))
• This data structure, however, is not efficient in operations that require any
ordering information among the elements, such as findMin, findMax and
printing the entire table in sorted order.
General Idea


The ideal hash table structure is merely an array of some fixed size, containing the
items.
A stored item needs to have a data member, called key, that will be used in
• computing the index value for the item.
• –Key could be an integer, a string, etc
–e.g. a name or Id that is a part of a large employee structure

• The size of the array is TableSize.


The items that are stored in the hash table are indexed by values from 0 to TableSize
• – 1.
Each key is mapped into some number in the range 0 to
TableSize – 1.
The mapping is called a hash function.
Example Hash
Table
0
1
Items john
2
25000 john 25000
3 john 25000
phil 31250 key Hash 4 philphil 31250
31250
Function
dave 27500 5
6 dave
dave 27500
27500
mary 28200
7 mary
mary 28200
28200
key 8
9
Hash Function
• The hash function:
– must be simple to compute.
– must distribute the keys evenly among the cells.
• If we know which keys will occur in advance we can write
perfect hash functions, but we don’t.
Hash function
Problems:
• Keys may not be numeric.
• Number of possible keys is much larger than the space
available in table.
• Different keys may map into same location
– Hash function is not one-to-one => collision.
– If there are too many collisions, the performance of the hash table
will suffer dramatically.
Hash Functions
• If the input keys are integers then simply
Key mod TableSize is a general strategy.
– Unless key happens to have some undesirable properties. (e.g.
all keys end in 0 and we use mod 10)
• If the keys are strings, hash function needs more care.
– First convert it into a numeric value.
Some methods
• Truncation:
– e.g. 123456789 map to a table of 1000 addresses by picking 3 digits of the
key.
• Folding:
– e.g. 123|456|789: add them and take mod.
• Key mod N:
– N is the size of the table, better if it is prime.
• Squaring:
– Square the key and then truncate
• Radix conversion:
– e.g. 1 2 3 4 treat it to be base 11, truncate if necessary.
Hash Function 1
• Add up the ASCII values of all characters of the key.
int hash(const string &key, int tableSize)
{
int hasVal = 0;

for (int i = 0; i < key.length(); i++) hashVal += key[i];


return hashVal % tableSize;
}

• Simple to implement and fast.


• However, if the table size is large, the function does not distribute the
keys well.
•e.g. Table size =10000, key length <= 8, the hash function can assume values only
between 0 and 1016
Hash Function 2
• Examine only the first 3 characters of the key.
int hash (const string &key, int tableSize)
{
return (key[0]+27 * key[1] + 729*key[2]) % tableSize;

• In theory, 26 * 26 * 26 = 17576 different words can be generated. However,


English is not random, only 2851 different combinations are possible.
Thus, this function although easily computable, is also not appropriate if the
• hash table is reasonably large.
Hash Function 3

KeySize1
hash(key)  Key[KeySize  i 1]37 i
i0

int hash (const string &key, int tableSize)


{
int hashVal = 0;

for (int i = 0; i < key.length(); i++) hashVal = 37 * hashVal +


key[i];

hashVal %=tableSize;
if (hashVal < 0) /* in case overflows occurs */ hashVal +=
tableSize;

return hashVal;
};
Hash function for strings:
98 108 105 key[i]

key a l i
0 1 2 i
KeySize = 3;

hash(“ali”) = (105 * 1 + 108*37 + 98*372) % 10,007 = 8172


0
1
2
“ali” hash function ……
ali 8172

……
10,006 (TableSize)
Collision Resolution
• If, when an element is inserted, it hashes to the same value as an
already inserted element, then we have a collision and need to
resolve it.
• There are several methods for dealing with this:
– Separate chaining
– Open addressing
• Linear Probing
• Quadratic Probing
• Double Hashing
Separate Chaining
• The idea is to keep a list of all elements that hash to the same value.
– The array elements are pointers to the first nodes of the lists.
– A new item is inserted to the front of the list.
• Advantages:
– Better space utilization for large items.
– Simple collision handling: searching linked list.
– Overflow: we can store more items than the hash table size.
– Deletion is quick and easy: deletion from the linked list.
Example
Keys: 0, 1, 4, 9, 16, 25, 36, 49, 64, 81

hash(key) = key % 10.


0 0

1 81 1
2

4 64 4
5 25
6 36 16
7

9 49 9
Operations
• Initialization: all entries are set to NULL
• Find:
– locate the cell using hash function.
– sequential search on the linked list in that cell.
• Insertion:
– Locate the cell using hash function.
– (If the item does not exist) insert it as the first item in the list.
• Deletion:
– Locate the cell using hash function.
– Delete the item from the linked list.
Hash Table Class for separate chaining
template <class HashedObj> class HashTable
{
public:
HashTable(const HashedObj & notFound, int size=101 ); HashTable( const
HashTable & rhs )
:ITEM_NOT_FOUND( rhs.ITEM_NOT_FOUND ),
theLists( rhs.theLists ) { }
const HashedObj & find( const HashedObj & x ) const; void makeEmpty( );
void insert( const HashedObj & x );
void remove( const HashedObj & x );

const HashTable & operator=( const HashTable & rhs ); private:

vector<List<HashedObj> > theLists; // The array of


Lists
const HashedObj ITEM_NOT_FOUND;
};
int hash( const string & key, int tableSize );
int hash( int key, int tableSize);
Insert routine
/**
*Insert item x into the hash table. If the item is
*already present, then do nothing.
*/
template <class HashedObj>
void HashTable<HashedObj>::insert(const HashedObj & x )
{
List<HashedObj> & whichList = theLists[ hash( x,
theLists.size( ) ) ]; ListItr<HashedObj> itr = whichList.find( x );

if( !itr.isValid() )
whichList.insert( x, whichList.zeroth( ) );
}
Remove routine
/**
* Remove item x from the hash table.
*/
template <class HashedObj>
void HashTable<HashedObj>::remove( const HashedObj & x )
{
theLists[hash(x, theLists.size())].remove( x );
}
Find routine
/**
*Find item x in the hash table.
*Return the matching item or ITEM_NOT_FOUND if not found
*/
template <class HashedObj>
const HashedObj & HashTable<HashedObj>::find( const HashedObj & x ) const
{
ListItr<HashedObj> itr;
itr = theLists[ hash( x, theLists.size( ) ) ].find( x ); if(!itr.isValid())
return ITEM_NOT_FOUND; else
return itr.retrieve( );
}
Analysis of Separate Chaining
• Collisions are very likely.
– How likely and what is the average length of lists?
• Load factor  definition:
– Ratio of number of elements (N) in a hash table to the hash
TableSize.
• i.e.  = N/TableSize
– The average length of a list is also 
– For chaining  is not bound by 1; it can be > 1.
Cost of searching
• Cost = Constant time to evaluate the hash function
+ time to traverse the list.
• Unsuccessful search:
– We have to traverse the entire list, so we need to compare  nodes on the average.
• Successful search:
– List contains the one node that stores the searched item + 0 or more other nodes.
– Expected # of other nodes = x = (N-1)/M which is essentially
 since M is presumed large.
– On the average, we need to check half of the other nodes while searching for a certain element
– Thus average search cost = 1 + /2
Summary
• The analysis shows us that the table size is not really
important, but the load factor is.
• TableSize should be as large as the number of expected
elements in the hash table.
– To keep load factor around 1.
• TableSize should be prime for even distribution of keys to
hash table cells.
Hashing: Open Addressing

41
CENG 213 Data Structures
Collision Resolution with Open
Addressing
• Separate chaining has the disadvantage of using linked
lists.
– Requires the implementation of a second data structure.
• In an open addressing hashing system, all the data go
inside the table.
– Thus, a bigger table is needed.
• Generally the load factor should be below 0.5.
– If a collision occurs, alternative cells are tried until an empty
cell is found.
Open Addressing
• More formally:
– Cells h0(x), h1(x), h2(x), …are tried in succession where
hi(x) = (hash(x) + f(i)) mod TableSize, with f(0) = 0.
– The function f is the collision resolution strategy.
• There are three common collision resolution strategies:
– Linear Probing
– Quadratic probing
– Double hashing
Linear Probing
• In linear probing, collisions are resolved by sequentially
scanning an array (with wraparound) until an empty cell
is found.
– i.e. f is a linear function of i, typically f(i)= i.
• Example:
– Insert items with keys: 89, 18, 49, 58, 9 into an empty hash
table.
– Table size is 10.
– Hash function is hash(x) = x mod 10.
• f(i) = i;
Figure 20.4
Linear probing hash
table after each
insertion
Find and Delete
• The find algorithm follows the same probe sequence as
the insert algorithm.
– A find for 58 would involve 4 probes.
– A find for 19 would involve 5 probes.
• We must use lazy deletion (i.e. marking items as deleted)
– Standard deletion (i.e. physically removing the item) cannot be
performed.
– e.g. remove 89 from hash table.
Clustering Problem
• As long as table is big enough, a free cell can always be
found, but the time to do so can get quite large.
• Worse, even if the table is relatively empty, blocks of
occupied cells start forming.
• This effect is known as primary clustering.
• Any key that hashes into the cluster will require several
attempts to resolve the collision, and then it will add to
the cluster.
Analysis of insertion
• The average number of cells that are examined in an insertion
using linear probing is roughly
(1 + 1/(1 – λ)2) / 2
• Proof is beyond the scope of text book.
• For a half full table we obtain 2.5 as the average number of cells
examined during an insertion.
• Primary clustering is a problem at high load factors. For half
empty tables the effect is not disastrous.
Analysis of Find
• An unsuccessful search costs the same as insertion.
• The cost of a successful search of X is equal to the cost of inserting
X at the time X was inserted.
• For λ = 0.5 the average cost of insertion is 2.5. The average cost of
finding the newly inserted item will be 2.5 no matter how many
insertions follow.
• Thus the average cost of a successful search is an average of the
insertion costs over all smaller load factors.
Average cost of find
• The average number of cells that are examined in an unsuccessful
search using linear probing is roughly (1 + 1/(1 – λ)2) / 2.
• The average number of cells that are examined in a successful
search is approximately
(1 + 1/(1 – λ)) / 2.
– Derived from:

1 1  1 1  dx

 2  (1 x) 2 
x 0
Linear Probing – Analysis -- Example
• What is the average number of probes for a successful search and an
unsuccessful search for this hash table?
0 9
1
-- 25: 3,4

2 2
– Hash Function: h(x) = x mod 11
3 13
Avg. •Probe
Successful Search:
for SS = (1+1+1+2+2+4+1+3)/8=15/8
• – 20: 9Search:
Unsuccessful -- 30: 8 -- 2 : 2 -- 13: 2, 3
4 25

– • assume
We – 24: 2,3,4,5
that the--hash
10: function
10 -- 9:uniformly
9,10, 0 distributes the keys. 5 24
– 0: 0,1 -- 1: 1 -- 2: 2,3,4,5,6 -- 3: 6
3,4,5,6 7
– 4: 4,5,6 -- 5: 5,6 -- 6: 6 -- 7: 7 -- 8 30
8: 8,9,10,0,1
9 20
– 9: 9,10,0,1 -- 10: 10,0,1
Avg. Probe for US = (2+1+5+4+3+2+1+1+5+4+3)/11=31/11 10 10
Quadratic Probing
• Quadratic Probing eliminates primary clustering problem of linear
probing.
• Collision function is quadratic.
– The popular choice is f(i) = i2.
• If the hash function evaluates to h and a search in cell h is
inconclusive, we try cells h + 12, h+22, … h + i2.
– i.e. It examines cells 1,4,9 and so on away from the original probe.
• Remember that subsequent probe points are a quadratic
number of positions from the original probe point.
Figure 20.6
A quadratic probing hash
table after each insertion
(note that the table size
was poorly chosen
because it is not a prime
number).
Quadratic Probing
• Problem:
– We may not be sure that we will probe all locations in the table (i.e. there is
no guarantee to find an empty cell if table is more than half full.)
– If the hash table size is not prime this problem will be much severe.
• However, there is a theorem stating that:
– If the table size is prime and load factor is not larger than 0.5, all probes
will be to different locations and an item can always be inserted.
Theorem
• If quadratic probing is used, and the table size is prime,
then a new element can always be inserted if the table is at
least half empty.
Proof
• Let M be the size of the table and it is prime. We show that the first
M/2 alternative locations are distinct.
• two of these locations are h + i2 and h + j2, where i, j are two probes
Let
s.t. 0 i,j  M/2. Suppose for the sake of contradiction, that these two locations are the same but
i  j. Then
h + i2 = h + j2 (mod M) i2 = j2 (mod M)
i2 - j2 = 0 (mod M)
(i-j)(i+j) = 0 (mod M)
Because M is prime, either (i-j) or (i+j) is divisible by M. Neither of these possibilities can occur.
Thus we obtain a contradiction.
• follows that the first M/2 alternative are all distinct and since there are at most M/2 items in
It
the hash table it is guaranteed that an insertion must succeed if the table is at least half full.

Some considerations
• How efficient is calculating the quadratic probes?
– Linear probing is easily implemented.
Quadratic probing appears to require * and % operations.
– However by the use of the following trick, this is overcome:
•H =H
i i-1 +2i – 1 (mod M)
Some Considerations
• What happens if load factor gets too high?
– Dynamically expand the table as soon as the load factor
reaches 0.5, which is called rehashing.
– Always double to a prime number.
– When expanding the hash table, reinsert the new table by
using the new hash function.
Analysis of Quadratic Probing
• Quadratic probing has not yet been mathematically analyzed.
• Although quadratic probing eliminates primary clustering,
elements that hash to the same location will probe the same
alternative cells. This is know as secondary clustering.
• Techniques that eliminate secondary clustering are available.
– the most popular is double hashing.
Double Hashing
• A second hash function is used to drive the collision resolution.
– f(i) = i * hash2(x)
• We apply a second hash function to x and probe at a distance
hash2(x), 2*hash2(x), … and so on.
• The function hash2(x) must never evaluate to zero.
– e.g. Let hash (x) = x mod 9 and try to insert 99 in the
2 previous example.
• A function such as hash2(x) = R – ( x mod R) with R a prime
smaller than TableSize will work well.
– e.g. try R = 7 for the previous example.(7 - x mode 7)
The relative efficiency of four collision-
resolution methods
Hashing Applications
• Compilers use hash tables to implement the symbol table
(a data structure to keep track of declared variables).
• Game programs use hash tables to keep track of positions
it has encountered (transposition table)
• Online spelling checkers.
Summary
• Hash tables can be used to implement the insert and find operations
in constant average time.
– it depends on the load factor not on the number of items in the table.
• It is important to have a prime TableSize and a correct choice of
load factor and hash function.
• For separate chaining the load factor should be close to 1.
• For open addressing load factor should not exceed
0.5 unless this is completely unavoidable.
– Rehashing can be implemented to grow (or shrink) the table.

You might also like