Here is Bubble Sort again:
def bubble_sort(L):
for i in range(len(L)):
#Set swapped to False. If nothing changes during a pass, we break.
#If that happens, the runtime is better than the worst-case runtime
#for a list of size n
swapped = False
#in the worst case, we need n-1 passes
#The worst case happens when the smallest element is at L[-1], since
#the smallest element only shifts one position to the left with each pass
for j in range(len(L)-1-i):
if L[j] > L[j+1]:
L[j], L[j+1] = L[j+1], L[j]
swapped = True
if not swapped:
break
Unlike with Selection Sort, Bubble Sort can terminate early -- if we break because a sweep didn't result in any two elements being swapped, the function returns faster.
We know that Bubble Sort will not run for more than n sweeps (where n = len(L)), just because the outer loop will not run for more than n iterations. (Recall that in the previous lecture we argued that Bubble Sort only ever needs at most n-1 sweeps.
Is it ever the case that Bubble Sort needs all n-1 sweeps to sort the list? Yes.
Note what happens when the smallest element of the list is initially the last element of the list:
def bubble_sort(L):
for i in range(len(L)):
swapped = False
for j in range(len(L)-1-i):
if L[j] > L[j+1]:
L[j], L[j+1] = L[j+1], L[j]
print("Swapped", L[j], "and", L[j+1])
print(L)
swapped = True
else:
print("No need to swap", L[j], "and", L[j+1])
print(L)
if not swapped:
return
print("====================================")
if __name__ == '__main__':
L = [2, 3, 4, 5, 1]
bubble_sort(L)
Note that the 1 only moves left once per sweep. That makes sense: any element can only move left once during the sweep (but an element can move to the right many times.)
We can therefore conclude that the in the worst case, Bubble Sort does not return before performing all n iterations of the outer loop.
Let's now figure out the worst-case runtime complexity of Bubble Sort for a list of length n by counting how many times the inner block repeats.
At iteration 0, the block runs for n-1-0 times
At iteration 1, the block runs for n-1-1 times
At iteration n-1, the block runs for n-1-(n-1)=0 times
So in total, the block runs $\sum_{i=0}^{n-1} (n-i-1)$ times.
$\sum_{i=0}^{n-1} (n-i-1) = n^2 -\sum_{i=0}^{n-1} i - n = n^2-n(n-1)/2-n = n^2- n^2 / 2 + n/2 - n= n^2 /2 - n/2$
(To compute $\sum_{i=0}^{n-1} i$, we use the fact that $\sum_{j=1}^{m} j = m(m+1)/2$, and substitute $m = n-1$.)
We can therefore conclude that the worst-case runtime complexity of Bubble Sort if $\mathcal{O}(n^2)$, just like Selection Sort.