$ \def\compare{ {\mathrm{compare}} } \def\swap{ {\mathrm{swap}} } \def\sort{ {\mathrm{sort}} } \def\insert{ {\mathrm{insert}} } \def\true{ {\mathrm{true}} } \def\false{ {\mathrm{false}} } \def\BubbleSort{ {\mathrm{BubbleSort}} } \def\SelectionSort{ {\mathrm{SelectionSort}} } \def\Merge{ {\mathrm{Merge}} } \def\MergeSort{ {\mathrm{MergeSort}} } $
Accountability Groups
Sorting meets Divide & Conquer
MergeSort: Divide by Index
# sort values of a between indices i and j-1
MergeSort(a, i, j):
if j - i = 1 then
return
endif
m <- (i + j) / 2
MergeSort(a,i,m)
MergeSort(a,m,j)
Merge(a,i,m,j)
Establish two claims:
Claim 1 (merge). If $a[i..m-1]$ and $a[m..j]$ are sorted, then after $\Merge(a, i, m, j)$, $a[i..j]$ is sorted.
Claim 2. For any indices $i < j$, after calling $\MergeSort(a, i, j)$, $a[i..j]$ is sorted.
00 # sort values of a between indices i and j-1
01 MergeSort(a, i, j):
02 if j - i = 1 then
03 return
04 endif
05 m <- (i + j) / 2
06 MergeSort(a,i,m)
07 MergeSort(a,m,j)
08 Merge(a,i,m,j)
Consider $\MergeSort(a, i, j)$, define $k = j - i$ to be size
$P(k)$: for every $k’ \leq k$, $\MergeSort(a, i, j)$ with size $k’$ succeeds
Base case $k = 1$:
Inductive step $P(k) \implies P(k+1)$:
How efficient is MergeSort?
00 # sort values of a between indices i and j-1
01 MergeSort(a, i, j):
02 if j - i = 1 then
03 return
04 endif
05 m <- (i + j) / 2
06 MergeSort(a,i,m)
07 MergeSort(a,m,j)
08 Merge(a,i,m,j)
00 # sort values of a between indices i and j-1
01 MergeSort(a, i, j):
02 if j - i = 1 then
03 return
04 endif
05 m <- (i + j) / 2
06 MergeSort(a,i,m)
07 MergeSort(a,m,j)
08 Merge(a,i,m,j)
Observation 1. Let $k = j - i$ be the size of the method call $\MergeSort(a, i, j)$. Then running time is $O(k) + $ running time of recursive calls on lines 6-7.
Observation 2. Recursive calls have size $k / 2$.
Define $\log$ by
Another way
Facts.
Running time $T(n)$ of $\MergeSort(a, 1, n+1)$:
\[\begin{align*} T(n) &= 2 T(n/2) + O(n)\\ &= 4 T(\frac n 4) + 2 O(\frac n 2) + O(n)\\ &= 8 T(\frac n 8) + 4 O(\frac n 4) + 2 O(\frac n 2) + O(n)\\ &\vdots\\ &= n T(1) + \frac n 2 O(2) + \cdots + 8 O(\frac n 8) + 4 O(\frac n 4) + 2 O(\frac n 2) + O(n)\\ &= O(n) + O(n) + \cdots + O(n) + O(n) + O(n)\\ \end{align*}\]SelectionSort. $O(n^2)$ operations
BubbleSort and InsertionSort. $O(n^2)$ operations
MergeSort. $O(n \log n)$ operations
Idea. Divide array $a$ by value
QuickSort(a, i, j):
if j - i <= 1 then
return
endif
p <- GetPivot(a, i, j) # select a pivot
k <- Split(a, i, j, p)
QuickSort(a, i, k-1)
QuickSort(a, k+1, j)
GetPivot(a, i, j):
k <- RandomInt(i, j)
return a[k]
What is a “good” pivot choice?
How likely is a good pivot to be chosen?
Can show. If random pivot is chosen, then on average QuickSort uses $O(n \log n)$ operations