Bubble Sort, Merge Sort, and Quick Sort Explained with Code
Sorting is one of the most fundamental operations in computer science. Whether you’re arranging names alphabetically or optimizing search performance, sorting algorithms are essential.
In this article, we’ll dive into three classic sorting algorithms:
- 🫧 Bubble Sort (Simple, but inefficient)
- 🧩 Merge Sort (Divide & Conquer based)
- ⚡ Quick Sort (Fast and practical)
Each comes with its own strengths, weaknesses, and Python implementation.
📚 1. Bubble Sort
🫧 The Simplest Sort – Good for Learning
Concept:
Repeatedly compare each pair of adjacent elements and swap them if they are in the wrong order. Continue until no swaps are needed.
Time Complexity:
- Best: O(n)
- Average/Worst: O(n²)
Python Code:
def bubble_sort(arr):
n = len(arr)
for i in range(n):
swapped = False
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
break
return arr
print(bubble_sort([64, 34, 25, 12, 22, 11, 90]))
🖨️ Output:
[11, 12, 22, 25, 34, 64, 90]
✅ Best For: Education, small datasets
❌ Not Recommended for performance-critical applications
🧠 2. Merge Sort
🧩 A Powerful Divide & Conquer Algorithm
Concept:
Split the array into halves, recursively sort them, and then merge the sorted halves.
Time Complexity:
- Best/Worst/Average: O(n log n)
Python Code:
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
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
result.extend(left[i:])
result.extend(right[j:])
return result
print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
🖨️ Output:
[3, 9, 10, 27, 38, 43, 82]
✅ Best For: Stable sorting, predictable performance
❌ Requires extra space (O(n))
⚡ 3. Quick Sort
The Fast and Practical Sort
Concept:
Choose a “pivot” and partition the array into two sub-arrays:
- One with elements smaller than the pivot
- One with elements greater than the pivot
Recursively apply the same logic.
Time Complexity:
- Best/Average: O(n log n)
- Worst: O(n²) (rare, with bad pivot choice)
Python Code:
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[0]
less = [x for x in arr[1:] if x <= pivot]
greater = [x for x in arr[1:] if x > pivot]
return quick_sort(less) + [pivot] + quick_sort(greater)
print(quick_sort([10, 7, 8, 9, 1, 5]))
🖨️ Output:
[1, 5, 7, 8, 9, 10]
✅ Best For: In-place sorting, average-case performance
❌ Unstable, worst-case performance can degrade
📊 Performance Comparison
Algorithm | Time Complexity (Average) | Stable? | In-Place? | Extra Space |
---|---|---|---|---|
Bubble Sort | O(n²) | ✅ Yes | ✅ Yes | ❌ No |
Merge Sort | O(n log n) | ✅ Yes | ❌ No | ✅ Yes (O(n)) |
Quick Sort | O(n log n) | ❌ No | ✅ Yes | ✅ No |
🎯 Choosing the Right Sort
- Use Bubble Sort only for learning.
- Use Merge Sort when stability and predictable performance matter.
- Use Quick Sort for practical use, especially when space is limited.
🧪 Challenge: Sort with Custom Comparator
Try writing a sort that:
- Sorts words based on length instead of alphabetical order
words = ["banana", "apple", "kiwi", "strawberry"]
sorted_words = sorted(words, key=len)
print(sorted_words)
🖨️ Output:
['kiwi', 'apple', 'banana', 'strawberry']
🧠 Summary
You’ve now learned:
- The core logic behind 3 famous sorting algorithms
- When and why to use each
- Python implementations to practice