The merge sort, in essence, is a divide-and-conquer algorithm. It breaks down a problem into multiple sub-problems, solves each individually, and finally combines these sub-problems solutions to form the final solution.
The key operation in merge sort is the merging step. This step is where the algorithm compares and combines the individual elements of the sublists. It takes advantage of the fact that each of the sublists is already sorted. The merging process continues until there is only one sorted list remaining.
This methodological partitioning allows for efficient sorting, even in large datasets.
The algorithm can be divided into two main steps:
- Divide: The divide step computes the midpoint of the indices, taking constant time regardless of the subarray size.
- Conquer (Merge): The conquer step recursively sorts two subarrays of approximately n/2 elements each. The time this step takes will be considered when we account for the subproblems. The combined step merges a total of n elements, taking Θ(n) time. This step is where the ‘merge’ in merge sort comes from.
Overall time complexity of Merge sort is O(nLogn).
Optimizing Merge Sort:
The Fusion of Merge Sort and Insertion Sort for Optimal Performance.
- We can cut the running time of merge sort substantially with some carefully considered modifications to the implementation.
- Use insertion sort for small subarrays. We can improve most recursive algorithms by handling small cases differently. Switching to insertion sort for small subarrays will improve the running time of a typical merge sort.
- This is because insertion sort, while worse in the long run, performs faster on small input sizes. Changing the base case of your merge sort so that if the array to sort is below a certain size threshold (say, 50–100), you switch to insertion sort, which can markedly improve the performance of the algorithm.
Why insertion sort for smaller dataset better than merge sort?
Insertion sort is often considered more efficient than merge sort for smaller datasets due to its lower overhead and simpler implementation. Time complexity of insertion sort is O(n^2) but for small datasets, the quadratic time complexity doesn’t impose a significant overhead thus, O(n^2)~ O(n), and the simplicity of the algorithm makes it efficient. While merge sort involves more complex operations and has higher space requirements.
Implementation of merge sort with insertion sort for better complexity and optimal performance.
#include <iostream> #include <vector> using namespace std;
// Merge two subarrays into array void merge(vector< int >& array, int p, int q, int r) {
// Calculate the sizes of the two subarrays
int n1 = q - p + 1;
int n2 = r - q;
// Create temporary vectors to hold the two subarrays
vector< int > leftArray(n1);
vector< int > rightArray(n2);
// Copy data to temporary vectors leftArray[] and rightArray[]
for ( int i = 0; i < n1; ++i) {
leftArray[i] = array[p + i];
}
for ( int j = 0; j < n2; ++j) {
rightArray[j] = array[q + 1 + j];
}
// Merge the two subarrays back into the original array
int i = 0; // Initial index of first subarray
int j = 0; // Initial index of second subarray
int k = p; // Initial index of merged subarray
while (i < n1 && j < n2) {
if (leftArray[i] <= rightArray[j]) {
array[k] = leftArray[i];
++i;
} else {
array[k] = rightArray[j];
++j;
}
++k;
}
// Copy the remaining elements of leftArray[], if there are any
while (i < n1) {
array[k] = leftArray[i];
++i;
++k;
}
// Copy the remaining elements of rightArray[], if there are any
while (j < n2) {
array[k] = rightArray[j];
++j;
++k;
}
} // Divide the vector into two subarrays, sort them, and merge them void mergeSort(vector< int >& array, int left, int right) {
if (left < right) {
// m is the point where the array is divided into two subarrays
int mid = (left + right) / 2;
// recursive calls to each subarray
mergeSort(array, left, mid);
mergeSort(array, mid + 1, right);
// Merge the sorted subarrays
merge(array, left, mid, right);
}
} int main() {
// created an unsorted vector
vector< int > array = {6, 5, 12, 10, 9, 1};
// call the method mergeSort()
mergeSort(array, 0, array.size() - 1);
cout << "Sorted Array:" << endl;
for ( int num : array) {
cout << num << " " ;
}
cout << endl;
return 0;
} |
import java.io.*;
import java.util.Arrays;
class Main {
// Merge two subarrays into array
void merge( int array[], int p, int q, int r)
{
// Calculate the sizes of the two subarrays
int n1 = q - p + 1 ;
int n2 = r - q;
// Create temporary arrays to hold the two subarrays
int [] leftArray = new int [n1];
int [] rightArray = new int [n2];
// Copy data to temporary arrays leftArray[] and
// rightArray[]
for ( int i = 0 ; i < n1; ++i) {
leftArray[i] = array[p + i];
}
for ( int j = 0 ; j < n2; ++j) {
rightArray[j] = array[q + 1 + j];
}
// Merge the two subarrays back into the original
// array
int i = 0 ; // Initial index of first subarray
int j = 0 ; // Initial index of second subarray
int k = p; // Initial index of merged subarray
while (i < n1 && j < n2) {
if (leftArray[i] <= rightArray[j]) {
array[k] = leftArray[i];
++i;
}
else {
array[k] = rightArray[j];
++j;
}
++k;
}
// Copy the remaining elements of leftArray[], if
// there are any
while (i < n1) {
array[k] = leftArray[i];
++i;
++k;
}
// Copy the remaining elements of rightArray[], if
// there are any
while (j < n2) {
array[k] = rightArray[j];
++j;
++k;
}
}
// Divide the array into two subarrays, sort them, and
// merge them
void mergeSort( int array[], int left, int right)
{
if (left < right) {
// m is the point where the array is divided
// into two subarrays
int mid = (left + right) / 2 ;
// recursive calls to each subarray
mergeSort(array, left, mid);
mergeSort(array, mid + 1 , right);
// Merge the sorted subarrays
merge(array, left, mid, right);
}
}
public static void main(String args[])
{
// created an unsorted array
int [] array = { 6 , 5 , 12 , 10 , 9 , 1 };
Main ob = new Main();
// call the method mergeSort()
ob.mergeSort(array, 0 , array.length - 1 );
System.out.println("Sorted Array:");
System.out.println(Arrays.toString(array));
}
} |
# Merge two subarrays into array def merge(array, p, q, r):
# Calculate the sizes of the two subarrays
n1 = q - p + 1
n2 = r - q
# Create temporary lists to hold the two subarrays
left_array = array[p:p + n1]
right_array = array[q + 1 :q + 1 + n2]
# Merge the two subarrays back into the original array
i = j = 0
k = p
while i < n1 and j < n2:
if left_array[i] < = right_array[j]:
array[k] = left_array[i]
i + = 1
else :
array[k] = right_array[j]
j + = 1
k + = 1
# Copy the remaining elements of left_array[], if any
while i < n1:
array[k] = left_array[i]
i + = 1
k + = 1
# Copy the remaining elements of right_array[], if any
while j < n2:
array[k] = right_array[j]
j + = 1
k + = 1
# Divide the list into two subarrays, sort them, and merge them def merge_sort(array, left, right):
if left < right:
# m is the point where the array is divided into two subarrays
mid = (left + right) / / 2
# recursive calls to each subarray
merge_sort(array, left, mid)
merge_sort(array, mid + 1 , right)
# Merge the sorted subarrays
merge(array, left, mid, right)
if __name__ = = "__main__" :
# Create an unsorted list
array = [ 6 , 5 , 12 , 10 , 9 , 1 ]
# Call the method merge_sort()
merge_sort(array, 0 , len (array) - 1 )
print ( "Sorted Array:" )
for num in array:
print (num, end = " " )
print ()
# This code is contributed by rambabuguphka |
using System;
using System.Collections.Generic;
class Program
{ // Merge two subarrays into array
static void Merge(List< int > array, int p, int q, int r)
{
int n1 = q - p + 1;
int n2 = r - q;
List< int > leftArray = new List< int >(n1);
List< int > rightArray = new List< int >(n2);
for ( int x = 0; x < n1; ++x)
{
leftArray.Add(array[p + x]);
}
for ( int y = 0; y < n2; ++y)
{
rightArray.Add(array[q + 1 + y]);
}
int i = 0, j = 0;
int k = p;
while (i < n1 && j < n2)
{
if (leftArray[i] <= rightArray[j])
{
array[k] = leftArray[i];
++i;
}
else
{
array[k] = rightArray[j];
++j;
}
++k;
}
while (i < n1)
{
array[k] = leftArray[i];
++i;
++k;
}
while (j < n2)
{
array[k] = rightArray[j];
++j;
++k;
}
}
// Divide the vector into two subarrays, sort them, and merge them
static void MergeSort(List< int > array, int left, int right)
{
if (left < right)
{
int mid = (left + right) / 2;
MergeSort(array, left, mid);
MergeSort(array, mid + 1, right);
Merge(array, left, mid, right);
}
}
static void Main( string [] args)
{
List< int > array = new List< int > { 6, 5, 12, 10, 9, 1 };
MergeSort(array, 0, array.Count - 1);
Console.WriteLine( "Sorted Array:" );
foreach ( int num in array)
{
Console.Write(num + " " );
}
Console.WriteLine();
}
} // This code is contributed by shivamgupta0987654321 |
// Javascript Implementation // Function to merge two subarrays into the main array function merge(array, p, q, r) {
let n1 = q - p + 1;
let n2 = r - q;
let leftArray = new Array(n1);
let rightArray = new Array(n2);
for (let i = 0; i < n1; ++i) {
leftArray[i] = array[p + i];
}
for (let j = 0; j < n2; ++j) {
rightArray[j] = array[q + 1 + j];
}
let i = 0, j = 0, k = p;
while (i < n1 && j < n2) {
if (leftArray[i] <= rightArray[j]) {
array[k] = leftArray[i];
++i;
} else {
array[k] = rightArray[j];
++j;
}
++k;
}
while (i < n1) {
array[k] = leftArray[i];
++i;
++k;
}
while (j < n2) {
array[k] = rightArray[j];
++j;
++k;
}
} // Function to perform merge sort function mergeSort(array, left, right) {
if (left < right) {
let mid = Math.floor((left + right) / 2);
mergeSort(array, left, mid);
mergeSort(array, mid + 1, right);
merge(array, left, mid, right);
}
} // Driver code let array = [6, 5, 12, 10, 9, 1]; mergeSort(array, 0, array.length - 1); console.log( "Sorted Array:" );
console.log(array.join( " " ));
// This code is contributed by Tapesh(tapeshdua420) |
Sorted Array: [1, 5, 6, 9, 10, 12]
Understanding and leveraging the merge sort algorithm can significantly enhance your code’s performance, particularly when working with large datasets.