worst-case running time of Theta(n^2)
pas le meilleur dans le plus mauvais cas, mais cas moyen en Theta(n lg n) avec des constantes faibles. Et tri sur place. Et bon avec environment virtuels.
approche divide-and-conquer. Pour trier A[p..r]
- diviser : rearrange en 2 sous tableaux A[p..q-1] et A[q+1..r], avec chaque element: A[p..q-1] <= A[q] <= A[q-1..r]
- conquérir : trier les sous tableau reursivement
- combiner: rien a faire
Algo:
QUICKSORT(A,p,r): if p < r q = PARTITION(A,p,r) QUICKSORT(A,p,q-1) QUICKSORT(A,q+1,r)
Pour tout trier : QUICKSORT(A,1,A.length)
PARTITION(A,p,r) x = A[r] i = p-1 for j = p to r-1 if A[j] <= x i = i + 1 swap A[i] et A[j] swap A[i+1] et A[r] return i+1
Analyse :
- x=A[r] comme pivot
- regroupe les elements inferieurs a x dans A[p,i]
- regroupe les elements superieur a x dans A[i+1,j]
Invariants de boucle :
- si p <= k <= i (sous tableau de gauche), alors A[k] <= x
- si i+1 <= k <= j-1 (sous tableau de droite), alors A[k] > x
- si k = r (pivot), alors A[k] = x
Preuve :
- initialisation : les sous tableaux sont vides, donc les invariants 1 et 2 sont vrais. Et l'initialisation du pivot fait que l'invariant 3 est vrai.
- maintenance : 2 cas, si A[j] est sup ou inf au pivot.
- si A[j] > x, alors j=j+1. Les invariants restent valides.
- si A[j] <= x, alors echange + incremantation de i et j. Invariants encore valides.
- terminaison : a la fin, j=r.
Running time de PARTITION : Theta(n)
proche de merge sort si partitionnement équilibré, proche de insertion sort sinon.
Pire cas lorsque un des sous tableaux contient 0 elements. (prouvé dans 7.4.1)
T(n) = T(n-1) + T(0) + Theta(n) // sous-tableau de taille n-1, puis sous-table de taille 0, puis merge T(n) = T(n-1) + Theta(n) T(n) = Theta(n^2)
Pas meilleur que insertion sort
Equilibré = 2 sous problème de tailles n/2 et (n/2)-1
T(n) = 2T(n/2) + Theta(n) T(n) = Theta(n lg n)
similaire merge sort
Meme si mal equilibré, plus proche du meilleur cas que du plus mauvais cas. (Prouvé dans 7.4)
Par exemple, avec partition 9/10 et 1/10 :
T(n) = T(9n/10) + T(n/10) + c.n
- log_10(n) = Theta(lg n)
- log_10/9(n) = Theta(lg n)
Donc au final : O(n lg n). Et plus generalement pour toute partition de taille constante.
Alternative entre bons et mauvais parttion fait que cela s'equilibre et pas trop mauvais au final.
En situation reelle, toutes les permutations ne sont pas forcement equivalentes.
Randomisation des entrées comme dans 5.3 possible. Mais plus simple a analyser : random sampling : choix du pivot aleatoire dans A[p..r] et echange avec A[r] au début.
RANDOMIZED-PARTITION(A,p,r): i = RANDOM(p,r) swap A[r] et A[i] return PARTITION(a,p,r)
RANDOMIZED-QUICKSORT(A,p,r): if p < r q = RANDOMIZED-PARTITION(A,p,r) RANDOMIZED-QUICKSORT(A,p,q-1) RANDOMIZED-QUICKSORT(A,q+1,r)
montrer que le cas où la partition est 0 vs n-1 est le pire cas.
T(n) = max (T(q) + T(n-q-1)) + Theta(n) | pour 0 <= q <= n-1
On a au pire : T(n) <= c.n^2, puis faire la substitution.
Lemma 7.1
Let X be the number of comparisons performed in line 4 of PARTITION over the entire execution of QUICKSORT on an n-element array. Then the running time of QUICKSORT is O.n C X/.