Simple Sorting & Searching
dsa.fetehadin.comFind the smallest remaining item and swap it to the front. Repeat. Simple, at most n − 1 swaps, but always O(n²) comparisons.
| Best case | O(n²): it still scans for the minimum every pass |
| Average case | O(n²) |
| Worst case | O(n²): n(n−1)/2 comparisons |
| Swaps | at most n − 1 (the fewest of the simple sorts) |
| Space | O(1) |
| Stable · in-place | no · yes |
- ✗Swapping inside the inner loop every time a smaller item is found. Swap once, after the inner loop.
- ✗Starting the inner loop at
j = iinstead ofj = i + 1(compares an item with itself). - ✗Claiming selection sort has an O(n) best case. It has none; it always scans.
Compare neighbours and swap them if they are out of order. After each pass the biggest remaining item has "bubbled" to the end.
| Best case (already sorted) | O(n): one pass, no swaps, stop |
| Average case | O(n²) |
| Worst case (reversed) | O(n²): n(n−1)/2 comparisons and swaps |
| Space | O(1) |
| Stable · in-place | yes · yes |
- ✗Looping
jup ton - 1in every pass instead ofn - 1 - i. It still works, but does useless comparisons. - ✗Writing
a[j] >= a[j+1]. Swapping equal items breaks stability. - ✗Reading past the array with
j < nand then accessinga[j + 1].
Grow a sorted part on the left by inserting each new item into its correct place, like sorting cards in your hand. Fast on nearly sorted data.
| Best case (already sorted) | O(n): n − 1 comparisons, 0 shifts |
| Average case | O(n²) |
| Worst case (reversed) | O(n²): n(n−1)/2 comparisons and shifts |
| Space | O(1) |
| Stable · in-place | yes · yes |
- ✗Writing
a[j] >= keyin the while loop. That breaks stability. - ✗Forgetting
j >= 0in the loop condition, which readsa[-1]. - ✗Putting
a[j + 1] = keyinside the while loop. It goes after the loop.
When records are big, don't move them. Sort an array of pointers (or indices) to the records instead, and only small addresses get swapped.
| Comparisons | same as the underlying sort (here selection: n(n−1)/2) |
| Data moved per swap | one pointer (8 bytes) instead of one whole record |
| Time | O(n²) with selection sort; O(n log n) with a fast sort |
| Space | O(n) for the pointer array |
| Stable · in-place | no · no |
- ✗Swapping the records (
swap(*p[i], *p[min])) instead of the pointers (swap(p[i], p[min])). That defeats the purpose. - ✗Comparing the pointers themselves (
p[j] < p[min]) instead of what they point to (p[j]->score). - ✗Forgetting to initialise
p[i] = &s[i]before sorting.
Check every item from left to right until you find the key or run out. Works on any array, sorted or not, but costs O(n).
| Best case | O(1): key at index 0 |
| Average case (found) | (n + 1)/2 comparisons → O(n) |
| Worst case | O(n): last item or not present |
| Needs sorted data? | No |
| Space | O(1) |
- ✗Returning
-1inside the loop (in anelse), which stops after checking only the first item. - ✗Looping with
i <= n, one past the end. - ✗Forgetting that an unsorted array cannot use binary search.
On a sorted array, check the middle, then throw away the half that cannot contain the key. Each step halves the problem, so it takes O(log n).
| Best case | O(1): key is at the first mid |
| Average / worst case | O(log n): about ⌊log₂ n⌋ + 1 comparisons |
| 1,000,000 items | at most 20 comparisons |
| Needs sorted data? | Yes, and random access (an array, not a linked list) |
| Space | O(1) iterative · O(log n) recursive (call stack) |
- ✗Using
while (low < high)instead oflow <= high. That misses a key when one item is left. - ✗Setting
low = midorhigh = midinstead ofmid ± 1, which can loop forever. - ✗Running it on unsorted data. The result is meaningless.
A function that calls itself on a smaller problem until it reaches a base case. You need this for trees (Ch 6) and merge/quick sort (Ch 8).
| factorial(n): time | O(n): n calls, O(1) work each |
| factorial(n): space | O(n) stack frames |
| recursive binary search: time | O(log n) |
| recursive binary search: space | O(log n) stack frames (iterative version: O(1)) |
- ✗No base case, or a base case that is never reached (e.g.
factorial(n + 1)). - ✗Forgetting to
returnthe recursive call's result (bs(...)instead ofreturn bs(...)). - ✗Thinking recursion is always slower or always better. It trades stack memory for simpler code.
- Stable sort:
- A sort that keeps equal keys in their original relative order.
- In-place:
- Uses only O(1) extra memory besides the input (recursion stack aside).
- Pass:
- One full sweep of the outer loop of a sort.
- Pointer sort:
- Sorting an array of pointers (or indices) to records instead of moving the records themselves.
- Linear search:
- Check each item in turn. O(n). Works on unsorted data.
- Binary search:
- Repeatedly halve a sorted range by comparing with the middle item. O(log n).
- Recursion:
- A function that calls itself on a smaller input, stopping at a base case.
- Base case:
- The input a recursive function answers directly, without recursing.
- Call stack:
- Memory where each active function call keeps its parameters and local variables.