Quicksort time complexity

The pseudocode of quicksort1:

function quicksort(arr) {
if (arr.length < 2) return arr;
const [pivot, ...rest] = arr;
const [less, greater] = partition(rest, pivot);
return [...quicksort(less), pivot, ...quicksort(greater)];
}
js

Let the time taken by quicksort() be T(n)T(n) where nn = arr.length.

T(n)=cn+k=0n1(P(K=k)(T(k)+T(nk1)))=cn+1nk=0n1(T(k)+T(nk1))=cn+2nk=0n1T(k)\begin{aligned} T(n) &= c\cdot n + \sum_{k = 0}^{n - 1}\left(P(K = k)\cdot (T(k) + T(n-k-1))\right)\\ &= c\cdot n + \frac{1}{n}\sum_{k = 0}^{n - 1}\left(T(k) + T(n-k-1)\right)\\ &= c\cdot n + \frac{2}{n}\sum_{k = 0}^{n - 1}T(k)\\ \end{aligned}

Substitute n=n1n = n - 1.

T(n1)=c(n1)+2n1k=0n2T(k)n1nT(n1)=c(n1)2n+2nk=0n2T(k)T(n)n1nT(n1)=c2n1n+2nk=0n1T(k)2nk=0n2T(k)\begin{aligned} T(n - 1) &= c\cdot (n - 1) + \frac{2}{n - 1}\sum_{k = 0}^{n - 2}T(k)\\ \frac{n - 1}{n}\cdot T(n - 1) &= c\cdot\frac{(n - 1)^2}{n} + \frac{2}{n}\sum_{k = 0}^{n - 2}T(k)\\ T(n) - \frac{n - 1}{n}\cdot T(n - 1) &= c\cdot \frac{2n - 1}{n} + \frac{2}{n}\sum_{k = 0}^{n - 1}T(k) - \frac{2}{n}\sum_{k = 0}^{n - 2}T(k)\\ \end{aligned}

Now we solve for the upper bound.

T(n)=n1nT(n1)+2nT(n1)+c2n1nn+1n(T(n1)+2c)=n+1nT(n1)+c2n+2nn+1nnn1(T(n2)+2c)+c2n+2n=n+1n1T(n2)+c(2n+2n1+2n+2n)(n+1)T(0)+c(2n+2)i=1n1in(n+1)O(1)+c(2n+2)lnn=O(nlnn)\begin{aligned} T(n) &= \frac{n - 1}{n}\cdot T(n - 1) + \frac{2}{n}\cdot T(n - 1) + c\cdot \frac{2n - 1}{n}\\ &\leq \frac{n + 1}{n}\cdot (T(n - 1) + 2c)\\ &= \frac{n + 1}{n}\cdot T(n - 1) + c\cdot\frac{2n + 2}{n}\\ &\leq \frac{n + 1}{n}\cdot \frac{n}{n - 1}\cdot (T(n - 2) + 2c) + c\cdot\frac{2n + 2}{n}\\ &= \frac{n + 1}{n - 1}\cdot T(n - 2) + c\cdot\left(\frac{2n + 2}{n - 1} + \frac{2n + 2}{n}\right)\\ &\leq \dots\\ &\leq (n + 1)\cdot T(0) + c\cdot (2n + 2)\sum_{i = 1}^n\frac{1}{i}\\ &\xrightarrow{n\to\infty} (n + 1)\cdot \mathcal{O}(1) + c\cdot (2n + 2)\ln n\\ &= \boxed{\mathcal{O}(n\ln n)} \end{aligned}

Footnotes

  1. It's pseudocode because you don't implement quicksort like this. Real quicksort is in-place because copying like ...rest is actually O(n)\mathcal{O}(n). Still, this is a correct (and O(nlnn)\mathcal{O}(n\ln n)) implementation.