Five Classic Sorting Algorithms: Descriptions and Complexity
This post describes five well-known sorting algorithms that can sort a list of \( n \) distinct elements \( A_1, A_2, \dots, A_n \). We analyze each algorithm in terms of time and space complexity.
1. Insertion Sort
Description: Builds the sorted array one item at a time. At each step, it inserts the next element into its correct position among the previously sorted elements.
Pseudocode:
for i in range(1, n):
key = A[i]
j = i - 1
while j >= 0 and A[j] > key:
A[j + 1] = A[j]
j -= 1
A[j + 1] = key
Complexity:
-
Time:
- Worst-case: \( O(n^2) \)
- Best-case (already sorted): \( O(n) \)
-
Space: \( O(1) \)
2. Merge Sort
Description: A divide-and-conquer algorithm. Splits the array into halves, recursively sorts each half, then merges the sorted halves.
Pseudocode:
def merge_sort(A):
if len(A) <= 1:
return A
mid = len(A) // 2
left = merge_sort(A[:mid])
right = merge_sort(A[mid:])
return merge(left, right)
Complexity:
- Time: \( O(n \log n) \) in all cases
- Space: \( O(n) \) extra space for merging
3. Quick Sort
Description: A divide-and-conquer algorithm. Chooses a pivot, partitions the array into elements less than and greater than the pivot, then recursively sorts the partitions.
Pseudocode:
def quick_sort(A):
if len(A) <= 1:
return A
pivot = A[0]
left = [x for x in A[1:] if x < pivot]
right = [x for x in A[1:] if x >= pivot]
return quick_sort(left) + [pivot] + quick_sort(right)
Complexity:
-
Time:
- Average: \( O(n \log n) \)
- Worst-case: \( O(n^2) \) (e.g., already sorted and poor pivot)
-
Space:
- Average: \( O(\log n) \) for recursive calls
4. Bubble Sort
Description: Repeatedly compares adjacent elements and swaps them if out of order. Continues until no swaps are needed.
Pseudocode:
for i in range(n):
for j in range(0, n - i - 1):
if A[j] > A[j + 1]:
A[j], A[j + 1] = A[j + 1], A[j]
Complexity:
-
Time:
- Worst and Average: \( O(n^2) \)
- Best (already sorted): \( O(n) \)
-
Space: \( O(1) \)
5. Selection Sort
Description: Repeatedly finds the smallest remaining element and swaps it into the next position.
Pseudocode:
for i in range(n):
min_index = i
for j in range(i + 1, n):
if A[j] < A[min_index]:
min_index = j
A[i], A[min_index] = A[min_index], A[i]
Complexity:
- Time: \( O(n^2) \) in all cases
- Space: \( O(1) \)
Summary Table
Algorithm | Time (Best) | Time (Avg) | Time (Worst) | Space |
---|---|---|---|---|
Insertion Sort | \( O(n) \) | \( O(n^2) \) | \( O(n^2) \) | \( O(1) \) |
Merge Sort | \( O(n \log n) \) | \( O(n \log n) \) | \( O(n \log n) \) | \( O(n) \) |
Quick Sort | \( O(n \log n) \) | \( O(n \log n) \) | \( O(n^2) \) | \( O(\log n) \) |
Bubble Sort | \( O(n) \) | \( O(n^2) \) | \( O(n^2) \) | \( O(1) \) |
Selection Sort | \( O(n^2) \) | \( O(n^2) \) | \( O(n^2) \) | \( O(1) \) |