I. Introduction▲
I-A. À propos▲
I-B. Versions des logiciels et bibliothèques utilisés▲
I-C. Mises à jour▲
I-D. Pourquoi on tri ? Pourquoi c'est important ?▲
I-E. Projet d'exemple▲
Puisque cet article et les éléments présentés sont placés sous le signe de la collaboration, un projet a été créé sur GitHubGitHub. Le projet se trouve à l'adresse https://github.com/thierryler/algo-tri-dvpProjet sur GitHub et vous pouvez le récupérer depuis votre console Git à l'aide de la commande suivante :
git clone https://github.com/thierryler/algo-tri-dvp.git
Le résultat devrait ressembler à la trace suivante :
$ git clone https://github.com/thierryler/algo-tri-dvp.git
Cloning into 'algo-tri-dvp'...
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
Checking connectivity... done.
Le code est offert sous license Apache 2.0License Apache 2.0 dont vous trouverez une copieCopie de license Apache 2.0 à la racine du projet.
Pour des raisons de lisibilité, les codes des algorithmes sont présentés sans gestion d'exception. Reportez vous au projet GitHub et/ou aux annexes pour consulter les versions completes.
I-F. Objets à trier▲
En Java, on peut trier soit des primitifs (entiers, doubles, etc.) soit des objets. Pour illustrer le tri des objets, je vous propose d'utiliser la classe Cat, qui représente un chat et qui est un bean Java tout ce qu'il y a de plus classique.
public interface Animal {
String getName();
double getSize();
double getWeight();
int getAge();
}
public abstract class AbstractAnimal implements Animal {
private String name;
private double size;
private double weight;
private int age;
...
public class Cat extends AbstractAnimal implements Animal {
private CatRace race;
private String favoriteMilk;
...Je vous propose également d'utiliser la classe Dog, représentant un chien et qui, contrairement à Cat, implémente Comparable. Cette interface définit la méthode compareTo() et indique au JDK comment décider d'un chien est plus grand qu'un autre.
public class Dog extends AbstractAnimal implements Animal, Comparable<Dog> {
private DogRace race;
@Override
public int compareTo(Dog o) {
...
}
...
I-G. Interfaces des algorithmes de tri▲
TODO
public interface IntSort {
void sort(final int[] tab);
}
public interface ComparableObjectSort {
<T extends Comparable<? super T>> void sort(final T[] tab);
<T extends Comparable<? super T>> void sort(final List<T> list);
}
public interface ObjectSort {
<T> void sort(final Object[] tab, final Comparator<? super T> comparator);
<T> void sort(final List<T> list, final Comparator<? super T> comparator);
}Dans le code source du projet, sur GitHub, vous trouverez également des interfaces définissant des tris sur des copies des listes et des tableaux. Pour des raison de clareté, et sauf exception, les implémentations de ces versions ne seront pas présentées dans ce document.
II. Complexité▲
III. Tests▲
Avant de programmer des algorithmes de tri, je vous propose d'écrire quelques tests qui nous permettront de vérifier que tout fonctionne. Puisqu'on va s'intéresser à des listes assez grosses, les tests automatisés vont vite devenir indispensables.
Cette approche s'inscrit dans un démarche qualité. Pour cela, on va faire du TDD (Test Driven Development) et plus spécifiquement du 3T (Tests en Trois Temps) dont le premier temps, visant à écrire les contrats, a déjà été effectué durant l'introduction de ce document.
III-A. Cas simple▲
public abstract class AbstractIntSortTest {
protected IntSort algo;
@Test
public void testSimpleSort() {
// Arrange
final int[] tab = { 3, 0, 1, 7, 6, 9, 8, 2, 4, 5 };
final int size = tab.length;
// Act
algo.sort(tab);
// Assert
// On verifie qu'on n'a pas perdu des elements en chemin.
Assert.assertEquals(size, tab.length);
// On verifie que l'ordre des elements est le bon
int previous = Integer.MIN_VALUE;
for (final int elt : tab) {
Assert.assertTrue(previous <= elt);
previous = elt;
}
}
public class JavaIntSortTest extends AbstractIntSortTest {
public JavaIntSortTest() {
this.algo = new JavaIntSort();
}
}
public class JavaIntSort implements IntSort {
@Override
public void sort(int[] tab) {
throw new UnsupportedOperationException("Not yet");
}
}On pourrait penser que ça suffit mais il arrive, quand on a mal programmer l'algorithme, qu'on perde des valeurs. Je dois donc ajouter un test sur la taille.
@Test
public void testSimpleSort() {
// Arrange
final int[] tab = { 3, 0, 1, 7, 6, 9, 8, 2, 4, 5 };
final int size = tab.length;
// Act
algo.sort(tab);
// Assert
// On verifie qu'on n'a pas perdu des elements en chemin.
Assert.assertEquals(size, tab.length);
...
}Mais cela ne suffit toujours pas. En effet, durant l'écriture de cet article, il m'est aussi arrivé d'écraser des valeurs, sans toutefois changer le nombre d'éléments.
Une première solution consiste à dénombrer chaque valeur dans une map, avant et après. Il faut donc faire une sauvegarde du tableau avant.
@Test
public void testSimpleSort() {
// Arrange
final int[] tab = { 3, 0, 1, 7, 6, 9, 8, 2, 4, 5 };
final int size = tab.length;
final int[] backup = new int[size];
System.arraycopy(tab, 0, backup, 0, size);
// Act
algo.sort(tab);
// Assert
...
// On verifie qu'il a bien les meme elements avant et apres
final Map<Integer, Integer> mapBackup = tabToMap(backup);
final Map<Integer, Integer> mapTab = tabToMap(tab);
Assert.assertEquals(mapBackup, mapTab);
}
public static Map<Integer, Integer> tabToMap(final int[] tab) {
final Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (final int elt : tab) {
int nb = 1;
if (map.containsKey(elt)) {
nb += (int) map.get(elt);
}
map.put(elt, nb);
}
return map;
}Ne criez pas tout de suite au scandale. Oui, vous avez raison ; cette solution est vraiment affreuse. J'ai cherché pendant longtemps comment faire mieux. Au final, je me suis dis qu'on pouvait bien se reposer sur les fonctions du JDK, qui ont déjà été testées par leurs auteurs.
@Test
public void testSimpleSort() {
// Arrange
final int[] tab = { 3, 0, 1, 7, 6, 9, 8, 2, 4, 5 };
final int size = tab.length;
final int[] expected = new int[size];
System.arraycopy(tab, 0, expected, 0, size);
Arrays.sort(expected);
// Act
algo.sort(tab);
// Assert
// On verifie juste que notre version triee est la meme que celle du JDK
Assert.assertArrayEquals(expected, tab);
}Dans cet article, j'ai donc finalement utilisé deux stratégies. Quand il y beaucoup de valeurs, j'utilise le tri du JDK pour comparer, comme ci-dessus. Et il y a très peu de valeurs, j'indique dirrectement le résultat attendu. Je trouve que c'est plus simple à lire.
@Test
public void testSimpleSort() {
// Arrange
final int[] tab = { 3, 0, 1, 7, 6, 9, 8, 2, 4, 5 };
final int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Act
algo.sort(tab);
// Assert
// On verifie juste que notre version triee est la meme que celle du JDK
Assert.assertArrayEquals(expected, tab);
}Il ne reste plus qu'à factoriser un peu tout cela, car on va ajouter de nombreux tests dans la suite.
public abstract class AbstractIntSortTest {
protected IntSort algo;
@Test
public void testSimpleSort() {
// Arrange
final int[] tab = { 3, 0, 1, 7, 6, 9, 8, 2, 4, 5 };
final int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Act
doTest(tab, expected);
}
@Test
public void testSimpleSortWithJdk() {
// Arrange
final int[] tab = { 3, 0, 1, 7, 6, 9, 8, 2, 4, 5 };
// Act and assert
doTestAndCompareWithJdk(tab);
}
private void doTestAndCompareWithJdk(final int[] tab) {
// Arrange
final int size = tab.length;
final int[] expected = new int[size];
System.arraycopy(tab, 0, expected, 0, size);
Arrays.sort(expected);
// Act and assert
doTest(tab, expected);
}
private void doTest(final int[] tab, final int[] expected) {
// Act
algo.sort(tab);
// Assert
// On verifie juste que notre version triee est la meme que celle du JDK
Assert.assertArrayEquals(expected, tab);
}III-B. Cas standards▲
TODO
III-C. Distributions▲
TODO
IV. Algorithmes▲
IV-A. Tri de Java▲
IV-B. Tri par selection▲
IV-C. Tri par insertion▲
IV-D. Tri rapide (Quick Sort)▲
IV-E. Tri fusion (Merge sort)▲
IV-F. Tri par inteclassement monotone▲
IV-G. Autres tris▲
Ce document est un article collaboratif. Vous pouvez donc proposer des algorithmes et des implémentations supplémentaires. Ils seront alors ajoutés à la suite.
V. Conclusion▲
TODO
V-A. Quand et comment utiliser tel ou tel tri ?▲
V-B. Performances comparées▲
V-C. Participez▲
TODO
Vos retours nous aident à améliorer nos publications. N'hésitez donc pas à commenter cet article sur le forum : TODO






