Escolar Documentos
Profissional Documentos
Cultura Documentos
Time Complexity
✓ It is the time taken by a computer to completely execute a program.
✓ Program may be in any language. (C, C++, Java etc…)
Asymptotic Notations
✓ It is the meaningful way of representation of time complexity.
They are
i) Big Oh [ O (g(n)) ] { Upper bound }
ii) Big Omega [ Ω (g(n)) ] { Lower bound }
iii) Big Theta [ Ɵ (g(n)) ] { Average }
A loop or recursion that runs a constant number of times is also considered as O(1). For
example the following loop is O(1).
// Here c is a constant
for (int i = 1; i <= c; i++) {
// some O(1) expressions
}
2) O(n): Time Complexity of a loop is considered as O(n) if the loop variables is incremented
/ decremented by a constant amount. For example following functions have O(n) time
complexity.
// Here c is a positive integer constant
for (int i = 1; i <= n; i += c) {
// some O(1) expressions
}
for (int i = n; i > 0; i -= c) {
// some O(1) expressions
}
3) O(nc): Time complexity of nested loops is equal to the number of times the innermost
statement is executed. For example the following sample loops have O(n2) time complexity
for (int i = 1; i <=n; i += c) {
for (int j = 1; j <=n; j += c) {
// some O(1) expressions
}
}
for (int i = n; i > 0; i -= c) {
for (int j = i+1; j <=n; j += c) {
// some O(1) expressions
}
For example Selection sort and Insertion Sort have O(n2) time complexity.
4) O(Logn) Time Complexity of a loop is considered as O(Logn) if the loop variables is
divided / multiplied by a constant amount.
for (int i = 1; i <=n; i *= c) {
// some O(1) expressions
}
for (int i = n; i > 0; i /= c) {
// some O(1) expressions
}
For example Binary Search(refer iterative implementation) has O(Logn) time complexity. Let
us see mathematically how it is O(Log n). The series that we get in first loop is 1, c, c2, c3, …
ck. If we put k equals to Logcn, we get cLogcn which is n.
How to calculate time complexity when there are many if, else statements inside loops?
As discussed here, worst case time complexity is the most useful among best, average and
worst. Therefore we need to consider worst case. We evaluate the situation when values in if-
else conditions cause maximum number of statements to be executed.
k=2
Sum = 12 + 22 + 32 + ... n12.
= n(n+1)(2n+1)/6
= n3/3 + n2/2 + n/6
k=3
Sum = 13 + 23 + 33 + ... n13.
= n2(n+1)2/4
= n4/4 + n3/2 + n2/4
Note that, in asymptotic notations like Θ we can always ignore lower order terms. So the time
complexity is Θ(nk+1 / (k+1))
Simple Statement
This statement takes O(1) time.
int y= n + 25;
If Statement
The worst case O(n) if the if statement is in a loop that runs n times, best case O(1)
if( n> 100)
{
…
}else{
..
..
}
For / While Loops
The for loop takes n time to complete and and so it is O(n).
for(int i=0;i<n;i++)
{
..
..
}
If the for loop takes n time and i increases or decreases by a constant, the cost is O(n)
for(int i = 0; i < n; i+=5)
sum++;
Nested loops
If the nested loops contain sizes n and m, the cost is O(nm)
for(int i=0;i<n;i++)
{
for(int i=0;i<m;i++){
..
..
}
}
If the first loop runs n2 times and the inner loop runs n times or (vice versa), the cost is O(n3)
for(int j=0;j<n*n;j++)
{
for(int i=0;i<n;i++){
..
..
}
}
If the first loop runs n times and the inner second loop runs n2 times and the third loop runs
n2, then O(n5)
for(int i = 0; i < n; i++)
for( int j = 0; j < n * n; j++)
for(int k = 0; k < j; k++)
sum++;
If the for loop takes n time and i increases or decreases by a multiple, the cost is O(log(n))
for(int i = 1; i < =n; i*=2)
sum++;
If the first loop runs N times and the inner loop runs log(n) times or (vice versa), the cost is
O(n*log(n))
for(int i=0;i<n;i++)
{
for(int j=1;i<=n;j*=4){
..
..
}
}
////////////////////////////////////////////////INSERTION SORT////////////////O(N2)/////////////////////
Insertion sort resembles the arrangement of cards in card-player game. When a new card is
cards as each successive card is dealt you might have observed the following sequence of events: the
first card is dealt and, of course, since it is the only card, it is already in your hand in the correct
place. The second card is dealt and then you decide to place it either before or after the first card.
With the third the decision that must is made is whether to insert it before the first or second cards
or after the third card. This basic behavior goes on as each new card is received.
Consider an array A with N elements. The insertion sort algorithm scans the elements from
left to right i.e., from a[I] to a[n], inserting each element a[k] into its proper position in the
previously sorted sub array i.e., a[1] to a[k-1].
Algorithm:
Step 1: A[1] by itself is sorted.
Step 2: A[2] is inserted before or after a[1], so that a[1] and a[2] are sorted.
Step 3: A[3] is inserted into its proper place with respect to the elements a[1] and a[2]
Step 4: This process is continued till all the elements are sorted.
To make the process of moving easier we introduce element a[0]=least value (according to
the data type) Whose key value is smaller than any other keys among a[1],a[2],……….,a[n]
Examples: Input = 5, 2, 4, 6, 1, 2
Output: 1, 2, 3, 4, 5, 6
Program:
#include <stdio.h>
void insertionsort( int a[], int n)
{
int i,j,t;
for (i = 1 ; i < n ; i++)
{
j = i;
while((j > 0) && (a[j] < a[j-1]))
{
t = a[j];
a[j] = a[j-1];
a[j-1] = t;
j--;
}
}
}
void main()
{
int n, a[100], i;
insertionsort(a,n);
Time Complexity:
The time for sorting is measured in terms of the number of comparisons. In the worst case if
the array is in the reverse order then the inner while loop takes j-1 comparisons. That means
according insertion sort in pass-1 it takes one comparison. In pass-II, 2 comparisons and etc.
Hence the time complexity function f(n) can be calculated as follows :
In the average case there will be approximately (j-1)/2 comparisons for the inner while loop.
The time complexity f(n) in the average case may be calculated as follows:
It is introduced by D.L Shell which uses insertion sort on periodic sub-sequence of the input
to produce a faster sorting algorithm. It is also known as diminishing increment sort. It is the
1st sorting algorithm to break 'n2' barrier. It is fast, easy to understand, easy to implement.
However, its complexity analysis is little more sophisticated. It begins by comparison of an
element i.e at a distance or gap 'd' which is initially half of the number of elements in the
array. Further in each part, the value of 'd' is reduced to half.
The Shell sort is significantly slower than merge, heap, quick sort but it is relatively simple
algorithm. It is also known as excellent choice for repetitive sorting of smaller list.
Algorithm:
Step 1 − Initialize the value of ‘gap = N/2’
Step 2 − Divide the list into smaller sub-list of equal interval ‘gap’
Step 3 − Sort these sub-lists using insertion sort
Step 3 − Repeat until complete list is sorted
Use Shell sort for the following array : 18, 32, 12, 5, 38, 30, 16, 2
Compare the elements at a gap of 4. i.e 18 with 38 and so on and swap if first number is
greater than second.
Compare the elements at a gap of 2 i.e 18 with 12 and so on.
Now the gap is 1. So now use insertion sort to sort this array.
Program:
#include <stdio.h>
void shellsort( int a[ ], int n)
{
int gap,i,j,t;
gap=n/2;
while(gap>0)
{
for( i=gap ;i<n;i++)
{
for(j=i-gap ; j>=0; j=j-gap)
{
if (a[j] >a[j+gap])
{
t=a[j];
a[j]=a[j+m];
a[j+m]=t;
}
}
}
gap=gap/2;
}
void main()
{
int n, a[10], i, j, t,m;
printf("Enter number of elements\n");
scanf("%d", &n);
printf("Enter %d integers\n", n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
shellsort(a,n);
Time Complexity:
Since in this algorithm insertion sort is applied in the large interval of elements and then
interval reduces in a sequence, therefore the running time of Shell sort is heavily dependent
on the gap sequence it uses .
Heap sort is a comparison based sorting technique based on Binary Heap data structure. It is
similar to selection sort where we first find the maximum element and place the maximum
element at the end. We repeat the same process for remaining element.
A Binary Heap is a Complete Binary Tree where items are stored in a special order such that
value in a parent node is greater(or smaller) than the values in its two children nodes. The
former is called as max heap and the latter is called min heap. The heap can be represented by
binary tree or array. Since a Binary Heap is a Complete Binary Tree, it can be easily
represented as array and array based representation is space efficient. If the parent node is
stored at index I, the left child can be calculated by 2 * I + 1 and right child by 2 * I + 2
(assuming the indexing starts at 0). Whereas I starts from n/2 – 1. ‘n' is total number of
elements in the array.
Algorithm:
Step 1. Build a max heap from the input data.
Step 2. At this point, the largest item is stored at the root of the heap. Replace it with the last
item of the heap followed by reducing the size of heap by 1. Finally, heapify the root of tree.
Step 3. Repeat above steps while size of heap is greater than 1.
Example:
10 5 3 4 1
Swap : a[0] and a[n-1]
1 5 3 4 10
Heapify the remaining elements of 1,5, 3, 4 except 10.
Repeat until to the array elements having single element.
Program:
#include<stdio.h>
void heapify(int a[], int n, int i)
{
int root,l,r,t;
root = i;
l = 2*i + 1;
r = 2*i + 2;
if (l<n && a[l] > a[root])
root = l;
if (r<n && a[r] > a[root])
root = r;
if (root != i)
{
t=a[i];
a[i] = a[root];
a[root] = t;
heapify(a, n, root);
}
}
void heapsort(int a[], int n)
{
int i,t;
for(i=n/2-1; i>=0; i--)
heapify(a, n, i);
for(i=n-1; i>=0; i--)
{
t= a[0];
a[0]= a[i];
a[i] = t;
heapify(a, i, 0);
}
}
int main()
{
int a[50],i,n;
printf("Enter total number of elements:");
scanf("%d", &n);
printf("Enter the elements:\n");
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
heapsort(a,n);
printf("\n\nAfter Heap sort:\n");
for(i = 0;i < n; i++)
printf("%d\t", a[i]);
return 0;
}
Merge sort is a sorting technique based on divide and conquer technique. With worst-case
time complexity being Ο(n log n), it is one of the most respected algorithms. Merge sort first
divides the array into equal halves and then combines them in a sorted manner.
Algorithm:
Merge-Sort (a, first, last)
Step 1: if (first < last)
{
Step 2: mid = (first+last)/2 ;
Step 3: Merge-Sort(a, first, mid) ;
Step 4: Merge-Sort(a, mid+1, last) ;
Step 5: Merge(a, first, mid, last) ;
}
The above procedure sorts the elements in the sub array a[first...last].
To sort given any a[n] invoke the algorithm with parameters (a, 0, n -1).
We can assume each element in a given array as a sorted sub array. Take adjacent arrays and
merge to obtain a sorted array of two elements. Next step, take adjacent sorted arrays, of size
two, in pair and merge them to get a sorted array of four elements. Repeat the step until
whole array is sorted.
if(low<high)
{
mid=(low+high)/2;
mergesort(a,low,mid);
mergesort (a,mid+1,high);
msort(a,low,mid,high);
}
}
void main()
{
int a[50] , i, n;
printf("Enter total number of elements:");
scanf("%d", &n);
printf("Enter the elements:\n");
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
Quick sort is a highly efficient sorting algorithm and is based on partitioning of array of data
into smaller arrays. A large array is partitioned into two arrays one of which holds values
smaller than the specified value, say pivot, based on which the partition is made and another
array holds values greater than the pivot value.
Quick sort partitions an array and then calls itself recursively twice to sort the two resulting
subarrays. This algorithm is quite efficient for large-sized data sets as its average and worst
case complexity are of Ο(n log n), where n is the number of items.
Algorithm:
Step 1: Input all the ‘n’ elements in the array as a[n].
Step 2: Choose the pivot element as first element, also assign ‘i’ as first element and
‘j’ as last element.
Step 3. Move the position ‘i’ from left to right ( i++) if a[i] < = a[pivot]
Step 4. Move the position ‘j’ from right to left ( j++) if a[j] > a[pivot]
Step 5. If ‘i’ and ‘j’ stops moving (i<j) then swap a[i] and a[j].
Step 6. If ‘i’ crossed ‘j’ (j>i) then swap a[ j ] & a[pivot] and fix pivot at the position of ‘j’.
Step 7. Now this pivot element partitioned the array elements in to two sub arrays.
Step 8. Apply recursion operation from steps 2 to step 7 for left side sub array from first
element to j-1 position. Then apply for right side sub array from j+1 position to last.
Step 9. Finally this recursion sort the element in sorted order.
Example:
Proceed the above operation recursively until the elements are sorted.
Program:
#include<stdio.h>
void quicksort(int a[ ],int first,int last)
{
int pivot,j,t,i;
if(first<last)
{
pivot=first;
i=first;
j=last;
while(i<j)
{
while(a[i] <= a[pivot])
i++;
while(a[j]>a[pivot])
j--;
if(i<j)
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
t=a[pivot];
a[pivot]=a[j];
a[j]=t;
quicksort(a,first,j-1);
quicksort(a,j+1,last);
}
}
int main()
{
int a[100],n,i;
printf("Enter size of the array: ");
scanf("%d",&n);
printf("Enter %d elements: ",n);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
quicksort(a,0,n-1);
Bucket sort is a comparison sort algorithm that operates on elements by dividing them into
different buckets and then sorting these buckets individually. Each bucket is sorted
individually using a separate sorting algorithm or by applying the bucket sort algorithm
recursively. Bucket sort is mainly useful when the input is uniformly distributed over a range.
One has been given a large array of floating point integers lying uniformly between the lower
and upper bound. This array now needs to be sorted. A simple way to solve this problem
would be to use another sorting algorithm such as Merge sort, Heap Sort or Quick Sort.
However, these algorithms guarantee a best case time complexity of O(N log N).. However,
using bucket sort, the above task can be completed in O(N) time. Let's have a closer look at
it.
Algorithm:
bucketSort(arr[], n)
1) Create n empty buckets (Or lists).
2) Do following for every array element arr[i].
-> Insert arr[i] into bucket[n*array[i]] (Apply hashing principle)
3) Sort individual buckets using insertion sort.
4) Concatenate all sorted buckets.
///////////////////////////////////////External sorting//////////////////////////////////////////////////
External sorting is a technique in which the data is stored on the secondary memory, in which
part by part data is loaded into the main memory and then sorting can be done over there.
Then this sorted data will be stored in the intermediate files. Finally, these files will be
merged to get a sorted data. Thus by using the external sorting technique, a huge amount of
data can be sorted easily. In case of external sorting, all the data cannot be accommodated on
the single memory, in this case, some amount of memory needs to be kept on a memory such
as hard disk, compact disk and so on.
The requirement of external sorting is there, where the data we have to store in the main
memory does not fit into it. Basically, it consists of two phases that are:
The external merge sort is a technique in which the data is stored in intermediate files and
then each intermediate files are sorted independently and then combined or merged to get a
sorted data.
For example: Let us consider there are 10,000 records which have to be sorted. For this, we
need to apply the external merge sort method. Suppose the main memory has a capacity to
store 500 records in a block, with having each block size of 100 records.
In this example, we can see 5 blocks will be sorted in intermediate files. This process will be
repeated 20 times to get all the records. Then by this, we start merging a pair of intermediate
files in the main memory to get a sorted output.
Two-way merge sort is a technique which works in two stages which are as follows here:
Stage 1: Firstly break the records into the blocks and then sort the individual record with the
help of two input tapes.
Stage 2: In this merge the sorted blocks and then create a single sorted file with the help of
two output tapes.
By this, it can be said that two-way merge sort uses the two input tapes and two output tapes
for sorting the data.
Step 1) Divide the elements into the blocks of size M. Sort each block and then write on disk.
Step 3) Repeat the step 2 and get longer and longer runs on alternates tapes. Finally, at last,
we will get a single sorted list.
Analysis
This algorithm requires log(N/M) passes with initial run pass. Therefore, at each pass the N
records are processed and at last we will get a time complexity as O(N log(N/M).