Every search result you see is sorted by relevance. Every database query uses binary search on indexes. These algorithms are everywhere โ and they're interview staples.
Repeatedly swap adjacent elements if they're in the wrong order.
Walkthrough with [5, 3, 8, 1]:
| Pass | Comparisons | Result | |------|------------|--------| | 1 | 5>3 swap, 5<8 skip, 8>1 swap | [3, 5, 1, 8] | | 2 | 3<5 skip, 5>1 swap | [3, 1, 5, 8] | | 3 | 3>1 swap | [1, 3, 5, 8] |
Time: O(nยฒ) | Space: O(1)
Bubble sort is only useful for learning. In interviews, mention it to show you know why better algorithms exist โ then immediately pivot to merge sort or quick sort.
Split the array in half recursively, then merge sorted halves back together.
The algorithm in 3 steps:
The merge step is the key โ compare front elements of each half:
| Left | Right | Pick | Result so far | |------|-------|------|---------------| | 3, 27, 38 | 9, 43, 82 | 3 | [3] | | 27, 38 | 9, 43, 82 | 9 | [3, 9] | | 27, 38 | 43, 82 | 27 | [3, 9, 27] | | 38 | 43, 82 | 38 | [3, 9, 27, 38] | | โ | 43, 82 | 43, 82 | [3, 9, 27, 38, 43, 82] |
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
Time: O(n log n) | Space: O(n)
Why O(n log n)? The array splits log(n) times (halving). At each level, we do O(n) work merging. Total: n work x log(n) levels = O(n log n).
| Algorithm | Best | Average | Worst | Space | Stable? | |-----------|------|---------|-------|-------|---------| | Bubble Sort | O(n) | O(nยฒ) | O(nยฒ) | O(1) | Yes | | Merge Sort | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes | | Quick Sort | O(n log n) | O(n log n) | O(nยฒ) | O(log n) | No | | Heap Sort | O(n log n) | O(n log n) | O(n log n) | O(1) | No |
Stable means equal elements keep their original order. This matters when sorting by multiple keys โ e.g., sort by name, then by age. A stable sort preserves the name ordering within same-age groups.
Find a target in a sorted array by eliminating half the elements each step.
Prerequisite: Array MUST be sorted.
Walkthrough โ find 23 in [2, 5, 11, 15, 23, 37, 41, 56]:
| Step | Range | Mid | Compare | Action | |------|-------|-----|---------|--------| | 1 | [2..56] | 15 | 23 > 15 | Search right half | | 2 | [23..56] | 37 | 23 < 37 | Search left half | | 3 | [23] | 23 | 23 = 23 | Found at index 4! |
3 steps instead of 8 (linear scan). For 1 million elements: 20 steps instead of 1,000,000.
def binary_search(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2 # avoids overflow
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1 # not found
Time: O(log n) | Space: O(1)
Binary search is deceptively tricky to implement correctly. A study by Jon Bentley found that 90% of professional programmers couldn't write a bug-free binary search. The most common bug? Integer overflow in (left + right) / 2 โ which is why we use left + (right - left) // 2.
Binary search isn't just for "find exact value." These variants appear constantly:
| Variant | Description | Key Change |
|---------|-------------|------------|
| Lower bound | First position where element could be inserted | left when nums[mid] >= target โ right = mid |
| Upper bound | Position after last occurrence | left when nums[mid] > target โ right = mid |
| Search rotated array | Sorted array rotated at unknown pivot | Check which half is sorted first |
| Search answer space | Find min/max satisfying a condition | Binary search on the answer, not the array |
"Binary search on the answer space" is a powerful technique. Example: "What's the minimum capacity to ship packages in D days?" Binary search between min and max possible capacity, checking if each candidate works. This turns an optimization problem into a decision problem.
Sorting and searching are fundamental to how AI systems deliver results:
Search engines (Google, Bing):
Recommendation systems:
Database query optimisation:
Google processes over 8.5 billion searches per day. Each query searches an index of hundreds of billions of web pages. Without sorted indexes and binary search, a single query would take minutes instead of milliseconds.
| Operation | Unsorted Array | Sorted Array | |-----------|---------------|--------------| | Search | O(n) | O(log n) | | Insert | O(1) | O(n)* | | Find min/max | O(n) | O(1) | | Find k-th element | O(n) | O(1) |
*Must shift elements to maintain sorted order
The trade-off is clear: sorting costs O(n log n) upfront, but enables O(log n) searches forever after. If you search more than O(log n) times, sorting pays for itself.