Python makes sorting so easy that it can hide the reasoning an interviewer wants to see. Calling sorted() is good production code, but it does not prove that you can maintain a binary-search invariant or explain why insertion sort becomes quadratic.
You need both skills. Implement the classics when the algorithm is the question. Use the built-ins when the result is the job.
The two jobs, kept separate
An implementation question tests whether you understand state and cost. You may need to write binary search, insertion sort, merge sort, or quick sort, then defend its time and space complexity.
Application code has a different goal. Python already gives you stable, well-tested sorting through sorted() and list.sort(). Its bisect module finds insertion positions in sorted lists. Reimplementing these tools without a specific reason usually adds bugs.
Python's built-in sorting algorithm is Timsort, a stable hybrid designed to exploit existing ordered runs. It has O(n log n) worst-case time and can approach O(n) when the data already contains useful order. Learning a classic sort explains the mechanics. Knowing Timsort explains the tool you actually call.
Binary search by hand
Binary search needs a sorted sequence. Keep an inclusive search window from lo to hi, probe its middle, and discard the half that cannot contain the target.
def binary_search(a, target):
lo, hi = 0, len(a) - 1
while lo <= hi:
mid = (lo + hi) // 2
if a[mid] == target:
return mid
if a[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1Trace it on the sorted array [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] with target 23:
lo = 0,hi = 9, somid = (0 + 9) // 2 = 4. The value16is below23, so setlo = 5.lo = 5,hi = 9, somid = (5 + 9) // 2 = 7. The value56is above23, so sethi = 6.lo = 5,hi = 6, somid = (5 + 6) // 2 = 5. The value is23, so return index5.
This target took three probes, five element comparisons in all. Since log2(10) is about 3.32, the small number of probes is consistent with logarithmic growth. The exact count depends on the target's position, but each failed probe still removes about half the remaining window.

Insertion sort by hand
Insertion sort treats the left part of the array as sorted. At each step, it stores the next value as the key, shifts larger values one position right, and inserts the key into the gap.
Start with [5, 2, 4, 1]:
At
i = 1, key2is less than5. Shift5and insert2:[2, 5, 4, 1].At
i = 2, key4is less than5but greater than2. Shift5and insert4:[2, 4, 5, 1].At
i = 3, key1is less than5,4, and2. Shift all three and insert at the front:[1, 2, 4, 5].
The final array is [1, 2, 4, 5]. In the worst case, such as reverse-sorted input, the nested comparisons and shifts take O(n^2) time. Already-sorted input needs one comparison per key and no shifts, so the best case is O(n). That behaviour on small, ordered runs is one reason insertion-sort ideas are useful inside hybrid algorithms.
Using Python's built-ins well
sorted(data) returns a new list and leaves data unchanged. data.sort() changes the list in place and returns None. Therefore, data = data.sort() destroys your reference by assigning None.
The key argument expresses the order directly:
words = ["pear", "fig", "banana"]
by_length = sorted(words, key=len)
rows = [("Sales", 50000), ("Tech", 70000), ("Sales", 65000)]
ordered = sorted(rows, key=lambda row: (row[0], -row[1]))Timsort is stable, so items with equal keys keep their original relative order. That matters when records were already sorted by a secondary field.
For a sorted list, bisect.bisect_left(a, x) finds the leftmost valid insertion index in O(log n) comparisons. bisect.insort(a, x) finds that position and inserts the item. The search is logarithmic, but shifting list elements makes the complete insertion O(n). That distinction is a common interview trap.
The complexity table you can defend
Algorithm | Best time | Average time | Worst time | Stable | Extra space |
|---|---|---|---|---|---|
Bubble sort |
|
|
| Yes |
|
Selection sort |
|
|
| No |
|
Insertion sort |
|
|
| Yes |
|
Merge sort |
|
|
| Yes |
|
Quick sort |
|
|
| Usually no | typically |
Python Timsort |
|
|
| Yes | up to |
Linear search takes O(n) and works on any list. Binary search takes O(log n) after the data is sorted. If you must sort only to perform one search, the total job is still dominated by the O(n log n) sort.
For a broader comparison, study Sorting Algorithms Compared: complexity, stability, and the n log n lower bound.
Traps that cost the mark
Binary search on unsorted data has no valid direction rule. Sortedness is a precondition.
A naive first or last pivot can push quick sort to
O(n^2)on ordered input. Randomised or better pivot selection reduces this risk.Stability matters when equal-key records must retain an earlier order.
list.sort()returnsNone, not the sorted list.Pick one boundary convention. Mixing inclusive
hi = len(a) - 1with exclusivehi = len(a)creates off-by-one bugs.Python integers do not overflow at the midpoint calculation. In fixed-width languages,
lo + (hi - lo) / 2avoids overflow.
Container choice drives several of these costs. Python Data Structures: Lists, Tuples, Sets, Dictionaries explains why inserting into a list shifts elements while set and dictionary lookup stays constant on average.
How interviews and tests frame this
"Implement binary search" asks for code, an invariant, and complexity. "Sort records by department and descending salary" asks whether you can use key cleanly. "Insert while keeping the list sorted" points towards bisect, with the warning that list insertion still shifts elements.
Rotated-array search and first-or-last occurrence problems test whether you understand the binary-search decision, not whether you memorised one loop. KnowledgeGate's question bank has over 1,000 algorithms questions, including sorting and searching, for this kind of practice. The Algorithm module in Coding For Placements sets these problems beside the wider programming toolkit.
The short version and your next step
Know binary search and one O(n log n) sort well enough to implement and explain them. In normal Python code, prefer sorted(), list.sort(), and bisect because their behaviour is tested and their intent is clear. Always check the sortedness precondition and include the cost of any setup work in your analysis.
Next, implement binary search and merge sort inside DSA using Python, then compare your code with the built-in approach on sorted, reverse-sorted, and duplicate-heavy inputs.




