Repeatedly compare adjacent elements and swap if out of order.
Largest element at last index after first pass.
Comparisons + Worst case Swaps = 2[(N – 1) + … + 1] = N² – N
O(N²), quadratic time.
Best case O(N)

Naive:
def bubble_sort(arr):
# Outer loop to iterate through the list n times
for n in range(len(arr) - 1, 0, -1):
# Inner loop to compare adjacent elements
for i in range(n):
if arr[i] > arr[i + 1]:
# Swap elements if they are in the wrong order
arr[i], arr[i + 1] = arr[i + 1], arr[i]
Break if already sorted:
def bubble_sort(arr):
# Outer loop to iterate through the list n times
for n in range(len(arr) - 1, 0, -1):
# Initialize swapped to track if any swaps occur
swapped = False
# Inner loop to compare adjacent elements
for i in range(n):
if arr[i] > arr[i + 1]:
# Swap elements if they are in the wrong order
arr[i], arr[i + 1] = arr[i + 1], arr[i]
# Mark that a swap has occurred
swapped = True
# If no swaps occurred, the list is already sorted
if not swapped:
break
Optimise number of swaps:
def optimized_bubble_sort(arr):
n = len(arr)
while n > 1:
last_swap = 0 # Track the last index where a swap occurs
for i in range(1, n):
if arr[i - 1] > arr[i]:
arr[i - 1], arr[i] = arr[i], arr[i - 1] # swap
last_swap = i # update last swap position
n = last_swap # shrink the boundary
return arr