Compare bubble sort with Python's built-in sort, counting the swaps needed to put a small list in order.
Bubble sort repeatedly walks the list, swapping neighbours that are out of order, and shrinks the range it scans after each pass. On a list of seven elements it needed twelve swaps to reach sorted order, which is quadratic work for a linear-size input.
def bubble_sort(values):
data = list(values)
swaps = 0
for end in range(len(data) - 1, 0, -1):
swapped = False
for i in range(end):
if data[i] > data[i + 1]:
data[i], data[i + 1] = data[i + 1], data[i]
swaps += 1
swapped = True
if not swapped:
break
return data, swaps
sample = [5, 1, 4, 2, 8, 0, 3]
ordered, swaps = bubble_sort(sample)
print(ordered)
print(swaps)
print(sorted(sample))All three lines agree on the answer, 0 through 8 in order, but only the second tells you how hard the work was. The early-exit flag is the reason bubble sort is sometimes forgiven: on already sorted data the first pass finds no swap and stops, so it is effectively linear. That is why it is still used as a detection routine for nearly sorted input, never as a general sort.
Python's built-in sort uses Timsort, which detects runs of already ordered data and merges them. On random data it behaves like a merge sort with a guaranteed O of n log n bound and O of n extra space, and on nearly sorted data it is close to linear because the runs it finds are long. The built-in also runs in C, so the constant factor is far smaller than anything you would write in Python.
The trade-off to mention is that the built-in sort is stable, meaning equal elements keep their original relative order, and quicksort is not. If you sort records by one key and need ties to keep another key's order, stability saves you an extra pass. As a general rule, use the library sort unless you have a reason not to, and when you do have a reason, name it: stability, memory limits, or a huge key range.