Vous êtes sur la page 1sur 6

LSb1.

3 - Algorithmique et structures de données



TD3 - Tri de tableau

Guillaume Revy

Séances du 18 et 19 décembre 2008

1 Présentation des algorithmes de tri


Tous les codes sont présentés en fin de corrigé.

Exercice 1. Écrire un programme C/C++ qui lit une série d’entiers, les stocke dans un tableau que l’on
agrandit au fur et à mesure, et qui appelle une fonction qui réalise un tri par extraction (tri de base).
Déterminer la complexité.

frontière élément minimal

partie triée

échange

Opérations de base du tri par extraction.

1. recherche de l’élément minimal dans la partie non triée


2. échange entre l’élément minimal et l’élément frontière
3. déplacement de la frontière

Complexité.
Pour un tableau de taille n, on effectue n parcours de tableau. Pour chaque parcours i, on
effectue n − i traitement(s). On effectue

n(n + 1)
n + (n − 1) + (n − 2) + · · · + 1 = traitements.
2
On a donc une complexité en O(n2 ).

Exercice 2. Écrire un programme C/C++ qui lit une série d’entiers, les stockes dans un tableau que
l’on agrandit au fur et à mesure, et qui appelle un fonction qui réelise un tri par insertion (tri de base).
déterminer la complexité.

LSb1.3 - Éléments de correction du TD3. 1


frontière

partie triée

insertion

Opérations de base du tri par insertion.

1. prise de l’élément frontière


2. insertion de l’élément frontière dans la partie triée
3. déplacement de la frontière

Pour inserer l’élément frontière dans la partie triée


– soit on le fait descendre dans la partie triée par échange successif (tant qu’il est inférieur à
son prédécesseur)
– soit on stocke sa valeur, on décale ses prédécesseurs, d’une case vers la droite (tant qu’ils sont
supérieurs à l’élément frontière), puis l’on insère finalement dans la case laissée vide.

Complexité.
Pour un tableau de taille n, quelque soit la méthode d’insertion, on effectue n parcours de
tableau. Pour chaque parcours i, on effectue au plus n − i traitement(s). On effectue

n(n + 1)
n + (n − 1) + (n − 2) + · · · + 1 = traitements.
2
On a donc une complexité en O(n2 ).

Exercice 3. Écrire un programme C/C++ qui lit une série d’entiers, les stocke dans un tableau que l’on
grandit au fur et à mesure, et qui appelle une fonction qui réalise un tri par échange.

Description de l’algorithme de tri par échange.


Pour un tableau de taille n, on effectue n − 1 parcours de gauche à droite, en effectuant la
comparaison de deux éléments successifs et en les permutants si nécessaires. On a alors une
bulle qui remonte de gauche à doite : on parle de tri par bulles.

8 3 6 2 7 tableau initial
8 3 6 2 7
3 8 6 2 7
3 6 8 2 7
3 6 2 8 7
3 6 2 7 8 tableau à la fin du premier parcours
···

Complexité.
On effectue n − 1 parcours, et pour chaque parcours n − 1 comparaisons. De la même manière
que dans les exercices précédents, on a une complexité en O(n2 ).

LSb1.3 - Éléments de correction du TD3. 2


Améliorations de l’algorithme.

– La partie droite du tableau est triée (valeur les plus élevées). Á chaque parcours i, il suffit
d’effectuer n − 1 − i comparaisons.
– Si lors d’un parcours on effectue aucune permutation, le tableau est trié, et on peut arrêter le
tri.

Exercice 4. Écrire le programme C/C++ qui effectue un tri rapide : le QuickSort. Déterminer la
complexité.

Opérations de base du tri rapide (QuickSort).

1. choisir un élément pivot (on choisira ici la valeur la plus à droite dans le tableau).
2. réorganiser le tableau de telle sorte que toutes les valeurs inférieures au pivot soient à
gauche du pivot, et toutes celles supérieures soient à droite du pivot : le pivot est alors
bien placé dans le tableau.
3. trier les parties non triées à droite et à gauche du pivot par la méthode du QuickSort.

1 8 3 4 0 7 2 6 tableau initial : pivot = 6


1 3 4 0 2 6 8 7 réorganisation
il reste à trier les deux sous-tableaux [1 3 4 0 2] et [8 7]

Complexités.
On note T (n) le cout du tri d’un tableau de taille n et c · n le cout de la réorganisation d’un
tableau de taille n de complexité O(n). On a donc

T (n) = c · n + 2 · T (n/2).

On note p = log2 (n). On a donc

T (2p ) = c · 2p + 2 · T (2p−1 )
T (2p−1 ) = c · 2p−1 + 2 · T (2p−2 )
···
T (2) = c · 2 + 2 · T (1),

et donc
T (2p ) = c · (2p + 2 · 2p−1 + · · · + 2p−1 · 2) + 2p · T (1).
| {z }
log2 (n) termes

Finalement, quand
T (n) = c · n · log2 (n) + n · T (1),
et la complexité est alors en O(n · log2 (n)).

LSb1.3 - Éléments de correction du TD3. 3


2 Programme C/C++
/* -----------------------------------------------
*
* TD3 - Exercices
* ===============
*
*/
#include <iostream>
#include <vector>

#include <time.h>

using namespace std;

// Fonctions communes a tous les exercices


void echange( int& a , int& b ){
int tmp = a;
a = b; b = tmp;
}

void affiche(vector<int> t){


int i;
for( i = 0 ; i < t.size() ; i++ )
cout << t[i] << " ";
cout << endl;
}
vector<int> random(int size, int range)
{
vector<int> v(size); int i;
srand ( time(NULL) );
for(i = 0 ; i < v.size() ; i++ ){
v[i] = ( rand() % range ) + 1;
}
return v;
}
// ---------------------------------------

void tri_extraction(vector<int>&); // Exercice 1

void tri_echange_insertion(vector<int>&); // Exercice 2


void tri_decalage_insertion(vector<int>&); // Exercice 2

void tri_par_bulle(vector<int>&); // Exercice 3


void tri_rapide(vector<int>&); // Exercice 3
void quick_sort(vector<int>&,int,int);

int main()
{
vector<int> v = random(20,100); affiche(v);
vector<int> v1=v; tri_extraction(v1); affiche(v1);
vector<int> v2=v; tri_echange_insertion(v2); affiche(v2);
vector<int> v3=v; tri_decalage_insertion(v3); affiche(v3);
vector<int> v4=v; tri_par_bulle(v4); affiche(v4);
vector<int> v5=v; tri_par_bulle(v5); affiche(v5);
return 0;
}

// ---------------------------------------
// Tri par extraction
// -> complexite O(n^2)
void tri_extraction(vector<int>& tab)
{
int i, j, min, rang_min;

for( i = 0 ; i < tab.size() ; i++ ){


min = tab[i];
rang_min = i;

for( j = i+1 ; j < tab.size() ; j++ ){


if( tab[j] < min ){
min = tab[j];
rang_min = j;
}
}
echange(tab[i],tab[rang_min]);
}
}

LSb1.3 - Éléments de correction du TD3. 4


// ---------------------------------------
// Tri par echange et insertion
// -> complexite O(n^2)
void tri_echange_insertion(vector<int>& tab)
{
int frontiere;

for( frontiere = 0 ; frontiere < tab.size() ; frontiere++ ){

int i = frontiere;
while( i > 0 && tab[i] < tab[i-1] ){
echange(tab[i],tab[i-1]);
i = i - 1;
}
}
}

// ---------------------------------------
// Tri par decalage et insertion
// -> complexite O(n^2)
void tri_decalage_insertion(vector<int>& tab)
{
int frontiere;

for( frontiere = 0 ; frontiere < tab.size() ; frontiere++ ){

int tmp = tab[frontiere];


int i = frontiere-1;
while( i >= 0 && tmp < tab[i] ){
tab[i+1] = tab[i];
i = i - 1;
}
tab[i+1] = tmp;
}
}

// ---------------------------------------
// Tri par echange simpliste : tri par bulle
// -> complexite O(n^2)
void tri_par_bulle(vector<int>& tab)
{
int i, j;

for( i = 0 ; i < tab.size() ; i++ ){

bool permut = false;


int size = tab.size();
for( j = 0 ; j < size - (i + 1) ; j++ ){
if( tab[j] > tab[j+1] ) {
echange(tab[j],tab[j+1]);
permut = true;
}
}

// Si lors d’un parcours aucune permutation n’est effectuee, le tablea est trie
if( !permut ) break;
}
}

// ---------------------------------------
// Tri rapide
// -> complexite O(n.log(n))
void tri_rapide(vector<int>& tab)
{ quick_sort(tab,0,tab.size()); }

void quick_sort(vector<int>& tab , int g, int d)


{
if( d <= g ) return; // la partie de tablea a trier est vide !

int pivot = tab[d]; // on stocke le pivot (element le plus a droite dans le tableau)
int i = g, j = d-1;

// on reordonne le tableau
while( j > i ){

while( tab[i] < pivot ) i++;


while( pivot < tab[j] && j > i ) j--;

if( j > i ) echange(tab[i],tab[j]);

LSb1.3 - Éléments de correction du TD3. 5


}

// on repositionne le pivot
echange(tab[d],tab[j]);

// on tri les deux sous-tableaux


quick_sort(tab,g,i-1);
quick_sort(tab,g+1,d);
}
// -----------------------------------------------

LSb1.3 - Éléments de correction du TD3. 6

Vous aimerez peut-être aussi