Vous êtes sur la page 1sur 14

Les algorithmes de tri

1. Introduction Le tri est sans doute le problme fondamental de lalgorithmique 1. plus de 25% des CPU cycles sont dans les tri 2. le tri est fondamental beaucoup dautres problmes, par exemple recherche binaire. Ainsi donc, aprs le tri, beaucoup de problmes deviennent faciles rsoudre. Par exemple : 1. Unicit dlments: aprs le tri tester les lments adjacents 2. Une fois le tri fait, on peut dterminer le kme plus grand lment en O(1) Les problme de tri discuts dans ce chapitre sont ceux o lensemble des donnes trier se trouvent en mmoire centrale. Les problmes de tri dont les donnes sont en mmoire secondaire ne sont pas discuts dans ce chapitre. 2. Prsentation du problme Le tri consiste rarranger une permutation of n objets de telle manire

X 1 X 2 X 3 ... X n tri par ordre dcroissant X 1 X 2 X 3 ... X n tri par ordre croissant

Comment trier ? Il existe plusieurs solutions: 3. Tri par slection

Rpter 1. chercher le plus grand (le plus petit) lment 2. le mettre la fin (au dbut)

Exemple

Figure: tri par slection Implmentation

void selsort(Elem* array, int n) { for (int i=0; i<n-1; i++) { // Selectionner le ime element int lowindex = i; // mmoriser cet indice for (int j=n-1; j>i; j--) // trouver la plus petite valeur if (key(array[j]) < key(array[lowindex])) lowindex = j; // mettre jour lindex swap(array, i, lowindex); changer } }
Complexit : Le pire cas, le plus mauvais cas et le cas moyen sont pareils (pourquoi?)

Pour trouver le plus petit lments, (n-1) itrations sont ncessaires, pour le 2me plus petit lment, (n-2) itrations sont effectues, . Pour trouver le dernier plus petit lment, 0 itration sont effectues. Le nombre ditrations que lalgorithme effectue est donc:

Si par contre, nous prenons comme mesure dvaluations le nombre de mouvement de donnes, alors l,algorithme en effectue n-1, car il y a exactement un change par itration.
4. Tri par Insertion

Dans ce cas, itrativement, nous insrons le prochain lment dans la partie qui est dj trie prcdemment. La partie de dpart qui est trie est le premier lment.

En insrant un lment dans la partie trie, il se pourrait quon ait dplacer plusieurs autres. void inssort(Elem* array, int n) { for (int i=1; i<n; i++) // insrer le ime element for (int j=i; (j>0) && (key(array[j])<key(array[j-1])); j--) swap(array, j, j-1); }

Figure: tri par insertion

Complexit : Comme nous navons pas ncessairement scanner toute la partie dj trie, le pire cas, le meilleur cas et le cas moyen peuvent diffrer entre eux. Meilleur cas: Chaque lment est insr la fin de la partie trie. Dans ce cas, nous navons dplacer aucun lment. Comme nous avons insrer (n-1) lments, chacun gnrant seulement une comparaison, la complexit est en O(n) . Pire cas: Chaque lment est insr au dbut de la partie tri. Dans ce cas, tous les lments de la partie trie doivent tre dplacs chaque itration. La ime itration gnre (i-1) comparaisons et changes de valeurs:

Note: Cest le mme nombre de comparaison avec le tri par slection, mais effectue plus dchanges de valeurs. Si les valeurs changer sont importantes, ce nombre peut ralentir cet algorithme dune manire significative. Cas moyen : Si on se donne une permutation alatoire de nombre, la probabilit dinsrer llment la kme position parmi les positions (0,1,2, , i-1) est 1/i. Par consquent, le nombre moyen de comparaisons la ime itration est:

En sommant sur i, on obtient:

Tri par bulles.


La stratgie de cet algorithme est comme suit : 1. Parcourir le tableau en comparant deux deux les lments successifs, permuter s'ils ne sont pas dans l'ordre 2. Rpter tant que des permutation sont effectues.

Exemple

void bubsort(Elem* array, int n) { // Bubble Sort for (int i=0; i<n-1; i++) // changer for (int j=n-1; j>i; j--) if (key(array[j]) < key(array[j-1])) swap(array, j, j-1); }

5. Tri far fusion Cet algorithme divise en deux parties gales le tableau de donnes en question. Aprs que ces deux parties soient tries dune manire rcursive, elle sont fusionnes pour le tri de lensemble des donnes. Remarquez cette fusion doit tenir compte du fait que ces parties soient dj tries.

Implantation
void mergesort(Elem* array, Elem* temp, int left, int right) { int i, j, k, mid = (left+right)/2; if (left == right) return; mergesort(array, temp, left, mid); // la premire moiti mergesort(array, temp, mid+1, right);// Sort 2nd half // lopration de fusion. Premirement, copier les deux moitis dans temp. for (i=left; i<=mid; i++) temp[i] = array[i]; for (j=1; j<=right-mid; j++) temp[right-j+1] = array[j+mid]; // fusionner les deux moits dans array for (i=left,j=right,k=left; k<=right; k++) if (key(temp[i]) < key(temp[j])) array[k] = temp[i++]; else array[k] = temp[j--]; } 6

Complexit:
La complexit de cet algorithme est donne par la relation suivante:

n tant le nombre dlments dans le tableau.

Exercice: pourquoi les fonctions plancher et plafond comme paramtres dans T(.)? Question : Peut-on parler des trois diffrentes complexits pour cet algorithme?
Dans le but de simplifier la rsolution de lquation 15.10, nous supposons que pour un entier k 0 . En remplaant O(n) par n, on obtient (en principe, on doit la remplacer par cn):

La complexit temporelle de tri par fusion est donc en O(n log n) .


6. Le tri rapide

La stratgie de lalgorithme de tri rapide (quicksort) consiste, dans un premier temps, diviser le tableau en deux parties spares par un lment (appel pivot) de telle manire que les lments de la partie de gauche soient tous infrieurs ou gaux cet lment et ceux de la partie de droite soient tous suprieurs ce pivot (dans lalgorithme donn ci-dessus, la partie qui effectue cette tche est appele partition) . Ensuite , dune manire rcursive, ce procd est itr sur les deux parties ainsi cres. Notez que quau dpart, le pivot est choisi dans la version de ci-dessus, comme le dernier lment du tableau.

Implantation

void tri_rapide_bis(int tableau[],int debut,int fin){ if (debut<fin){ int pivot=partition(tableau,debut,fin); tri_rapide_bis(tableau,debut,pivot-1); tri_rapide_bis(tableau,pivot+1,fin); } } void tri_rapide(int tableau[],int n) { tri_rapide_bis(tableau,0,n-1); } void echanger(int tab[], int i, int j) { int memoire; memoire=tab[i]; tab[i]=tab[j]; tab[j]=memoire; } int partition(int tableau[], int deb, int fin){ int pivot = tableau[fin]; int i = debut ; j = fin; do{ do i++ while (tableau[i] < pivot) do j-while (tableau[j] > pivot) if (i < j) echanger (tableau,i,j) } while(i < j) tableau[deb] = a[j]; tableau[j] = pivot; return(j) }
Choix du pivot : Le choix idal serait que a coupe le tableau exactement en deux parties gales. Mais cela nest pas toujours possible. On peut prendre le premier lment. Mais il existe plusieurs autres stratgies! Partitionnement : on parcourt le tableau de gauche droite jusqu' rencontrer un lment suprieur au pivot

on parcourt le tableau de droite gauche jusqu' rencontrer un lment infrieur au pivot

on change ces deux lments

et on recommence les parcours gauche-droite et droite-gauche jusqu' a avoir :

il suffit alors de mettre le pivot la frontire (par un change)

Exemple

Complexit

lappel de QSORT (1,n), le pivot se place en position i. Ceci nous laisse avec un problme de tri de deux sous parties de taille i-1 et n-i.. Lalgorithme de partition clairement a une complexit au plus de cn pour une constante c.
T (n) = T (i 1) + T (n i ) + cn T (1) = 1 Voyons les trois cas possibles de complexit:

Cas dfavorable

Le pivot est chaque fois le plus petit lment. La relation de rcurrence devient T(n) = T(n 1) + cn (pourquoi ?) T(n -1) = T(n - 2) + c(n - 1)

10

T(n - 2) = T(n - 3) + c(n - 2) ... . T(2) = T(1) + c(2) En ajoutant membre membre, on obtient : T(n) = O( n 2 )

Cas favorable :

Dans le meilleur des cas, le pivot est, chaque fois, situ au milieu de la parti trier. T(n) = 2T(n/2) + cn Ce dveloppement sarrte ds quon atteint T(1). Autrement dit, ds que

n / 2k = 1
n = 2 k k = log n Solution finale: T(n) = O(n log n)

Question : dterminer la relation de rcurrence exacte de la complexit dans ce cas de figure? Cas moyen
Nous savons que T(n) = T(i-1) + T(n - i) + cn (voir plus haut). Supposons que toutes les permutations des lments du tableau soient quiprobables. Autrement dit, la probabilit que la case i soit choisie comme pivot est 1/n. Comme i peut varier entre 1 et n, alors on obtient facilement la rcurrence suivante:

t (n) = cn +

1 n t (i 1) + t (n i) n i =1

11

Comme

t (i 1) = t (n i) , on obtient la relation: t (n) = cn + n t (i) qui peut scrire aussi:


i =1 i =1 i =1

(1)

nt (n) = cn 2 + 2 t (i)
i =1

qui est aussi vraie pour n+1. Autrement dit, (2) (n + 1)t (n + 1) = c(n + 1) 2 + 2 t (i)
i =1 n +1

en soustrayant (1) de (2), on obtient:


(n + 1)t (n + 1) nt (n) = c(2n + 1) + 2t (n + 1) (n 1)t (n + 1) nt (n) = c(2n + 1) 2 1 (2n + 1) t (n + 1) t (n) = c( ) =c n(n 1) n 1 n n n 1 Cette quation est aussi vraie pour n, n-1, n-2, , 4

t (n) t (n 1) 2 1 ) = c( n 1 n 2 n 2 n 1
t (n 1) t (n 2) 2 1 = c( ) n2 n3 n3 n2 t (n 2) t (n 3) 2 1 = c( ) n3 n4 n4 n3 t (3) t (2) 2 1 = c( ) 2 1 1 2 En additionnant membre membre pour n, n-1,., on obtient aprs simplifications: t ( n ) t ( 2) 1 1 1 = c + + ... + 1 + c1 n 1 1 n2 n3 n 1 Or, on sait que :

12

log(n 2) < 1 + on obtient donc :

1 1 + ... + < log(n 1) 2 n2

1 t (n) < c(n 1) log(n 1) + 1 + t (2) n 1 t(2) tant une constante, on obtient : t (n) = O(n log n) .

7. Le tri de Shell
La stratgie de cette mthode consiste subdiviser subdiviser la liste cls trier en plusieurs sous-listes de telle manire que les lments de chacune de ces listes sont des positions distance fixe, appel incrment. Chacune de sous-listes est trie en utilisant lalgorithme de tri par insertion. Ensuite, un autre groupe de sous-liste est choisi avec un incrment plus petit que le prcdent. On rpte ce processus jusqu ce que lincrment soit gal 1. Par exemple, supposons que le nombre n dlments trier soit une puissance de 2. Si on choisit un incrment de dpart de n/2, ensuite de n/4, , jusqu 1, alors pour n =16, on aura 8 souslistes de 2 lments, spar de 8 positions dans le tableau; ensuite 4 sous-listes de 4 lments spars de 4 postions dans le tableau; ensuite 2 sous-listes de 8 lments spars 2 positions dans le tableau; ensuite une seule de 16 lments. Par exemple :

13

void shellsort(Elem* array, int n) { for (int i=n/2; i>2; i/=2) // pour chaque incrment for (int j=0; j<i; j++) // trier chque sous-liste inssort2(&array[j], n-j, i); inssort2(array, n, 1); } // Version de tri par insertion avec des incrments qui varient void inssort2(Elem* A, int n, int incr) { for (int i=incr; i<n; i+=incr) for (int j=i; (j>=incr) && (key(A[j])<key(A[j-incr])); j-=incr) swap(A, j, j-incr); } La complexit de cet algorithme est de O(n 3 / 2 )

Rsultats exprimentaux sur pentium III windows 98

14

Vous aimerez peut-être aussi