Tri du drapeau néerlandais (Dutch National Flag)
Contexte
Proposé par Edsger W. Dijkstra, ce problème doit son nom aux trois bandes du drapeau des Pays-Bas. Il s'agit de trier un tableau ne contenant que c valeurs distinctes (typiquement 0, 1, 2 pour les trois couleurs) en un seul parcours, sans utiliser de mémoire auxiliaire.
Cas c = 3 — Trois couleurs
L'algorithme maintient trois pointeurs :
lo : frontière de la zone des 0 (tout ce qui est avant est 0)
mid : élément en cours d'examen
hi : frontière de la zone des 2 (tout ce qui est après est 2)
La zone entre lo et mid-1 contient les 1 (déjà examinés et classés).
#include <stddef.h>
// Trie un tableau ne contenant QUE les valeurs 0, 1, 2
// @pre: pour tout i, x[i] ∈ {0, 1, 2}
// @post: x est trié en ordre croissant
void drapeau_neerlandais_3(int *x, size_t n) {
int *lo = x; // frontière des 0 (tout ce qui est < lo est 0)
int *mid = x; // élément courant
int *hi = x + n - 1; // frontière des 2 (tout ce qui est > hi est 2)
// @invariant: [x, lo) = 0
// [lo, mid) = 1
// [mid, hi] = zone non examinée
// (hi, x+n) = 2
// @variant: hi - mid + 1
while (mid <= hi) {
switch (*mid) {
case 0:
// échanger avec lo, les deux avancent
{ int t = *lo; *lo = *mid; *mid = t; }
lo++;
mid++;
break;
case 1:
// déjà à sa place
mid++;
break;
case 2:
// échanger avec hi, seul hi recule
// (ne pas avancer mid : la nouvelle valeur doit être examinée)
{ int t = *mid; *mid = *hi; *hi = t; }
hi--;
break;
}
}
}
Trace d'exécution (exemple)
Tableau initial : [2, 0, 2, 1, 1, 0]
| lo |
mid |
hi |
État après l'étape |
| 0 |
0 |
5 |
[0, 0, 2, 1, 1, 2] — *mid=2, échangé avec hi |
| 0 |
0 |
4 |
[0, 0, 2, 1, 1, 2] — *mid=0, échangé avec lo |
| 1 |
1 |
4 |
[0, 0, 2, 1, 1, 2] — *mid=0, échangé avec lo |
| 2 |
2 |
4 |
[0, 0, 2, 1, 1, 2] — *mid=2, échangé avec hi |
| 2 |
2 |
3 |
[0, 0, 1, 1, 2, 2] — *mid=1, avance |
| 2 |
3 |
3 |
[0, 0, 1, 1, 2, 2] — *mid=1, avance |
| 2 |
4 |
3 |
Terminé (mid > hi) |
Analyse pour c = 3
- Comparaisons : exactement n (un
switch par élément)
- Échanges : au plus n (chaque élément est échangé au plus une fois)
- Complexité temporelle : Θ(n)
- Complexité spatiale : O(1)
- Stable : Non (les échanges avec
hi brisent la stabilité)
Généralisation à c quelconque
Pour c valeurs quelconques, on peut trier en appliquant récursivement le partitionnement :
// Trie un tableau de n entiers ∈ [0, c-1]
void drapeau_neerlandais(int *x, size_t n, int c) {
if (c <= 1 || n <= 1) return;
// Partition : 0 à gauche, {1..c-1} à droite
int *lo = x, *hi = x + n - 1;
while (lo <= hi) {
if (*lo == 0) lo++;
else { int t = *lo; *lo = *hi; *hi = t; hi--; }
}
// Récurrence sur le suffixe pour les couleurs 1..c-1
if (lo < x + n)
drapeau_neerlandais(lo, x + n - lo, c - 1);
}
- Complexité : Θ(c·n) comparaisons et échanges
- Efficace quand c ≪ n ; si c est grand, préférer un tri Θ(n lg n).