Understanding Bubble Sort in Python with Practical Examples

πŸ’‘ Problem Formulation: This article tackles the concept of sorting a list of numbers in Python using the bubble sort algorithm. Bubble sort is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted. For instance, given an input list [34, 10, 64, 51], the desired output after applying bubble sort would be [10, 34, 51, 64].

Method 1: Implementing Bubble Sort Using Nested Loops

The traditional implementation of bubble sort involves using two nested loops: the outer loop ensures we go through the list as many times as there are elements, while the inner loop performs the comparisons and swaps. This method ensures that after each pass, the largest element ‘bubbles up’ to its correct position at the end of the list.

Here’s an example:

def bubble_sort(lst):
    n = len(lst)
    for i in range(n):
        for j in range(0, n-i-1):
            if lst[j] > lst[j+1]:
                lst[j], lst[j+1] = lst[j+1], lst[j]
                
my_list = [34, 10, 64, 51]
bubble_sort(my_list)
print(my_list)

Output:

[10, 34, 51, 64]

This code defines a function bubble_sort that takes a list and sorts it in place. It compares each pair of adjacent items and swaps them if they are in the incorrect order, which continues until the entire list is sorted. The list my_list is passed to the function and sorted, and the sorted list is then printed out.

Method 2: Optimized Bubble Sort with a Flag

The optimized version of bubble sort introduces a flag to detect if a swap has been made during the inner loop iteration. If no swap has occurred, the list is already sorted, and there’s no need for further passes, cutting down on unnecessary comparisons and improving efficiency.

Here’s an example:

def optimized_bubble_sort(lst):
    n = len(lst)
    for i in range(n):
        swapped = False
        for j in range(0, n-i-1):
            if lst[j] > lst[j+1]:
                lst[j], lst[j+1] = lst[j+1], lst[j]
                swapped = True
        if not swapped:
            break

my_list = [34, 10, 64, 51]
optimized_bubble_sort(my_list)
print(my_list)

Output:

[10, 34, 51, 64]

The function optimized_bubble_sort introduces a boolean variable swapped to track if any swap has been made in a particular pass. If no elements were swapped, the algorithm stops as the list is considered sorted. This optimized version reduces the number of operations required when sorting a list that may already be partially sorted.

Method 3: Bubble Sort Using While Loop

Instead of using nested ‘for’ loops, bubble sort can be implemented with a ‘while’ loop and a counter. The ‘while’ loop continues to run as long as the counter indicates that a pass through the list is needed, which is determined by the number of swaps made during the previous pass.

Here’s an example:

def bubble_sort_while(lst):
    n = len(lst)
    swapped = True
    while swapped:
        swapped = False
        for i in range(1, n):
            if lst[i-1] > lst[i]:
                lst[i-1], lst[i] = lst[i], lst[i-1]             # Swap the elements
                swapped = True
        n -= 1
                
my_list = [34, 10, 64, 51]
bubble_sort_while(my_list)
print(my_list)

Output:

[10, 34, 51, 64]

In this snippet, the bubble_sort_while function uses a ‘while’ loop to repeatedly pass through the list until no more swaps are required. Decrementing n at the end of each pass ensures the last sorted elements are not compared again, improving performance over multiple iterations.

Method 4: Using Recursion for Bubble Sort

Bubble sort can also be implemented recursively. In this approach, a recursive function is used to perform the passes through the list, calling itself with a decreased list size each time until the entire list is sorted.

Here’s an example:

def bubble_sort_recursive(lst, n=None):
    if n is None:
        n = len(lst)
    if n > 1:
        for i in range(n - 1):
            if lst[i] > lst[i + 1]:
                lst[i], lst[i + 1] = lst[i + 1], lst[i]
        bubble_sort_recursive(lst, n - 1)

my_list = [34, 10, 64, 51]
bubble_sort_recursive(my_list)
print(my_list)

Output:

[10, 34, 51, 64]

This code snippet shows the function bubble_sort_recursive, which performs bubble sort by recursively calling itself, decreasing the size of the sorting area by 1 with each call. The function continues until the base case of a single element list is reached, which is naturally sorted.

Bonus One-Liner Method 5: Bubble Sort Using List Comprehensions

Although not a conventional implementation, for those who enjoy Python’s list comprehensions, bubble sort can be executed in a ‘Pythonic’ one-liner by nesting list comprehensions inside a single loop that runs for the length of the list.

Here’s an example:

my_list = [34, 10, 64, 51]
[my_list := [my_list[j+1] if my_list[j] > my_list[j+1] else my_list[j] for j in range(i)] + my_list[i:] for i in range(len(my_list), 0, -1)]
print(my_list)

Output:

[10, 34, 51, 64]

This intriguing one-liner utilizes the walrus operator := introduced in Python 3.8 to perform the swaps within a list comprehension. It iteratively shortens the processing range by slicing the list, effectively bubbling up the largest element each iteration. It’s a clever yet complex way to implement bubble sort in a single line of code.

Summary/Discussion

  • Method 1: Traditional Bubble Sort. Easy to understand and implement. It’s the classic educational example but is inefficient for large lists due to its O(nΒ²) time complexity.
  • Method 2: Optimized Bubble Sort. More efficient than the traditional method as it can potentially finish early. However, it still retains suboptimal performance for large, nearly sorted lists.
  • Method 3: While Loop Implementation. An alternative loop style that can be clearer to some developers. It provides similar optimizations to Method 2, maintaining the average performance.
  • Method 4: Recursive Bubble Sort. Demonstrates the principle of recursion. This is more of a conceptual curiosity than a practical method due to Python’s recursion limit and the larger overhead of function calls.
  • Bonus Method 5: One-Liner. A Pythonic trick demonstrating advanced comprehension and assignment expressions. While clever, it sacrifices readability and is not recommended for production code.