An Overview of Sorting Algorithms: Examining the Different Types and Their Uses
Sorting algorithms play a crucial role in a variety of computer science applications. They can be used to tackle a wide range of issues and are used to efficiently and effectively sort data.
We will discuss 7 types of sorting algorithms:
Insertion Sort
Selection Sort
Merge Sort
Bubble Sort
Quick Sort
Heap Sort
Radix Sort
1. Insertion Sort
Insertion sort is a simple sorting algorithm that works the way we sort playing cards in our hands. By comparing each item with those to the left and placing it in the proper position, it produces the final sorted array one item at a time.
An illustration of how the insertion sort algorithm operates on an unsorted list of numbers is provided below:
Unsorted list: [7, 2, 4, 1, 5, 3]
Start with the second element (2) and compare it with the first element (7). Since 2 is smaller than 7, swap their positions.
Sorted list: [2, 7, 4, 1, 5, 3]
Move to the third element (4) and compare it with the elements to its left. Since 4 is greater than 2 and less than 7, it should be inserted between them.
Sorted list: [2, 4, 7, 1, 5, 3]
Continue this process for the remaining elements until the list is completely sorted.
Sorted list: [1, 2, 3, 4, 5, 7]
In the worst situation, the insertion sort's time complexity is O(n2), where n is the number of elements in the list. It is useful for short lists, though, and is frequently included into more intricate sorting algorithms.
Now let's check this on code:
void insertionSort(int arr[], int n) {
int i, key, j;
for (i = 1; i < n; i++) {
key = arr[i];
j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
The "insertionSort" function in this implementation accepts two arguments: an integer array arr and its length n. Using the insertion sort technique, the function arranges the array in ascending order.
Starting with the second element (i.e., index 1), the algorithm iterates through each element of the array. It moves each element to the right until it determines the proper place for the current element for each element by comparing it to the ones to its left. The while loop, located inside the for loop, does this.
The time complexity of this algorithm is O(n^2) in the worst case, where n is the length of the array.
2.Selection Sort
Another straightforward sorting technique is selection sort, which functions by repeatedly selecting the lowest-value element from the array's unsorted section and inserting it at the beginning of the array.
#include <stdio.h>
void selectionSort(int arr[], int n) {
int i, j, min_idx;
for (i = 0; i < n-1; i++) {
min_idx = i;
for (j = i+1; j < n; j++) {
if (arr[j] < arr[min_idx]) {
min_idx = j;
}
}
// Swap the found minimum element with the first element
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}
int main() {
int arr[] = {64, 25, 12, 22, 11};
int n = sizeof(arr)/sizeof(arr[0]);
selectionSort(arr, n);
printf("Sorted array: ");
for (int i=0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
In this illustration, we first build a function called "selectionSort" that accepts as input an array of numbers called "arr" and a length "n". The inner loop extends from the element immediately following the outer loop's index to the last element of the array, whereas the outer loop extends from the first element to the array's next-to-last element.
Each element is compared to the most recent minimal element discovered inside the inner loop. The "min_idx" variable is updated to store the index of the smaller element if one is discovered. After completing the inner loop iterations, we exchange the minimum element we discovered with the first element of the array's unsorted section.
Using a for loop and the "printf" function, we print the sorted array after using the "selectionSort" function to sort the array.
3.Merge Sort
A well-liked divide-and-conquer algorithm for sorting elements in an array or list is merge sort. The algorithm splits the input array into two equal halves, sorts the two parts iteratively, and then combines the sorted parts to get the final output.The fundamental principle of merge sort is to split the input array in half until each side has exactly one element. The individual elements are then compared and combined to form sorted pairings. Once all of the input array is sorted, the sorted pairs are combined into sorted four-element groups.
Merge sort has a time complexity of O(n log n), which makes it more efficient than simple quadratic sorting algorithms like bubble sort or insertion sort. However, merge sort requires additional memory space to store the temporary arrays during the sorting process.
#include <stdio.h>
// Merge two subarrays into a sorted array
void merge(int arr[], int left[], int right[], int left_size, int right_size) {
int i = 0, j = 0, k = 0;
while (i < left_size && j < right_size) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
k++;
}
while (i < left_size) {
arr[k] = left[i];
i++;
k++;
}
while (j < right_size) {
arr[k] = right[j];
j++;
k++;
}
}
// Recursive merge sort function
void merge_sort(int arr[], int size) {
if (size < 2) {
return;
}
int mid = size / 2;
int left[mid], right[size - mid];
for (int i = 0; i < mid; i++) {
left[i] = arr[i];
}
for (int i = mid; i < size; i++) {
right[i - mid] = arr[i];
}
merge_sort(left, mid);
merge_sort(right, size - mid);
merge(arr, left, right, mid, size - mid);
}
// Driver program to test the merge sort function
int main() {
int arr[] = { 12, 11, 13, 5, 6, 7 };
int size = sizeof(arr) / sizeof(arr[0]);
merge_sort(arr, size);
printf("Sorted array: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
4.Bubble Sort
"bubble sort" analyzes neighboring elements and swaps them if they are in the wrong order as it iteratively moves through the list to be sorted. Until no swaps are required—a sign that the list is sorted—this process is repeated. Smaller elements "bubble" to the top of the list as it is iterated through repeatedly, giving the algorithm its name. The worst-case and average complexity of bubble sort, where n is the number of items to be sorted, is O(n2).
#include <stdio.h>
void bubble_sort(int arr[], int n) {
int temp;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, n);
printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}
In this implementation, the bubble_sort() function takes an array of integers arr and its size n as arguments. The function then performs bubble sort on the array using nested for loops.
In the outer loop, i iterates from 0 to n - 2, and in the inner loop, j iterates from 0 to n - i - 2. Inside the inner loop, the function compares arr[j] and arr[j + 1] to determine whether they are in the correct order. If arr[j] is greater than arr[j + 1], the function swaps the two elements.
Once the function has completed sorting the array, the main() function calls bubble_sort() on an array of integers, prints the sorted array to the console, and returns 0.
5.Quick Sort
Another popular sorting algorithm that use the divide-and-conquer method is quick sort. It sorts an array of elements. It is a recursive comparison-based sorting method that divides an array into two sub-arrays before sorting the sub-arrays individually. One of the most effective sorting algorithms is quick sort, which has an average case time complexity of O(n log n).
Choose a pivot element from the array. The pivot element is used to partition the array into two sub-arrays.
Reorder the array so that all elements less than the pivot come before the pivot, while all elements greater than the pivot come after it.
Recursively apply the above steps to the sub-array of elements less than the pivot and the sub-array of elements greater than the pivot.
Initial array: {5, 2, 9, 3, 7, 6, 1, 8, 4}
Step 1: Choose a pivot element. Let's choose the first element, 5. Array: {5, 2, 9, 3, 7, 6, 1, 8, 4}
Step 2: Reorder the array so that all elements less than the pivot come before the pivot, while all elements greater than the pivot come after it. We can do this by comparing each element in the array to the pivot element, and swapping it with the element at the current index if it is less than the pivot. Array after partitioning: {2, 3, 1, 4, 5, 9, 7, 8, 6}
Step 3: Recursively apply the above steps to the sub-arrays of elements less than the pivot and elements greater than the pivot. Sub-array less than pivot: {2, 3, 1, 4} Choose pivot element: 2 Array after partitioning: {1, 2, 3, 4} Resulting sub-array: {1, 2, 3, 4}
Sub-array greater than pivot: {5, 9, 7, 8, 6} Choose pivot element: 5 Array after partitioning: {6, 5, 7, 8, 9} Resulting sub-array: {6, 5, 7, 8, 9}
Final sorted array: {1, 2, 3, 4, 5, 6, 7, 8, 9}
#include <stdio.h>
// A function to swap two elements
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
/* This function takes last element as pivot, places the pivot element
at its correct position in sorted array, and places all smaller (smaller than pivot)
to left of pivot and all greater elements to right of pivot */
int partition (int arr[], int low, int high)
{
int pivot = arr[high]; // pivot
int i = (low - 1); // Index of smaller element
for (int j = low; j <= high- 1; j++)
{
// If current element is smaller than or equal to pivot
if (arr[j] <= pivot)
{
i++; // increment index of smaller element
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
}
/* The main function that implements QuickSort
arr[] --> Array to be sorted,
low --> Starting index,
high --> Ending index */
void quickSort(int arr[], int low, int high)
{
if (low < high)
{
/* pi is partitioning index, arr[p] is now at right place */
int pi = partition(arr, low, high);
// Separately sort elements before
// partition and after partition
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
/* Function to print an array */
void printArray(int arr[], int size)
{
int i;
for (i=0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
// Driver program to test above functions
int main()
{
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr)/sizeof(arr[0]);
quickSort(arr, 0, n-1);
printf("Sorted array: ");
printArray(arr, n);
return 0;
}
The
swap
function swaps the values of two integers passed to it.The
partition
function takes the last element of the array as pivot and places it at its correct position in the sorted array.The
quickSort
function recursively calls itself to sort the elements before and after the partitioning index.The
printArray
function is used to print the sorted array.
When the quickSort
function is called for the first time, it passes the arr
array, starting index 0
and the ending index n-1
. It calculates the partitioning index pi
using the partition
function and then recursively calls itself for the two subarrays on both sides of pi
.
The partition
function iterates over the array and places all the elements smaller than or equal to the pivot on the left side of the pivot and all the elements greater than the pivot on the right side of the pivot. Finally, it places the pivot at its correct position in the sorted array.
6.Heap Sort
The unsorted input data are first converted into a binary heap data structure, which has the feature that every parent node is higher (for max-heap) or lesser (for min-heap) than its offspring. Heap sort is a comparison-based sorting algorithm. The final step is to remove the largest (or smallest) element from the heap and place it at the very end of the output array. A sorted array is produced by repeating this procedure up until the heap is completely empty.
Convert the input array into a binary heap using the heapify procedure. The heap will have the property that each parent node is greater than or equal to its children.
Input array: [7, 2, 9, 1, 5, 4, 3, 8, 6] Binary heap: [9, 8, 7, 1, 5, 4, 3, 2, 6]
Swap the root (maximum element) with the last element of the heap and remove it from the heap.
Binary heap: [8, 5, 7, 1, 6, 4, 3, 2]
Re-heapify the remaining heap to maintain the heap property.
Binary heap: [8, 6, 7, 1, 5, 4, 3, 2]
Repeat steps 2-3 until the heap is empty.
Sorted array: [1, 2, 3, 4, 5, 6, 7, 8, 9]
// Heapify a subtree rooted with node i, n is size of heap
void heapify(int arr[], int n, int i) {
int largest = i; // Initialize largest as root
int l = 2*i + 1; // Left child
int r = 2*i + 2; // Right child
// If left child is larger than root
if (l < n && arr[l] > arr[largest])
largest = l;
// If right child is larger than largest so far
if (r < n && arr[r] > arr[largest])
largest = r;
// If largest is not root
if (largest != i) {
swap(&arr[i], &arr[largest]);
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
// Main function to perform heap sort
void heapSort(int arr[], int n) {
// Build heap (rearrange array)
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (int i = n-1; i >= 0; i--) {
// Move current root to end
swap(&arr[0], &arr[i]);
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
In the heapify
function, we start with a root node i
and compare it with its left and right children. We determine the largest element among the root and its children and swap them if necessary to maintain the heap property. This process is repeated recursively on the affected sub-tree until the entire heap is in order.
In the heapSort
function, we first build the heap by calling heapify
on all non-leaf nodes.
7.Radix Sort
A non-comparison-based sorting algorithm called radix sort arranges elements by analyzing their numbers or bits. Both string and integer data types can be sorted using radix. The method arranges the items according to their least to greatest or greatest to least significant digit.
Find the maximum element in the input array to determine the number of digits or bits to process.
For each digit or bit position, starting from the least significant to the most significant, perform a stable sort (such as counting sort or bucket sort) on the input array based on that digit or bit.
Repeat step 2 for all digit or bit positions.
The sorted array is the final output.
#include <stdio.h>
void radix_sort(int arr[], int n) {
int i, j, m = arr[0], exp = 1;
int bucket[10] = {0};
int output[n];
// Find the maximum element in the array
for (i = 1; i < n; i++) {
if (arr[i] > m) {
m = arr[i];
}
}
// Sort by each digit using counting sort
while (m / exp > 0) {
for (i = 0; i < 10; i++) {
bucket[i] = 0;
}
for (i = 0; i < n; i++) {
bucket[arr[i] / exp % 10]++;
}
for (i = 1; i < 10; i++) {
bucket[i] += bucket[i - 1];
}
for (i = n - 1; i >= 0; i--) {
output[bucket[arr[i] / exp % 10] - 1] = arr[i];
bucket[arr[i] / exp % 10]--;
}
for (i = 0; i < n; i++) {
arr[i] = output[i];
}
exp *= 10;
}
}
int main() {
int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};
int n = sizeof(arr) / sizeof(arr[0]);
radix_sort(arr, n);
printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
The "radix_sort" function takes an array arr of integers and its size "n" as input. The function first finds the maximum element in the array to determine the number of digits to process. It then performs a stable sort on the array for each digit using counting sort. The bucket array is used to store the count of elements based on their digit, and the output array is used to store the sorted elements. The sorted array is then copied back to the original array, and the process is repeated for each digit until all digits have been processed. The sorted array is printed at the end of the program using a loop.