DSA Unit 4
DSA Unit 4
UNIT-4
Sorting Algorithms
Sorting is the process of arranging the elements of an array so that they can be placed either in
ascending or descending order. For example, consider an array A = {A1, A2, A3, A4, ?? An },
the array is called to be in ascending order if element of A are arranged like A1 > A2 > A3 > A4
> A5 > ? > An .
Consider an array;
int A[10] = { 5, 4, 10, 2, 30, 45, 34, 14, 18, 9 )
The Array sorted in ascending order will be given as;
A[] = { 2, 4, 5, 9, 10, 14, 18, 30, 34, 45 }
There are many techniques by which sorting can be performed. In this section of the tutorial, we
will discuss each method in detail.
SN Sorting Description
Algorithms
1 Bubble Sort It is the simplest sort method which performs sorting by repeatedly
moving the largest element to the highest index of the array. It
comprises of comparing each element to its adjacent element and
replace them accordingly.
2 Insertion Sort As the name suggests, insertion sort inserts each element of the array
to its proper place. It is a very simple sort method which is used to
arrange the deck of cards while playing bridge.
3 Merge Sort Merge sort follows the divide and conquer approach in which the list
is first divided into the sets of equal elements and then each half of
the list is sorted by using merge sort. The sorted list is combined
again to form an elementary sorted array.
4 Quick Sort Quick sort is the most optimized sort algorithm which performs
sorting in O(n log n) comparisons. Like Merge sort, quick sort also
works by using divide and conquer approach.
5 Heap In the heap sort, Min heap or max heap is maintained from the
Sort array elements depending upon the choice and the elements are
sorted by deleting the root element of the heap.
7 Selectio Selection sort finds the smallest element in the array and places it
n Sort on the first place on the list, then it finds the second smallest
element in the array and places it on the second place. This
process continues until all the elements are moved to their correct
ordering. It carries running time O(n2) which is worse than
insertion sort.
Bubble sort
Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent
elements if they are in the wrong order. This algorithm is not suitable for large data sets as its
average and worst-case time complexity is quite high.3
1. begin BubbleSort(arr)
2. for all array elements
3. if arr[i] > arr[i+1]
4. swap(arr[i], arr[i+1])
5. end if
6. end for
7. return arr
8. end BubbleSort
Implementation:
// C++ program for implementation
// of Bubble sort
#include <bits/stdc++.h>
using namespace std;
// Driver code
int main()
{
int arr[] = { 5, 1, 4, 2, 8};
int N = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, N);
cout << "Sorted array: \n";
printArray(arr, N);
return 0;
}
First Pass:
Bubble sort starts with the very first two elements, comparing them to check which one is
greater.
( 5 1 4 2 8 ) –> ( 1 5 4 2 8 ), Here, algorithm compares the first two elements, and swaps since 5
> 1.
( 1 5 4 2 8 ) –> ( 1 4 5 2 8 ), Swap since 5 > 4
( 1 4 5 2 8 ) –> ( 1 4 2 5 8 ), Swap since 5 > 2
( 1 4 2 5 8 ) –> ( 1 4 2 5 8 ), Now, since these elements are already in order (8 > 5), the algorithm
does not swap them.
Second Pass:
Now, during second iteration it should look like this:
( 1 4 2 5 8 ) –> ( 1 4 2 5 8 )
( 1 4 2 5 8 ) –> ( 1 2 4 5 8 ), Swap since 4 > 2
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
Third Pass:
Now, the array is already sorted, but our algorithm does not know if it is completed.
The algorithm needs one whole pass without any swap to know it is sorted.
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
Illustration:
Illustration of Bubble Sort
Insertion Sort
Insertion sort is a simple sorting algorithm that works similarly to the way you sort playing cards
in your hands. The array is virtually split into a sorted and an unsorted part. Values from the
unsorted part are picked and placed at the correct position in the sorted part.
12 11 13 5 6
First Pass:
● Initially, the first two elements of the array are compared in insertion sort.
12 11 13 5 6
● Here, 12 is greater than 11 hence they are not in the ascending order and 12 is not at its
correct position. Thus, swap 11 and 12.
● So, for now 11 is stored in a sorted sub-array.
11 12 13 5 6
Second Pass:
● Now, move to the next two elements and compare them
11 12 13 5 6
● Here, 13 is greater than 12, thus both elements seems to be in ascending order, hence, no
swapping will occur. 12 also stored in a sorted sub-array along with 11
Third Pass:
● Now, two elements are present in the sorted sub-array which are 11 and 12
● Moving forward to the next two elements which are 13 and 5
11 12 13 5 6
● Both 5 and 13 are not present at their correct place so swap them
11 12 5 13 6
● After swapping, elements 12 and 5 are not sorted, thus swap again
11 5 12 13 6
5 11 12 13 6
Fourth Pass:
● Now, the elements which are present in the sorted sub-array are 5, 11 and 12
● Moving to the next two elements 13 and 6
5 11 12 13 6
● Clearly, they are not sorted, thus perform swap between both
5 11 12 6 13
● Now, 6 is smaller than 12, hence, swap again
5 11 6 12 13
5 6 11 12 13
Implementation
#include <bits/stdc++.h>
using namespace std;
// Driver code
int main()
{
int arr[] = { 12, 11, 13, 5, 6 };
int N = sizeof(arr) / sizeof(arr[0]);
insertionSort(arr, N);
printArray(arr, N);
return 0;
}
Illustrations:
Quick sort
Quicksort is a sorting algorithm based on the divide and conquer approach where an array is
divided into subarrays by selecting a pivot element (element selected from the array).
1. While dividing the array, the pivot element should be positioned in such a way that
elements less than the pivot are kept on the left side, and elements greater than the pivot
is on the right side of the pivot.
2. The left and right subarrays are also divided using the same approach. This process
continues until each subarray contains a single element.
3. At this point, elements are already sorted. Finally, elements are combined to form a
sorted array.
Step1
● Traverse elements from j = low to high-1
● j = 0: Since arr[j] <= pivot, do i++ and swap(arr[i], arr[j])
● i=0
● arr[] = {10, 80, 30, 90, 40, 50, 70} // No change as i and j are same
● j = 1: Since arr[j] > pivot, do nothing
Step2
● j = 2 : Since arr[j] <= pivot, do i++ and swap(arr[i], arr[j])
● i=1
● arr[] = {10, 30, 80, 90, 40, 50, 70} // We swap 80 and 30
Step3
● j = 3 : Since arr[j] > pivot, do nothing // No change in i and arr[]
● j = 4 : Since arr[j] <= pivot, do i++ and swap(arr[i], arr[j])
● i=2
● arr[] = {10, 30, 40, 90, 80, 50, 70} // 80 and 40 Swapped
Step 4
● j = 5 : Since arr[j] <= pivot, do i++ and swap arr[i] with arr[j]
● i=3
● arr[] = {10, 30, 40, 50, 80, 90, 70} // 90 and 50 Swapped
Step 5
● We come out of loop because j is now equal to high-1.
● Finally we place pivot at correct position by swapping arr[i+1] and arr[high] (or pivot)
● arr[] = {10, 30, 40, 50, 70, 90, 80} // 80 and 70 Swapped
Step 6
● Now 70 is at its correct place. All elements smaller than 70 are before it and all elements
greater than 70 are after it.
● Since quick sort is a recursive function, we call the partition function again at left and
right partitions
Step 7
● Again call function at right part and swap 80 and 90
#include <bits/stdc++.h>
using namespace std;
int partition(int arr[],int low,int high){
//choose the pivot
int pivot=arr[high];
//Index of smaller element and Indicate
//the right position of pivot found so far
int i=(low-1);
for(int j=low;j<=high;j++){
//If current element is smaller than the pivot
if(arr[j]<pivot){
//Increment index of smaller element
i++;
swap(arr[i],arr[j]); }}
swap(arr[i+1],arr[high]);
return (i+1);
}
// The Quicksort function Implement
void quickSort(int arr[],int low,int high){
// when low is less than high
if(low<high){
// pi is the partition return index of pivot
int pi=partition(arr,low,high);
//Recursion Call
//smaller element than pivot goes left and
//higher element goes right
quickSort(arr,low,pi-1);
quickSort(arr,pi+1,high);}}
int main() {
int arr[]={10,7,8,9,1,5};
int n=sizeof(arr)/sizeof(arr[0]);
// Function call
quickSort(arr,0,n-1);
//Print the sorted array
cout<<"Sorted Array\n";
for(int i=0;i<n;i++)
{
cout<<arr[i]<<" ";
}
return 0;
}
// This Code is Contributed By Diwakar Jha
Selection sort
Selection sort is another sorting technique in which we find the minimum element in every
iteration and place it in the array beginning from the first index. Thus, a selection sort also gets
divided into a sorted and unsorted subarray.
● Begin with the smallest (or largest, depending on the sorting order) element.
● In the unsorted segment, compare the minimum element to the following
element.
● If a smaller (or larger) element is discovered, the index of the minimum (or
maximum) element is updated.
● Continue comparing and updating the minimum (or maximum) element until the
unsorted segment is finished.
● Swap the identified minimum (or maximum) element with the unsorted part’s
initial element.
● Move the sorted part’s boundary one element to the right to expand it.
● Steps 2-6 should be repeated until the full list is sorted.
First pass:
For the first position in the sorted array, the whole array is traversed from index 0 to 4
sequentially. The first position where 64 is stored presently, after traversing the whole array it is
clear that 11 is the lowest value.
64 25 12 22 11
Thus, replace 64 with 11. After one iteration 11, which happens to be the least value in the array,
tends to appear in the first position of the sorted list.
11 25 12 22 64
Second Pass:
For the second position, where 25 is present, again traverse the rest of the array in a sequential
manner.
11 25 12 22 64
After traversing, we found that 12 is the second lowest value in the array and it should appear at
the second place in the array, thus swap these values.
11 12 25 22 64
Third Pass:
Now, for third place, where 25 is present again, traverse the rest of the array and find the third
least value present in the array.
11 12 25 22 64
While traversing, 22 came out to be the third least value and it should appear at the third place in
the array, thus swap 22 with element present at third position.
11 12 22 25 64
Fourth pass:
Similarly, for fourth position traverse the rest of the array and find the fourth least element in the
array
As 25 is the 4th lowest value hence, it will place at the fourth position.
11 12 22 25 64
Fifth Pass:
At last the largest value present in the array automatically gets placed at the last position in the
array The resulting array is the sorted array.
11 12 22 25 64
// C++ program for implementation of
// selection sort
#include <bits/stdc++.h>
using namespace std;
// Driver program
int main()
{
int arr[] = { 64, 25, 12, 22, 11 };
int n = sizeof(arr) / sizeof(arr[0]);
// Function Call
selectionSort(arr, n);
cout << "Sorted array: \n";
printArray(arr, n);
return 0;
}
Merge Sort
The Merge Sort algorithm is a sorting algorithm that is based on the Divide and Conquers
paradigm. In this algorithm, the array is initially divided into two equal halves and then they are
combined in a sorted manner.
Now, again find that is left index is less than the right index for both arrays, if found yes, then
again calculate mid points for both the arrays.
Now, further divide these two arrays into further halves, until the atomic units of the array is
reached and further division is not possible.
After dividing the array into smallest units, start merging the elements again based on
comparison of size of elements
Firstly, compare the element for each list and then combine them into another list in a sorted
manner.
After the final merging, the list looks like this:
Step 1: Find the largest element in the array, which is 802. It has three digits, so we will iterate
three times, once for each significant place.
Step 2: Sort the elements based on the unit place digits (X=0). We use a stable sorting technique,
such as counting sort, to sort the digits at each significant place.
● Perform counting sort on the array based on the unit place digits.
● The sorted array based on the unit place is [170, 90, 802, 2, 24, 45, 75, 66].
Step 3: Sort the elements based on the tens place digits.
● Perform counting sort on the array based on the tens place digits.
● The sorted array based on the tens place is [802, 2, 24, 45, 66, 170, 75, 90].
● Perform counting sort on the array based on the hundreds place digits.
● The sorted array based on the hundreds place is [2, 24, 45, 66, 75, 90, 170, 802].
Step 5: The array is now sorted in ascending order.
The final sorted array using radix sort is [2, 24, 45, 66, 75, 90, 170, 802].
Heap sort
Heap sort is a comparison-based sorting technique based on Binary Heap data structure. It is
similar to the selection sort where we first find the minimum element and place the minimum
element at the beginning. Repeat the same process for the remaining elements.
Working of Heap Sort algorithm:
To understand heap sort more clearly, let’s take an unsorted array and try to sort it using heap
sort. Consider the array: arr[] = {4, 10, 3, 5, 1}.
Build Complete Binary Tree: Build a complete binary tree from the array.
● Now, as seen, 4 as a parent is smaller than the child 5, thus swap both of these again and
the resulted heap and array should be like this:
Make the tree a max heap
Perform heap sort: Remove the maximum element in each step (i.e., move it to the end position
and remove that) and then consider the remaining elements and transform it into a max heap.
● Delete the root element (10) from the max heap. In order to delete this node, try to swap
it with the last node, i.e. (1). After removing the root element, again heapify it to convert
it into max heap.
● Resulted heap and array should look like this:
Remove 10 and perform heapify
● Repeat the above steps and it will look like the following:
Remove 5 and perform heapify
● Now remove the root (i.e. 3) again and perform heapify.
Searching
Searching Algorithms are designed to check for an element or retrieve an element from any data
structure where it is stored.
Linear Search is defined as a sequential search algorithm that starts at one end and goes through
each element of a list until the desired element is found, otherwise the search continues till the
end of the data set.
For example: Consider the array arr[] = {10, 50, 30, 70, 80, 20, 90, 40} and key = 30
Step 1: Start from the first element (index 0) and compare key with each element (arr[i]).
● Comparing key with first element arr[0]. SInce not equal, the iterator moves to the next
element as a potential match.
#include <bits/stdc++.h>
using namespace std;
// Driver code
int main(void)
{
int arr[] = { 2, 3, 4, 10, 40 };
int x = 10;
int N = sizeof(arr) / sizeof(arr[0]);
// Function call
int result = search(arr, N, x);
(result == -1)
? cout << "Element is not present in array"
: cout << "Element is present at index " << result;
return 0;
}
Output
Element is present at index 3
Binary Search
Binary Search is defined as a searching algorithm used in a sorted array by repeatedly dividing the search
interval in half. The idea of binary search is to use the information that the array is sorted and reduce the
time complexity to O(log N).
// Driver code
int main()
{
int arr[] = { 2, 3, 4, 10, 40 };
int x = 10;
int n = sizeof(arr) / sizeof(arr[0]);
int result = binarySearch(arr, 0, n - 1, x);
(result == -1)
? cout << "Element is not present in array"
: cout << "Element is present at index " << result;
return 0;
}
Output
Element is present at index 3
Linear Search
Linear search is the simplest search algorithm. It works by iterating through a list of items,
one by one, until it finds the item it is searching for. If the item is not found, the algorithm
returns -1.
Binary Search
Binary search is a more efficient search algorithm than linear search. It works by dividing the
list in half, and then searching the half that is more likely to contain the item it is searching
for. This process is repeated until the item is found or the list is empty.
Comparison
Linear search is the simplest search algorithm, but it is also the least efficient. Binary search
is more efficient than linear search, but it is also more complex.
Here is a table that summarizes the key differences between linear search and binary search:
Conclusion
Binary search is the more efficient search algorithm, but it is also more complex. Linear
search is the simplest search algorithm, but it is also the least efficient.
Hashing
Hashing refers to the process of generating a fixed-size output from an input of variable size
using the mathematical formulas known as hash functions. This technique determines an index or
location for the storage of an item in a data structure.
Need for Hash data structure
The amount of data on the internet is growing exponentially every day, making it difficult to
store it all effectively. In day-to-day programming, this amount of data might not be that big, but
still, it needs to be stored, accessed, and processed easily and efficiently. A very common data
structure that is used for such a purpose is the Array data structure.
Now the question arises if Array was already there, what was the need for a new data structure!
The answer to this is in the word “efficiency“. Though storing in Array takes O(1) time,
searching in it takes at least O(log n) time. This time appears to be small, but for a large data set,
it can cause a lot of problems and this, in turn, makes the Array data structure inefficient.
So now we are looking for a data structure that can store the data and search in it in constant
time, i.e. in O(1) time. This is how Hashing data structure came into play. With the introduction
of the Hash data structure, it is now possible to easily store data in constant time and retrieve it in
constant time as well.
Components of Hashing
There are majorly three components of hashing:
1. Key: A Key can be anything string or integer which is fed as input in the hash
function, the technique that determines an index or location for storage of an item in a
data structure.
2. Hash Function: The hash function receives the input key and returns the index of an
element in an array called a hash table. The index is known as the hash index.
3. Hash Table: Hash table is a data structure that maps keys to values using a special
function called a hash function. Hash stores the data in an associative manner in an
array where each data value has its own unique index.
Components of Hashing
What is Collision?
The hashing process generates a small number for a big key, so there is a possibility that two
keys could produce the same value. The situation where the newly inserted key maps to an
already occupied, and it must be handled using some collision handling technology.
Collision in Hashing