Merge Sort Algorithm¶
Algorithm Introduction¶
Merge sort is an efficient, divide-and-conquer based sorting algorithm. It divides the array into two halves, sorts each half recursively, and then merges the sorted halves. Merge sort is a stable sorting algorithm suitable for large datasets.
Algorithm Steps¶
- Divide the unsorted array into two equal-sized subarrays
- Recursively sort each subarray
- Merge the two sorted subarrays into one sorted array
Code Implementation¶
def merge_sort(arr):
if len(arr) <= 1:
return arr
# Divide the array
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]
# Recursively sort both halves
left_half = merge_sort(left_half)
right_half = merge_sort(right_half)
# Merge the two sorted halves
return merge(left_half, right_half)
def merge(left, right):
result = []
i = j = 0
# Compare elements from both arrays and merge
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# Add remaining elements
result.extend(left[i:])
result.extend(right[j:])
return result
# Test
arr = [5, 3, 8, 4, 2]
sorted_arr = merge_sort(arr)
print("Sorted array:", sorted_arr)
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] arr = {5, 3, 8, 4, 2};
int[] sorted = mergeSort(arr);
System.out.println("Sorted array:");
for (int num : sorted) {
System.out.print(num + " ");
}
}
public static int[] mergeSort(int[] arr) {
if (arr.length <= 1) {
return arr;
}
// Divide the array
int mid = arr.length / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, arr.length);
// Recursively sort both halves
left = mergeSort(left);
right = mergeSort(right);
// Merge the two sorted halves
return merge(left, right);
}
private static int[] merge(int[] left, int[] right) {
int[] result = new int[left.length + right.length];
int i = 0, j = 0, k = 0;
// Compare elements from both arrays and merge
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result[k++] = left[i++];
} else {
result[k++] = right[j++];
}
}
// Add remaining elements
while (i < left.length) {
result[k++] = left[i++];
}
while (j < right.length) {
result[k++] = right[j++];
}
return result;
}
}
Complexity Analysis¶
- Time Complexity:
- Worst case: O(n log n)
- Average case: O(n log n)
- Best case: O(n log n)
- Space Complexity: O(n) (requires additional space for merging)
Sorting Example¶
Let's understand how merge sort works with a concrete example. Suppose we have an array: [5, 3, 8, 4, 2]
Divide Phase¶
First, we recursively divide the array into smaller subarrays:
First division:
Second division:
Third division:
Now we have single-element subarrays: [5], [3], [8], [4], [2], which are all sorted (single elements are always sorted).
Merge Phase¶
Next, we start merging these subarrays from bottom up:
First merge:
Merging [4] and [2]:
Then merge [8] and [2, 4]:
Compare 8 and 2: 2 < 8, take 2
Result: [2]
Compare 8 and 4: 4 < 8, take 4
Result: [2, 4]
Remaining: [8]
Final result: [2, 4, 8]
Second merge:
Detailed merge process:
Compare 3 and 2: 2 < 3, take 2
Result: [2]
Compare 3 and 4: 3 < 4, take 3
Result: [2, 3]
Compare 5 and 4: 4 < 5, take 4
Result: [2, 3, 4]
Compare 5 and 8: 5 < 8, take 5
Result: [2, 3, 4, 5]
Remaining: [8]
Final result: [2, 3, 4, 5, 8]
Final Result¶
After sorting, we get the sorted array: [2, 3, 4, 5, 8]
This example demonstrates key characteristics of merge sort: - Divide-and-conquer approach: breaking the problem into smaller subproblems - Recursion: applying the same sorting algorithm to subproblems - Merging: combining sorted subarrays into larger sorted arrays - Stability: relative order of equal elements is preserved after sorting
Tree Visualization¶
Exercises¶
- Implement an in-place version of merge sort to reduce space complexity
- Analyze the differences between merge sort implementations for linked lists versus arrays
- Compare the performance differences between merge sort and quick sort
- Implement a bottom-up, non-recursive version of merge sort