Bubble Sort in Python: Algorithm, Code, and Complexity

Quick answer: Bubble sort repeatedly compares adjacent values and swaps strictly out-of-order pairs. A swapped flag can stop after an already sorted pass, giving O(n) best-case behavior, but the algorithm remains O(n²) in typical and worst cases and sorted() is usually the production choice.

Python Pool infographic showing bubble sort passes, adjacent comparisons, swaps, and early exit
Bubble sort repeatedly compares adjacent values; a swapped flag can stop an already sorted pass, but Python’s sorted() is usually the production choice.

Bubble sort in Python is a small sorting algorithm that compares neighboring values and swaps them when they are in the wrong order. It is easy to read because every pass pushes the largest remaining value toward the end of the list, similar to a value bubbling into its final position.

You should learn bubble sort to understand comparisons, swaps, nested loops, and algorithmic complexity. For real application code, Python’s built-in sorting tools are faster and more reliable, so use list.sort() or sorted() unless you are studying algorithms or writing a teaching example.

How bubble sort works

Bubble sort repeatedly scans the list from left to right. During each scan, it compares two adjacent values. If the value on the left is greater than the value on the right, the algorithm swaps them. After the first full pass, the largest value is at the end. After the second pass, the second largest value is also in the correct area, so the inner loop can stop one step earlier.

Start: 7 5 9 6 3
Compare 7 and 5 -> swap: 5 7 9 6 3
Compare 7 and 9 -> keep: 5 7 9 6 3
Compare 9 and 6 -> swap: 5 7 6 9 3
Compare 9 and 3 -> swap: 5 7 6 3 9

At this point, 9 is already fixed at the end. The next pass only needs to work with the unsorted part: 5 7 6 3.

Bubble sort Python code

This implementation returns a new sorted list instead of changing the input list in place. That makes the function easier to test and safer to use in examples where the original values should remain available.

<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">def bubble_sort(values):
    items = list(values)
    n = len(items)

    for end in range(n - 1, 0, -1):
        swapped = False

        for i in range(end):
            if items[i] > items[i + 1]:
                items[i], items[i + 1] = items[i + 1], items[i]
                swapped = True

        if not swapped:
            break

    return items


numbers = [7, 5, 9, 6, 3]
print(bubble_sort(numbers))</div>
[3, 5, 6, 7, 9]

The outer loop uses range() to move the right boundary of the unsorted portion. The inner loop compares each pair up to that boundary. The swapped flag is an early-exit optimization: if one full pass makes no swaps, the list is already sorted and the function can stop.

In-place bubble sort

If you want the function to mutate the same list object, remove the copy and sort the list directly. This mirrors how list.sort() behaves, though the built-in method is still the better choice for production sorting.

<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">def bubble_sort_in_place(items):
    for end in range(len(items) - 1, 0, -1):
        swapped = False
        for i in range(end):
            if items[i] > items[i + 1]:
                items[i], items[i + 1] = items[i + 1], items[i]
                swapped = True
        if not swapped:
            return


scores = [88, 75, 92, 75]
bubble_sort_in_place(scores)
print(scores)</div>
Python Pool infographic showing an unsorted list, adjacent values, comparisons, and bubble sort
Unsorted list: An unsorted list, adjacent values, comparisons, and bubble sort.

Bubble sort complexity

Bubble sort has O(n²) average and worst-case time complexity because nested loops compare many pairs as the input grows. With the early-exit check, the best case is O(n) when the list is already sorted. The space complexity is O(1) for the in-place version because swaps reuse the existing list storage.

Bubble sort is stable when it swaps only on >, not on >=. Equal values keep their original relative order. Stability is useful when records have the same sort key, such as two students with the same score.

When should you use bubble sort?

Use bubble sort when you are learning how sorting algorithms work, demonstrating loop boundaries, or debugging a tiny educational example. Do not use it for large lists, data pipelines, web requests, or performance-sensitive code. Python’s built-in sort is based on Timsort and is designed for real workloads; see our Timsort guide and list sorting examples for practical alternatives.

Python Pool infographic mapping one bubble-sort pass through adjacent comparisons and largest value
Sorting pass: One bubble-sort pass through adjacent comparisons and largest value.

Common mistakes

  • Looping too far and reading items[i + 1] past the end of the list.
  • Forgetting to shrink the inner loop after each pass.
  • Using >= and accidentally making the algorithm unstable for equal values.
  • Expecting bubble sort to compete with sorted() on large inputs.
  • Changing the input list when the caller expects a new sorted copy.

How to test a bubble sort function

Small sorting functions are easy to get almost right, so test more than one input shape. A good bubble sort test should include an unsorted list, an already sorted list, an empty list, and repeated values. Repeated values matter because they reveal whether the function keeps equal items in a predictable order.

<div class="pythonpool-code-scroll" style="max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;">assert bubble_sort([7, 5, 9, 6, 3]) == [3, 5, 6, 7, 9]
assert bubble_sort([1, 2, 3]) == [1, 2, 3]
assert bubble_sort([]) == []
assert bubble_sort([4, 4, 1]) == [1, 4, 4]</div>

Also test the original input if your function promises to return a new list. If callers pass a tuple or another iterable, converting with list(values) makes the implementation flexible while keeping the result as a normal Python list.

Ascending and descending order

The examples above sort in ascending order because they swap when the left value is greater than the right value. For descending order, reverse the comparison and swap when the left value is smaller. Keep the rest of the loop boundaries the same. In normal Python code, however, prefer sorted(values, reverse=True) for descending results because it is shorter, faster, and easier for other developers to recognize.

Python Pool infographic comparing ordered and inverted neighbors, swaps, and early stopping
Swap neighbors: Ordered and inverted neighbors, swaps, and early stopping.

Related Python guides

Official references

Implement The Basic Passes

At the end of each pass, the largest remaining value has bubbled to the right boundary. The inner range can therefore shrink after every pass. Use a temporary value or tuple unpacking to exchange adjacent elements without losing either value.

def bubble_sort(values):
    items = list(values)
    for end in range(len(items) - 1, 0, -1):
        for index in range(end):
            if items[index] > items[index + 1]:
                items[index], items[index + 1] = items[index + 1], items[index]
    return items

print(bubble_sort([5, 1, 4, 2, 8]))
Python Pool infographic testing stability, worst case, best case, in-place behavior, and validation
Sort checks: Stability, worst case, best case, in-place behavior, and validation.

Stop When A Pass Makes No Swaps

If one complete pass does not swap any pair, the list is already sorted and later passes cannot change it. This optimization makes the best case linear, but it does not improve the quadratic behavior of a reverse-sorted or otherwise difficult input.

def bubble_sort_early_exit(values):
    items = list(values)
    for end in range(len(items) - 1, 0, -1):
        swapped = False
        for index in range(end):
            if items[index] > items[index + 1]:
                items[index], items[index + 1] = items[index + 1], items[index]
                swapped = True
        if not swapped:
            break
    return items

print(bubble_sort_early_exit([1, 2, 3, 4]))

Preserve Stability And Input Policy

Swapping only when the left value is greater than the right value keeps equal values in their original order, which makes the usual implementation stable. The function above returns a new list; mutate the input only if that behavior is deliberately part of the API.

records = [("first", 2), ("second", 2), ("low", 1)]
print(bubble_sort(records))

Compare With Python’s Sort

Use sorted(iterable) when you need a new sorted list and list.sort() when in-place mutation is appropriate. Python’s built-in sorting is stable and highly optimized. Bubble sort is valuable for teaching adjacent swaps, pass invariants, and complexity, not as a default replacement for the standard library.

values = [5, 1, 4, 2, 8]
print(sorted(values))
values.sort()
print(values)

Python’s official Sorting Techniques guide covers stable sorting, key functions, and the production sorting tools.

For related sorting algorithms, compare counting sort, pigeonhole sort, and heapq selection before choosing an implementation for the input range.

Frequently Asked Questions

What is bubble sort in Python?

Bubble sort repeatedly compares adjacent values and swaps them when they are out of order until a pass makes no swaps.

What is the time complexity of bubble sort?

Bubble sort is O(n²) in average and worst cases; an early-exit check gives O(n) best-case behavior for an already sorted list.

Is bubble sort stable?

The usual adjacent-swap implementation is stable when it swaps only strictly out-of-order pairs, so equal values keep their relative order.

Should I use bubble sort in production Python?

Usually no. Use sorted() or list.sort() for general-purpose code; bubble sort is mainly useful for learning or a narrowly constrained small input.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted