Introduction à l'Algorithmique 1
Introduction à l'Algorithmique 1
m+n
2
|. On a m k < n. Donc, les longueurs des deux tableaux passes
en argument des appels recursifs verient : k m + 1 n m N
et n k n m N. Par hypoth`ese de recurrence, quelque soit le
resultat du test T[k] < x, lalgorithme donnera une reponse correcte.
2.1.3 Exercices
Prouvez la correction des algorithmes suivants :
10 CHAPITRE 2. ANALYSE DES ALGORITHMES
1. Recherche du plus grand element dun tableau (version iterative) :
Fonction MaxEltIter(T)
entree : T[1, n] est un tableau dentiers.
sortie : le plus grand element de T[1, n]
debut
Max T[1]
pour i = 2 `a n faire
si T[i] > Max alors
Max T[i]
nsi
npour
retourner Max
n
2. Recherche du plus grand element dun tableau (version recursive) :
Algorithme 4: MaxEltRec
entree : T[1, n] est un tableau dentiers.
sortie : le plus grand element de T[1, n]
debut
si n = 1 alors
retourner T[1]
sinon
retourner Max(T[n], MaxEltRec(T[1, n 1]))
nsi
n
3. Recherche conjointe du plus petit element et du plus grand element
dun tableau.
2.2. COMPLEXIT
E DES ALGORITHMES 11
Algorithme 5: MinMaxElt
entree : T[1, n] est un tableau dentiers.
sortie : le plus petit et le plus grand element de T[1, n]
debut
Min T[1], Max T[1]
pour j = 1 `a
n1
2
| faire
si T[2 j] < T[2 j + 1] alors
si T[2 j] < Min alors
Min T[2 j]
nsi
si T[2 j + 1] > Max alors
Max T[2 j + 1]
nsi
sinon
si T[2 j + 1] < Min alors
Min T[2 j + 1]
nsi
si T[2 j] > Max alors
Max T[2 j]
nsi
nsi
npour
si n est pair alors
si T[n] < Min alors
Min T[n]
nsi
si T[n] > Max alors
Max T[n]
nsi
nsi
retourner Min, Max
n
2.2 Complexite des algorithmes
En plus detre correct, cest-`a-dire deectuer le calcul attendu, on de-
mande `a un algorithme detre ecace, lecacite etant mesuree par le temps
que prend lexecution de lalgorithme ou plus generalement, par les quantites
de ressources necessaires `a son execution (espace memoire, bande passante,
12 CHAPITRE 2. ANALYSE DES ALGORITHMES
. . . ).
Le temps dexecution dun algorithme depend evidemment de la taille
des donnees fournies en entree, ne serait-ce que parce quil faut les lire avant
de les traiter. La taille dune donnee est mesuree par le nombre de bits
necessaires `a sa representation en machine. Cette mesure ne depend pas du
materiel sur lequel lalgorithme sera programme.
Mais comment mesurer le temps dexecution dun algorithme puisque
les temps de calcul de deux implementations du meme algorithme sur deux
machines dierentes peuvent etre tr`es dierents ? En fait, quelque soit la
machine sur laquelle un algorithme est implemente, le temps de calcul du
programme correspondant est egal au nombre doperations elementaires ef-
fectuees par lalgorithme, `a un facteur multiplicatif pr`es, et ce, quelle que
soit la taille des donnees dentree de lalgorithme. Par operations elementaires
on entend evaluations dexpressions, aectations, et plus generalement trai-
tements en temps constant ou independant de lalgorithme (entrees-sorties,. . . ).
Le detail de lexecution de ces operations au niveau du processeur par lin-
termediaire dinstructions machine donnerait des resultats identiques `a un
facteur constant pr`es.
Autrement dit, pour toute machine /, il existe des constantes m et M
telles que pour tout algorithme / et pour toute donnee d fournie en entree de
lalgorithme, si lon note T(d) le nombre doperations elementaires eectuees
par lalgorithme avec la donnee d en entree et T
M
(d) le temps de calcul du
programme implementant / avec la donnee d en entree,
mT(d) T
M
(d) MT(d).
Soit, en utilisant les notations de Landau (voir section 8.1),
T = (T
M
).
On peut donc denir la complexite en temps dun algorithme comme le
nombre doperations elementaires T(n) eectuees lors de son execution sur
des donnees de taille n. Il sagit donc dune fonction. On distingue
la complexite dans le pire des cas (worst case complexity) : T
pire
(n) =
le nombre doperations maximal calcule sur toutes les donnees de taille
n;
la complexite dans le meilleur des cas (best case complexity) : T
min
(n) =
le nombre doperations minimal calcule sur toutes les donnees de taille
n;
la complexite en moyenne (average case complexity) : T
moy
(n) = le
nombre doperations moyen calcule sur toutes les donnees de taille n,
en supposant quelles sont toutes equiprobables.
2.2. COMPLEXIT
E DES ALGORITHMES 13
Analyser un algorithme consiste `a evaluer la ou les fonctions de com-
plexite qui lui sont associees. En pratique, on cherche `a evaluer le comporte-
ment asymptotique de ces fonctions relativement `a la taille des entrees (voir
section 8.1). En considerant le temps dexecution, dans le pire des cas ou en
moyenne, on parle ainsi dalgorithmes
en temps constant : T(n) = (1),
logarithmiques : T(n) = (log n),
lineaires : T(n) = (n),
quadratiques : T(n) = (n
2
),
polynomiaux : T(n) = (n
k
) pour k N,
exponentiels : T(n) = (k
n
) pour k > 0.
Si un traitement elementaire prends un millioni`eme de seconde, le tableau
suivant decrit les temps dexecutions approximatifs de quelques classes de
probl`emes en fonction de leurs tailles
Taille n log
2
n n nlog
2
n n
2
2
n
10 0.003 ms 0.01 ms 0.03 ms 0.1 ms 1 ms
100 0.006 ms 0.1 ms 0.6 ms 10 ms 10
14
siecles
1000 0.01 ms 1 ms 10 ms 1 s
10
4
0.013 ms 10 ms 0.1 s 100 s
10
5
0.016 ms 100 ms 1.6 s 3 heures
10
6
0.02 ms 1 s 20 s 10 jours
Dun autre point de vue, le tableau ci-dessous donne la taille des probl`emes
que lon peut traiter en une seconde en fonction de la rapidite de la machine
utilisee (nTs est le nombre de traitements par secondes).
nTs 2
n
n
2
nlog
2
n n log
2
n
10
6
20 1000 63000 10
6
10
300000
10
7
23 3162 600000 10
7
10
3000000
10
9
30 31000 4.10
7
10
9
10
12
40 10
6
3.10
10
En pratique, un algorithme est constitue dinstructions agencees `a laide
de structures de controle que sont les sequence, les embranchements et les
boucles (nous verrons plus tard comment traiter le cas particulier des fonc-
tions recursives, mais de toutes facons toute fonction recursive secrit aussi
iterativement). Le co ut dune sequence de traitements est la somme des
14 CHAPITRE 2. ANALYSE DES ALGORITHMES
co uts de chaque traitement. Le co ut dune structure iterative est la somme
des co uts des traitements eectues lors des passages successifs dans la boucle.
Par exemple il arrive frequemment que lon eectue n fois une boucle avec
des co uts degressifs ((n), (n 1),. . . ,(1)). Dans ce cas
T(n) = n + (n 1) + (n 2) + . . . + 1 =
n
i=1
i =
n (n + 1)
2
Le co ut dun embranchement (si test alors traitement1 sinon traitement2)
est le co ut du plus couteux des deux traitements.
2.2.1 Exemples danalyse dalgorithmes
Lalgorithme dinsertion dun element dans un tableau trie Dans
le pire des cas, celui o` u lentier `a inserer est plus petit que tous les elements
du tableau, lalgorithme 2 requiert
2N + 2 aectations,
2N comparaisons dentiers,
N soustractions et 1 addition,
N evaluations dune expression booleenne
pour un tableau de N elements.
En supposant que les entiers ont une taille constante k, une donnee en
entree de taille n permet de representer n/k entiers, soit un tableau de
N = n/k 1| entiers. On remarque que N = (n). En supposant de plus
que chaque instruction elementaire sexecute en un temps constant, on en
deduit que T
pire
(n) = (n). Lalgorithme dinsertion ordonnee est lineaire
dans le pire des cas : `a des constantes additive et multiplicative pr`es, le
temps dexecution est egal au nombre delements du tableau.
On voit facilement que T
min
(n) = (1) : en eet, dans le meilleur des
cas, lelement `a inserer est plus grand que tous les elements du tableau et la
boucle nest pas executee.
Si lon suppose que lelement `a inserer et un element pris au hasard
dans le tableau suivent la meme loi de probabilite, son insertion necessitera
en moyenne N comparaisons. On aura donc, T
moy
(n) = (n), soit une
complexite moyenne du meme ordre que la complexite du pire des cas.
Lalgorithme de tri par insertion Dans le pire des cas, celui o` u le
tableau en entree est dej`a trie, mais par ordre decroissant, lalgorithme
necessite
2.2. COMPLEXIT
E DES ALGORITHMES 15
N1 comparaisons et aectations pour la condition de la boucle pour
N 1 appels `a lalgorithme dinsertion ordonnee, pour des tailles de
tableaux variant de 1 `a N 1.
A des constantes additive et multiplicative pr`es, le temps dexecution
dans le pire des cas de lalgorithme de tri par insertion est donc egal `a
(N 1) + 1 + 2 + . . . + (N 1) = N 1 +
N(N 1)
2
= (N
2
)
soit en appliquant la remarque sur legalite `a des constantes additive et
multiplicative pr`es de la taille des donnees et du nombre delements du
tableau,
T
pire
(n) = (n
2
).
On voit facilement que T
min
(n) = (n), cas o` u le tableau est dej`a or-
donne par ordre croissant.
Si tous les elements du tableau en entree sont tires selon la meme loi
de probabilites, on peut montrer que T
moy
(n) = (n
2
). Autrement dit, sil
peut arriver que le temps dexecution soit lineaire, il y a beaucoup plus de
chance quil soit quadratique en la taille des donnees en entree.
Lalgorithme de recherche dichotomique Si le tableau contient un
seul element, lalgorithme executera 2 comparaisons (co ut total : c
1
)
Si le tableau contient N > 1 elements, lalgorithme executera
3 operations arithmetiques, une aectation et une comparaison entre
2 entiers (co ut total : c
2
), et
un appel recursif sur un tableau possedant N/2| ou N/2| elements,
soit N/2| dans le pire des cas.
On en deduit que
T
pire
(N) = T
pire
(N/2|) + c
2
.
Conformement `a la remarque faite ci-dessus sur lequivalence entre la
taille dun tableau dentiers et sa longueur, on supposera que N est egal `a
la taille des donnees.
Cette equation est simple `a resoudre lorsque N est egal `a une puissance
de 2 :
T
pire
(2
k
) = T
pire
(2
k1
) + c
2
= T
pire
(2
k2
) + 2c
2
= . . . = c
1
+ kc
2
.
Dans le cas general, soit
N = a
k
2
k
+ . . . + a
1
2
1
+ a
0
16 CHAPITRE 2. ANALYSE DES ALGORITHMES
lecriture de N en base 2 : a
k
= 1 et 0 a
i
1 pour 0 i < k. On a
2
k
N < 2
k+1
et donc 2
k1
N/2| 2
k
.
On en deduit que
c
1
+ kc
2
T
pire
(N) c
1
+ (k + 1)c
2
.
En remarquant que k = (log
2
N), on en deduit que T
pire
(N) = (log
2
N).
La recherche dichotomique dun element dans un tableau trie se fait donc
en temps logarithmique.
Lanalyse dalgorithmes conduit souvent `a des equations recursives de la
forme
T(n) = a T(n/b) + f(n).
Le theor`eme 1 de la section 8.2 donne la solution de ces equations dans le cas
general. Dans lexemple precedent, on se trouve dans le cas (2) du theor`eme,
avec a = 1 et b = 2 : on retrouve que T
pire
(N) = (log
2
N).
Le tri par fusion
Algorithme 6: TriFusion
entree : T[1, n] est un tableau dentiers, n 1.
resultat : les elements de T sont ordonnes par ordre croissant.
debut
si n > 1 alors
T
1
= triFusion(T[1, n/2|])
T
2
= triFusion(T[n/2| + 1, n])
T = Fusion(T
1
, T
2
)
nsi
n
La recurrence est
T(n) = 1 + n + 2 T(n/2) + n
Dautre part T(0) = T(1) = 1.
Solution methode generale (voir le theor`eme 1 de la section 8.2).
On est dans le cas 2 puisque f(n) = 2 n + 1 et puisque a = b = 2 on a
log
b
a = 1 et f(n) = (n). Donc T(n) = (n lgn).
Solution en developpant la recurrence.
2.2. COMPLEXIT
E DES ALGORITHMES 17
T(n) = 2n + 1 + 2 T(n/2) = 2n + 1 + 2n + 2 + 4 T(n/4)
= 2n + 1 + 2n + 2 + 2n + 4 + 8 T(n/8) = . . .
On en deduit que puisque T(1) = 1, on a une suite de log
2
n| termes
puisquon divise n par 2 `a chaque fois, et donc
T(n) =
log
2
n
i=0
(2n + 2
i
) = 2n log
2
n +
log
2
n
i=0
2
i
que lon sait calculer
3
. Donc
T(n) = 2nlog
2
n+2
log
2
n+1
1 = 2nlog
2
n+2n = 2n(log
2
n+1) = O(nlog
2
n)
hauteur : log
2
n|
n
2x
n 2x
n 2x
n
n/2 n/2
n/4 n/4 n/4 n/4
n/8 n/8 n/8 n/8 n/8 n/8 n/8 n/8
1 1 1 ....
log
n
Figure 2.1 Arbre des appels recursifs pour lalgorithme de tri par fusion.
Solution intuitive. On remarque que lexecution de la procedure produit
le parcours dun arbre virtuel de profondeur log
2
n|, et qu`a chaque niveau
de larbre on eectue (n) traitements (2
p
noeuds `a chaque niveau, et des
suites de longueurs n/(2
p
) elements en chaque noeud). On pressent donc in-
tuitivement que le co ut est quelque chose de la forme nlog
2
n. Mais attention
il peut y avoir des termes de classe inferieure. On va donc partir sur une
forme compl`ete de T(n) avec des constantes
T(n) = a n log
2
n + b n + c
Nous essaierons ensuite de xer les constantes, dune part en developpant
lequation recursive en substituant `a T(n/2) la formule ci-dessus, mais aussi
en utilisant les cas pour lesquels on connait le resultat, cest-`a-dire quand n
est tr`es petit.
3. Quelques formules de sommation :
P
n
i=0
i =
n(n+1)
2
= O(n
2
) ;
P
n
i=0
x
i
=
x
n+1
1
x1
=
O(x
n
) si x = 1.
18 CHAPITRE 2. ANALYSE DES ALGORITHMES
Donc T(n/2) = a n/2 log
2
n a n/2 + b n/2 + c. En reprenant la
recurrence on obtient
T(n) = (2c + 1) + (b + 2 a) n + a n log
2
n
On en deduit (1) 2c + 1 = c donc c = 1, (2) b + 2 1 = b donc a = 2.
Puisque T(1) vaut 1, on en deduit que 2 n log
2
1 + b 1 = 1 et donc
b = 2. Donc
T(n) = 2 n log
2
n + 2 n 1 = O(n log
2
n)
2.2.2 Exercices
Exercice 1 Quelle est la classe de complexite de la fonction f(n) suivante
(demontrez-le)
f(n) = 2 n
3
+ 4 n
2
+ 2
3
Exercice 2 Quelle est la complexite de la fonction bidon suivante :
fonction bidon(n)
debut
pour i := 1 `a n n faire
u := i,
tant que (u > 1) faire
u := u/2,
n faire
n faire
n fonction
Exercice 3
1.
Ecrivez un algorithme qui prend en entree un tableau dentiers et
calcule les valeurs minimale et maximale quil contient.
Evaluez la
complexite de votre algorithme (mesuree en nombre de comparaisons
eectuees).
2.
Evaluez la complexite de lalgorithme 7 (meme mesure).
Exercice 4 Calculez la complexite de la fonction ProdMat(MAT A, MAT
B, MAT C, int n) qui calcule le produit des matrices carrees A et B de n
lignes et n colonnes.
Exercice 5 Calculer la complexite de la fonction exp(x, n) qui calcule x
n
`a partir de la formule suivante :
2.2. COMPLEXIT
E DES ALGORITHMES 19
x
n
= (x
2
)
n/2
si n est pair,
x
n
= x x
n1
si n est impair
En utilisant ce principe, quel est le co ut de lelevation dune matrice `a
la puissance n.
Exercice 6 : Calcul de lelement majoritaire dun tableau.
Etant donne un ensemble E de n elements, on veut savoir sil existe
un element majoritaire et quel est-il (un element majoritaire apparat plus
dune fois sur deux, i.e. si k est son nombre dapparitions, 2 k > n).
Methode nave Donnez une methode naive pour ce calcul. Quelle est sa
complexite ?
Methode 2 : diviser pour regner. On consid`ere maintenant lapproche
diviser pour regner suivante : pour calculer lelement majoritaire dans
lensemble E sil existe, on reparti les n elements de E dans deux
ensembles de memes tailles E
1
et E
2
, et on calcule (recursivement)
dans chacune de ces parties lelement majoritaire. Pour que e soit
majoritaire dans E il sut que e soit majoritaire dans E
1
et dans E
2
,
ou que e soit majoritaire dans E
1
et non dans E
2
(ou inversement),
mais quil apparaisse susamment dans E
2
.
Ecrivez une procedure de calcul correspondant `a cette idee. Quelle est
sa complexite ?
Une troisi`eme methode sera proposee au chapitre suivant.
20 CHAPITRE 2. ANALYSE DES ALGORITHMES
Chapitre 3
Structures lineaires
Les donnees que doivent traiter les programmes informatiques sont sou-
vent liees par des relations qui leur conf`erent une certaine structure : dans
certains jeux de cartes, on peut etre oblige de deposer des cartes au som-
met dun tas et navoir acc`es qu`a la derni`ere carte deposee : on parle de
structure de pile ; les processus envoyes `a une imprimante doivent etre geres
de mani`ere que le premier processus envoye soit le premier traite : on parle
de le dattente ; les positions sur un echiquier peuvent etre representes par
les noeuds dun arbre dont les successeurs sont toutes les positions attei-
gnables en un coup; les noeuds dun reseau de communication peuvent etre
representes par les sommets dungraphe, . . .
On a repertorie un certain nombre de structures de donnees qui re-
viennent dans la tr`es grande majorite des traitements informatique. Les
structures de donnees en informatique jouent un peu le role des struc-
tures abstraites en mathematiques, groupes, anneaux, corps, espaces vec-
toriels, . . . : elles sont susamment generales pour quil y ait avantage `a
les etudier independamment de toute application specique. On peut par
exemple etudier la question du tri dun tableau ou de la recherche dun
chemin dans un graphe sur des structures abstraites : les algorithmes qui
resolvent ces probl`emes dans le cas general pourront alors etre utilises dans
nimporte quelle situation specique.
Les langages de programmation proposent generalement des types simples,
quelques types plus complexes (tableaux, listes, etc) et des contructeurs de
types permettant dimplementer toutes les structures de donnees.
On parle de structure lineaire lorsque les donnees sont representees sequen-
tiellement : chaque element, sauf le dernier, poss`ede un successeur. On
etudiera en particulier les tableaux `a une dimension, les listes, les piles, les
21
22 CHAPITRE 3. STRUCTURES LIN
EAIRES
!
1
!
2
!
3
!
"
!
1
!
2
!
"
!
#
!
# +1
!
# +2
!
"
Insertion aprs les autres lments
Insertion dans un tableau tri
$
"%&
"
"%&
"!# recopies
$
Figure 3.1 Insertion dun element en n de tableau ou `a un indice donne.
les et les tables de hachage. Les matrices carrees, les arbres et les graphes
sont des structures non lineaires.
3.1 Tableaux
Un tableau (array) T est un ensemble delements accessibles par un indice
i [1 . . . n]. On suppose que le calcul du nombre delements dun tableau,
longueur(T) = n, lacc`es au i-`eme element T[i] et lajout dun element en
n de tableau peuvent etre realises en temps constant (O(1)). En revanche,
linsertion dun nouvel element `a un indice i donne necessite de deplacer
tous les elements suivants (temps lineaire dans le pire des cas). De meme
la recherche dun element dans un tableau non trie se fait en temps lineaire
dans le pire des cas. Nous avons vu precedemment que la recherche dun
element dans un tableau trie pouvait etre eectuee en temps logarithmique.
Implantation des tableaux en C
/* Definition */
#define NBMAX 1000 /* nombre maximum delements */
typedef int ELEMENT; /* les elements du tableau */
typedef ELEMENT TABLEAU[NBMAX];
3.2. LISTES CHA
IN
EES 23
/* Declaration */
TABLEAU t;
ou avec allocation dynamique de la memoire,
/* Definition */
typedef ELEMENT* TABLEAU;
/* Declaration */
TABLEAU t;
int nb_elts=50; /* nombre delements du tableau */
/* Allocation */
t=malloc(nb_elts*sizeof(ELEMENT));
3.2 Listes chanees
Une liste chanee (linked list) est une structure lineaire, composee de
maillons, chaque maillon comprenant un champ valeur qui permet de stocker
un element et un champ suivant qui pointe vers le maillon suivant ou est
egal `a NIL, sil sagit du dernier maillon de la liste. Une liste chainee L est
un objet qui est egal `a NIL si la liste est vide ou qui pointe vers un maillon,
le premier de la liste, si elle est non vide.
Implantation des listes chanees en C
/* Definition */
typedef int ELEMENT; /* par exemple */
typedef struct maillon *LISTE;
typedef struct maillon{
ELEMENT val;
LISTE suiv;
}MAILLON;
/* Declaration */
24 CHAPITRE 3. STRUCTURES LIN
EAIRES
i+2 i+1 i i!1
V V V V
prec p
prec !> suiv = CreerMaillon (x, prec !> suiv);
Cration et insertion d`un nouveau maillon avec la valeur x
x
Figure 3.2 Insertion dun element dans une liste `a une place donnee.
LISTE l;
/* Insertion dun element en t^ete dune liste */
LISTE CreerMaillon(ELEMENT elt, LISTE liste_initiale){
LISTE l;
l=malloc(sizeof(MAILLON));
l->val=elt;
l->suiv=liste_initiale;
return l;
}
On voit que linsertion dun element en tete de liste se fait en temps
constant.
Le traitement des listes chanees requiert de pouvoir :
inserer un element en queue de liste,
rechercher si un element est present dans la liste,
supprimer la premi`ere occurrence dun element,
inserer un element dans une liste ordonnee,
trier une liste,
vider une liste de ses elements.
Exercice 1
1. Quelle est la complexite dans le pire des cas des algorithmes precedents ?
2. Programmez-les en C.
3.2. LISTES CHA
IN
EES 25
liste
v1
v2 v3 bidon
Figure 3.3 Liste doublement chanee circulaire avec maillon vide.
Ameliorations de la structure de liste chanee Il nest pas possible
dacceder directement au maillon precedent le maillon courant dune liste
chanee. Cela complique notamment lecriture de lalgorithme de suppres-
sion dun maillon. Pour remedier `a ce defaut, on consid`ere des listes double-
ment chanees dans lesquels les maillons comprennent egalement un champ
precedent qui pointe vers le maillon precedent ou est egal `a NIL, sil sagit
du premier maillon de la liste.
Par ailleurs, les algorithmes de traitement des listes chanees sont alour-
dis par le fait quil faut traiter dieremment le premier maillon, qui na pas
de maillon precedent, des maillons suivants. Pour remedier `a cela, on intro-
duit un maillon vide, dont le champ val prend une valeur quelconque, et qui
constituera le premier maillon de la liste chanee.
Mais le meme probl`eme se pose alors pour le dernier maillon, qui nadmet
pas de maillon suivant. On suppose alors que le champ suivant du dernier
maillon pointe circulairement sur le maillon vide et que le champ precedent
du maillon vide pointe vers le dernier maillon.
On obtient ainsi la notion de liste doublement chanee circulaire avec
maillon vide (circular, doubly linked list, with a sentinel ).
Le tableau 3.5 compare la complexite de quelques operations elementaires
sur des tableaux et des listes chanees.
Exercice 2
Ecrire les algorithmes de traitement des listes doublement
chanees circulaires avec maillon vide.
Exercice 3 On represente un polynome `a coecients entiers par une liste
chainee dont les maillons poss`edent un champ coefficient et un champ
exposant ; on suppose de plus que les listes sont ordonnees par valeurs crois-
santes dexposant. Implantez cette structure de donnees en C et programmez
les fonctions suivantes :
26 CHAPITRE 3. STRUCTURES LIN
EAIRES
liste suppression de p
v1
v2 v3 bidon
Figure 3.4 Suppression dun maillon dans une liste doublement chanee
circulaire avec maillon vide.
Operation Tableau Tableau Liste Liste Liste Liste
trie ordonnee doublement ordonnee
chanee doublement
chanee
acc`es i-`eme element (1) (1) (n) (n) (n) (n)
insertion `a une (n) (n) (1) (1) (1) (1)
place donnee
recherche element (n) (log
2
n) (n) (n) (n) (n)
recherche succ. (1) (1) (1) (1) (1) (1)
recherche pred. (1) (1) (n) (n) (1) (1)
recherche maximum (n) (1) (n) (n) (n) (1)
recherche minimum (n) (1) (n) (1) (n) (1)
Figure 3.5 Comparatif tableaux / listes chanees.
3.3. PILES ET FILES 27
int degre(POLYNOME p)
void affiche(POLYNOME p)
POLYNOME somme(POLYNOME p1, POLYNOME p2)
POLYNOME multiplication scalaire(POLYNOME p, int a)
POLYNOME multiplication(POLYNOME p1, POLYNOME p2)
POLYNOME derivee(POLYNOME p)
float eval(POLYNOME p, float x)
3.3 Piles et les
Les piles (stack) et les les (queues) sont des structures lineaires qui
di`erent par la mani`ere dont les elements sont inseres et supprimes : dans
une pile, le dernier element entre est le premier sorti (Last In First Out :
LIFO) ; dans une le, le premier element entre est le premier sorti (First In
First Out : FIFO).
Trois fonctions (ou operations) susent `a faire fonctionner une pile :
la fonction booleenne pileVide ?(P) (StackEmpty(P)) qui retourne VRAI
si la pile P est vide et FAUX sinon,
la fonction empiler(P,x) (Push(P,x)) qui ins`ere un element x dans la
pile P,
la fonction depiler(P) (Pop(P)) qui retourne le dernier element insere
ou un message derreur si la pile est vide.
De meme, trois fonctions (ou operations) susent `a faire fonctionner une
le :
la fonction booleenne leVide ?(F) (QueueEmpty(F)) qui retourne VRAI
si la le F est vide et FAUX sinon,
la fonction enler(F,x) (EnQueue(F,x)) qui ins`ere un element x dans
la le F,
la fonction deler(F) (DeQueue(F)) qui retourne le premier element
insere ou un message derreur si la le est vide.
Toute implementation dune pile ou dune le necessite limplementation
des fonctions correspondantes.
Implementation dune pile par un tableau. Une pile peut etre representee
par les premiers elements dun tableau de taille xe et par un entier indi-
quant lindice du dernier element insere. La dimension du tableau etant xee,
il est necessaire dimplanter aussi une fonction booleenne PilePleine ? qui
28 CHAPITRE 3. STRUCTURES LIN
EAIRES
indiquera si la pile est pleine ou non. En C, cela peut se programmer de la
facon suivante :
/* Declarations */
#define TAILLE_MAX 1000 // Nombre maximal delements dans la pile
typedef int ELEMENT; /* pour une pile dentiers */
typedef struct {
ELEMENT elts[TAILLE_MAX];
int nbElts;} // nombre delements de la pile
PILE;
PILE p;
/* Initialisation */
[Link]=0;
/* retourne 1 si la pile p est vide, 0 sinon */
char pileVide(PILE p){
return ([Link]==0);
}
/* retourne 1 si la pile p est pleine, 0 sinon */
char pilePleine(PILE p){
return ([Link]==TAILLE_MAX);
}
/* empile lelement x sur la pile p, si celle-ci nest pas pleine */
void empiler(PILE *p, ELEMENT x){
assert(!pilePleine(*p));
p->elts[p->nbElts++]=x;
}
/* retourne lelement x au sommet de la pile p, si celle-ci nest pas vide */
ELEMENT depiler(PILE *p){
assert(!pileVide(*p));
return p->elts[--p->nbElts];
}
3.3. PILES ET FILES 29
Implementation dune le par un tableau. Une le peut etre representee
par les elements consecutifs dun tableau (circulaire) de taille xe et par
deux entiers indiquant lindice du premier element insere et le nombre to-
tal delements inseres. La dimension du tableau etant xee, il est necessaire
dimplanter une fonction booleenne FilePleine ? qui indiquera si la le est
pleine ou non. En C, cela peut se programmer de la facon suivante :
/* Declarations */
#define TAILLE_MAX 1000 // Nombre maximal delements dans la file
typedef int ELEMENT; /* pour une pile dentiers */
typedef struct {
ELEMENT elts[TAILLE_MAX];
int indPremierElt; // indice du premier element
int nbElts; // nombre delements
} FiLE;
FILE f;
/* Initialisation */
[Link]=[Link]=0;
/* retourne 1 si la file f est vide, 0 sinon */
char fileVide(FiLE f){
return ([Link]==0);
}
/* retourne 1 si la file f est pleine, 0 sinon */
char filePleine(FiLE f){
return ([Link]==TAILLE_MAX);
}
/* enfile lelement x dans la file f, si celle-ci nest pas pleine */
void enfiler(FiLE *f, ELEMENT x){
assert(!filePleine(*f));
f->elts[(f->indPremierElt+f->nbElts++) % TAILLE_MAX]=x;
}
/* retourne le premier element de la file f, si elle nest pas vide */
30 CHAPITRE 3. STRUCTURES LIN
EAIRES
ELEMENT defiler(FiLE *f){
assert(!fileVide(*f));
ELEMENT x=f->elts[f->indPremierElt];
f->nbElts--;
f->indPremierElt=(f->indPremierElt+1)%TAILLE_MAX;
return x;
}
Exercice 4 La notation post-xee des expressions arithmetiques, appelee
encore notation polonaise inversee, consiste `a ecrire les operandes avant les
operateurs. Lexpression ((3+(54)2) secrira par exemple 3 5 4 + 2 .
Aucune parenth`ese nest necessaire et levaluation dune telle expression se
fait simplement au moyen de lalgorithme suivant :
les termes sont parcourus de gauche `a droite ;
si le terme courant est un nombre, il est empile ;
si le terme courant est un operateur, les deux derniers nombres empiles
sont depiles, loperateur leur est applique et le resultat est empile ;
lorsque lexpression est totalement parcourue, la pile ne contient plus
quun seul element : le resultat de levaluation.
Ecrivez un programme qui evalue une expression `a partir de sa notation
post-xee (on supposera, pour simplier, que les operandes sont compris
entre 0 et 9).
Exercice 5 Quelles structures de listes chanees conviennent-elles le mieux
pour implementer un pile ? une liste ? Implementez ces structures en C, ainsi
que les fonctions correspondantes, au moyen de listes chanees.
Exercice 6 Calcul de lelement majoritaire (3`eme methode : les deux
premi`eres ont ete decrites dans des exercices de la section precedente).
Supposons que lon ait `a determiner sil existe une couleur majoritaire
parmi n balles colorees. On dispose dune etag`ere pour poser les balles les
unes `a cote des autres et dune corbeille pouvant contenir autant de balles
que necessaire. La methode se deroule en deux phases :
phase 1 : Prendre les balles les unes apr`es les autres en les mettant soit
sur letag`ere, si la derni`ere balle posee sur letag`ere est de couleur dierente,
soit dans la corbeille dans le cas contraire. Dans le premier cas on posera
ensuite sur letag`ere une balle prise au hasard dans la corbeille (sil y en a).
phase 2 : Soit C la couleur de la derni`ere balle posee sur letag`ere. On
jette les balles (pas dans la corbeille, dans la poubelle ! ) de letag`ere les unes
3.4. TABLES DE HACHAGE 31
apr`es les autres en commen cant par les derni`eres de la fa con suivante : (1)
si la balle `a jeter est de la couleur C et si elle a une voisine sur letag`ere, les
jeter toutes les deux, (2) si la derni`ere balle sur letag`ere nest pas de couleur
C, la jeter et jeter aussi une balle de la corbeille si elle nest pas vide, dans
le cas contraire on peut sarreter, il ny a pas delement majoritaire. Une
fois le processus termine, on regarde le contenu de la corbeille apr`es y avoir
mis eventuellement la deni`ere boule restant sur letag`ere : si la corbeille est
vide cest quil ny a pas de couleur majoritaire, sinon cest que la couleur
majoritaire est C.
1. Montrez qu`a tout moment de la phase 1, les balles de la corbeille, sil
y en a, sont toutes de la couleur de la derni`ere balle posee sur letag`ere.
En deduire qualors sil y a une couleur dominante cest cette couleur
la et que lalgorithme est donc correct.
2. Ecrivez lalgorithme en utilisant des piles. Quelle est la complexite
dans le pire des cas (en nombre de comparaisons de couleurs) ?
3.4 Tables de hachage
Les dictionnaires ou tableaux associatifs sont des structures de donnees
composees delements de la forme (cle,valeur) o` u chaque cle determine au
plus une valeur.
On peut par exemple considerer le dictionnaire compose du numero dun
etudiant inscrit `a luniversite dAix-Marseille et de son dossier, le diction-
naire compose dun numero de securite sociale et de lassure correspondant
ou du dictionnaire compose des mots du fran cais et de leur denition.
Les operations de base associees `a un dictionnaire sont : linsertion dun
nouvel element, la recherche dun element et la suppression dun element.
Lorsque les cles sont des entiers appartenant `a lintervalle [0 . . . n 1] (on
peut toujours se ramener `a ce cas) et lorsque n nest pas trop grand, les
dictionnaires peuvent etre implementes par des tableaux T `a adressage di-
rect dans lesquels les cles ne correspondant `a aucune valeur sont associees
conventionnellement `a une valeur NIL : en eet, inserer un element (c, x),
rechercher si element x de cle c est present ou supprimer lelement (c, x) se
fait en temps constant. Mais le cas le plus frequent est celui o` u le nombre
de valeurs renseignees est tr`es petit par rapport `a n : voir les exemples cites
precedemment.
32 CHAPITRE 3. STRUCTURES LIN
EAIRES
Si lon represente les n elements par une liste chanee, chaque operation
de base seectue en temps lineaire (par rapport `a n) : on souhaiterait pou-
voir les realiser en temps constant.
Les tables de hachage (hash tables) sont des structures de donnees qui
generalisent les tableaux au cas o` u lensemble U des cles possibles est gigan-
tesque par rapport au nombre de valeurs `a stocker. Lidee de base consiste
`a introduire une fonction de hachage
h : U [0 . . . m1]
telle que m soit de lordre du nombre de valeurs `a stocker et telle quen pra-
tique, le nombre de collisions, cest-`a-dire le nombre de cas o` u deux elements
distincts du dictionnaire (c, x) et (c
, x
) soit le plus
petit possible. Si lon pouvait assurer quil ny a pas de collisions, on pourrait
implementer le dictionnaire par un tableau T, chaque element (c, x) etant
represente par lelement T(h(c)). Mais il est en general impossible deviter
toute collision.
Deux questions doivent etre donc resolues :
1. comment gerer les collisions ?
2. comment denir une fonction de hachage minimisant le nombre de
collisions ?
3.4.1 Gestion des collisions par chanage externe
On appelle h(c) la position de lelement (c, x). Une collision correspond
donc `a deux elements du dictionnaire possedant la meme position. On choisit
de representer les elements ayant la meme position par une liste (simplement
ou doublement) chanee : T[i] pointe alors vers les elements dont la position
est i. Inserer, supprimer ou rechercher un element (c, x) est realise par les
operations correspondantes dans la liste chanee T(h(c)).
Le pire des cas est celui o` u tous les elements du dictionnaire occupent la
meme position! Dans ce cas, on est ramene au cas du stockage du diction-
naire dans une liste chanee et les operations de base seectuent en temps
lineaire. Mais si les cles se repartissent `a peu pr`es equitablement entre les
dierentes positions et si le nombre de position est de lordre du nombre
delement `a stocker, le nombre delements de chaque liste chanee est borne
par une constante et les operations de base peuvent donc etre realisees en
temps constant. Voir ci-dessous les algorithmes dinsertion et de recherche
dun element.
3.4. TABLES DE HACHAGE 33
0
1
2
3
4
5
table
e3 e7 e2
h(cl(e3)) = h(cl(e7)) = h(cl(2))
liste des objets
Figure 3.6 Gestion des collisions par chanage externe.
Algorithme 7: insertionTableHachage
entree : Table de Hachage T, element e de cle c
resultat : e est insere dans T
debut
Inserer e en tete de la liste chainee T(h(c)).
n
Fonction rechercheTableHachage(T,c)
entree : Table de Hachage T, cle c
sortie : un pointeur vers lelement de cle c ou NIL si lelement est
absent
debut
p = T[h(c)]
tant que p ,= NIL ET p ne pointe pas vers lelement de cle c
faire
p [Link]
ntq
retourner p
n
3.4.2 Gestion des collisions par adressage ouvert
La gestion des collisions par adressage ouvert consiste `a utiliser la table
de hachage elle-meme pour stocker les elements. Pour cela, on utilise une
34 CHAPITRE 3. STRUCTURES LIN
EAIRES
fonction de hachage modiee
h : U [0 . . . m1] [0 . . . m1]
telle que pour toute cle c, h(c, 0), . . . , h(c, m1) realise une permutation de
[0 . . . m1].
Pour inserer un nouvel un element e de cle c, on cherche le premier indice
i tel que T[h(c, i)] soit libre et on insere e dans T[h(c, i)] : toutes les cases
du tableau peuvent donc potentiellement recevoir tous les elements.
Pour savoir si un element de cle c est present dans T, on parcourt les
elements de la liste T[h(c, 0)], . . . , T[h(c, m1)] jusqu`a le trouver ou tomber
sur une case libre ou avoir parcouru toutes les cases du tableau.
En revanche, la suppression dun element est plus complexe car il ne
sut pas de vider la case qui le contient. En eet, supposons
que lelement de cle c soit stocke dans la case dindice h(c, i),
que lelement de cle c
, i
),
quil existe un indice j < i
, j) = h(c, i).
Si on lib`ere simplement la case T(h(c, i)), lelement de cle c
ne sera
plus trouve. Une solution consiste `a distinguer les cases libres des cases sup-
primees, dans lesquelles de nouvelles valeurs pourront etre inserees mais qui
ne devront pas etre considerees comme libres lors dune recherche delement.
Dapr`es louvrage de reference, la gestion des collisions par chanage externe
est plus souvent utilisee lorsquil doit y avoir des suppressions.
3.4.3 Fonctions de hachage
Une bonne fonction de hachage doit satisfaire la condition suivante :
les cles des elements du dictionnaire doivent se repartir (`a peu pr`es) uni-
formement entre les dierentes positions.
Des informations sur la distribution des cles peuvent permettre de denir
une fonction de hachage optimale. Cest par exemple le cas lorsquon sait que
les cles sont des nombres reels independamment et uniformement distribues
dans lintervalle [0, 1[, on peut utiliser la fonction h(c) = mc|.
En labsence dinformation, on cherche `a denir des fonctions de ha-
chage dependant le moins possible des donnees. On decrit ci-dessous deux
methodes courantes lorsque les cles sont des entiers, cas auquel on peut
toujours se ramener.
Methode par division On denit
h(c) = c mod m.
3.4. TABLES DE HACHAGE 35
La position de la cle c est son reste dans la division par m. Cest un methode
simple qui peut donner des resultats peu satisfaisants pour certaines valeurs
de m. Par exemple, si m = 2
p
est une puissance de 2, la position dune cle
ne depend que de ses p derniers bits ; si ceux-ci ne sont pas uniformement
repartis, la fonction de hachage correspondante ne sera pas uniforme non
plus.
En pratique, on recommande de choisir pour m un nombre premier pas
trop proche dune puissance de 2.
Methode par multiplication On denit
h(c) = mcA|
o` u A est un nombre reel tel que 0 < A < 1 et o` u x designe la partie
fractionnaire de x, cest-`a-dire x x|.
Cette methode est plus robuste que la precedente au choix des valeurs de
A et m. On choisit souvent m egal `a une puissance de 2 et Donald Knuth
1
sugg`ere de prendre A = (
: U[0 . . . m1]
[0 . . . m1] par
h
EAIRES
le sondage quadratique (quadratic probing) : `a partir dune fonction
de hachage simple h : U [0 . . . m 1], on denit la fonction h
: U
[0 . . . m1] [0 . . . m1] par
h
(c, i) = h(c) + ai + bi
2
mod m
o` u a et b sont des constantes auxiliaires. Voir dans un exercice ci-dessous un
exemple de choix de ces constantes lorsque m est une puissance de 2.
double hachage (double probing) : `a partir de deux fonctions de
hachage simples h
1
, h
2
: U [0 . . . m 1], on denit la fonction h
:
U [0 . . . m1] [0 . . . m1] par
h
(c, i) = h
1
(c) + ih
2
(c) mod m.
Pour que h
= (s
g
, . . . , s
q
) et S
= (s
q
, . . . , s
d
) telles que les
elements les plus petits sont dans la premi`ere et les elements les plus grands
dans la seconde. En appliquant recursivement le tri sur les suites S
et S
n
e
n
n log n n log e
et donc la hauteur minimale de larbre est de lordre de n log n.
44 CHAPITRE 4. ALGORITHMES DE TRIS
On en conclut quaucun tri par comparaison ne peut avoir une complexite
meilleure que O(n log n).
4.4 Tri par tas.
Un tas (heap en anglais) ou le de priorite (priority queue) est un arbre bi-
naire etiquete presque compl`etement equilibre : tous ses niveaux sont remplis
sauf peut-etre le dernier, qui est rempli `a partir de la gauche jusqu`a un cer-
tain noeud. On denit ensuite une propriete sur les tas : chaque noeud est
dans une relation dordre xee avec ses ls. En general on consid`ere des
tas dans lesquels chaque noeud a une valeur plus petite que celles de ses
ls. Pour le tri par tas on utilise des arbres maximiers (max-heap), cest-`a-
dire des tas dans lesquels chaque noeud porte une valeur plus grande que
celles de ses ls. La valeur la plus grande du tas se trouve donc `a la racine.
Les operations et les techniques presentees dans ce chapitre pour des arbres
maximiers sappliquent de la meme facon `a des tas bases sur lordre inverse.
Les operations essentielles sur les tas sont :
la construction du tas,
lextraction du maximum,
lajout dune nouvelle valeur,
la modication de la valeur dun noeud.
Habituellement les tas sont des arbres binaires representes dans des tableaux.
Les valeurs du tas sont stockees dans les premi`eres cases du tableau. Si le
tas est compose de n elements, ces derniers apparaissent donc aux indices
0, 1,. . . ,n1. La racine du tas gure dans la case dindice 0. La disposition
des elements du tas dans le tableau correspond `a un parcours de larbre par
niveau, en partant de la racine et de gauche `a droite.
4.4. TRI PAR TAS. 45
2
hauteur log n
7
1 5
11
3
8 4 2
7
13
10
0
2
3
1
4 5 6
9 8
Le ls gauche du noeud qui gure `a lindice i, sil existe, se trouve `a
lindice FilsG(i) = 2 i + 1, et son ls droit, sil existe, se trouve `a lindice
FilsD(i) = 2 i + 2. Inversement, le p`ere du noeud dindice i non nul se
trouve `a lindice
i1
2
|.
n1
5 1 4 8 2 0 5
2 1 3 4 5 7 8 9 10 11 0
6
fils droit
fils gauche
3 10 7 11 13
Cette disposition entrane que larbre est forcement equilibre (i.e. toutes
ses branches ont la meme longueur `a un element pr`es), et les plus longues
branches sont `a gauche. La hauteur dun tas, i.e. son nombre de niveaux,
contenant n elements est donc log
2
n| +1 (le nombre de fois que lon peut
diviser n par 2 avant dobtenir 1). Si le dernier noeud du tas se trouve `a
la position n 1, son p`ere se trouve `a la position
n
2
1|. Cest le dernier
noeud qui a au moins un ls. Les feuilles de larbre se trouvent donc entre la
position
n
2
et la position n 1 dans le tableau puisquil ny a pas de feuilles
avant le dernier p`ere.
Loperation Entasser est une operation de base sur les tas. Elle est utilisee
notamment pour la construction des tas ou encore pour lextraction de la
46 CHAPITRE 4. ALGORITHMES DE TRIS
valeur maximale. Elle consiste `a reconstruire le tas lorsque seule la racine
viole (eventuellement) la propriete de superiorite entre un noeud et ses ls,
en faisant descendre dans larbre lelement fautif par des echanges successifs.
14
1
2 8 4
1
2 8 4
9
7
5 10
14
13 1
2 8 4
7
5
9
10
13
7
5
13
9 10
14
Fonction Entasser(i,T,n)
entree : T est un tas ; le ls gauche et le ls droit du noeud dindice i
verient la propriete Max-heap ; ce nest pas forcement le
cas du noeud dindice i.
sortie : La propriete max-heap est veriee par le noeud dindice i.
debut
iMax i
si (FilsG(i) < n) ET (T[FilsG(i)] > T[iMax]) alors
iMax := FilsG(i)
si (FilsD(i) < n) ET (T[FilsD(i)] > T[iMax]) alors
iMax := FilsD(i)
si (iMax ,= i) alors
Echanger T[i] et T[iMax]
Entasser(iMax,T,n)
La fonction proc`ede de la fa con suivante : si lon na pas atteint une feuille ou
que la valeur du noeud courant dindice i viole la propriete de superiorite des
p`eres sur leurs ls, on echange cette valeur avec la plus grande des valeurs
des ls. De cette facon le noeud dindice i aura une valeur plus grande que
les valeurs de ses ls. On reit`ere loperation tant que la propriete de tas nest
pas retablie.
La fonction Entasser a un co ut en O(log
2
n) puisque, dans le pire des
cas, il faudra parcourir une branche enti`erement.
Extraction de la valeur maximale. La valeur maximale dun tas qui
verie la propriete Max-heap est `a la racine de larbre. Pour un tas de taille
4.4. TRI PAR TAS. 47
n stocke dans le tableau T cest la valeur T[0] si n est non nul. Lextraction
de la valeur maximale consiste ` a recopier en T[0] la derni`ere valeur du tas
T[n1], `a decrementer la taille du tas, puis `a appliquer loperation Entasser
`a la racine du tas, an que la nouvelle valeur de la racine prenne sa place.
fonction ExtraireLeMax(T, n)
max := T[0],
T[0] := T[n 1],
Entasser(0, T, n 1),
renvoyer max, T, n 1),
n fonction
Inserer une nouvelle valeur dans un tas consiste `a ajouter la valeur `a la
n du tas, en derni`ere position dans le tableau, puis `a la faire remonter dans
le tas en lechangeant avec son p`ere tant quelle ne se trouve pas `a la racine
et que la valeur de son p`ere lui est inferieure. Loperation dinsertion nest
pas utile pour le tri par tas.
Principe general du tri par tas. Supposons que lon ait `a trier une
suite de n valeurs stockee dans un tableau T. On commence par construire
un tas dans T avec les valeurs de la suite. Ensuite, tant que le tas nest pas
vide on rep`ete lextraction de la valeur maximale du tas. Chaque fois, la
valeur extraite est stockee dans le tableau immediatement apr`es les valeurs
du tas. Lorsque le processus se termine on a donc la suite des valeurs triee
dans le tableau T.
Construction du tas.
Les valeurs de la suite `a trier sont stockees dans le tableau T. La procedure
consiste `a parcourir les noeuds qui ont des ls et `a leur appliquer loperation
Entasser, en commen cant par les noeuds qui sont `a la plus grande profondeur
dans larbre. Il sut donc de parcourir les noeuds dans lordre decroissant
des indices et en partant du dernier noeud qui a des ls, le noeud din-
dice (n/2)| 1. Lordre dans lequel les noeuds sont traites garantit que les
sous-arbres sont des tas.
fonction ConstruireUnTas(T, n)
pour i := n/2 1 jusqu`a 0 faire
Entasser(i, T, n),
n fonction
48 CHAPITRE 4. ALGORITHMES DE TRIS
Complexite de la construction. De facon evident la complexite est au
plus O(n log n). En eet la hauteur du tas est log n et on eectue n/2 fois
loperation Entasser. En fait le co ut de la construction est O(n). La fonction
eectue un grand nombre dentassements sur des arbres de petites hauteur
(pour les noeuds les plus profonds), et tr`es peu dentassements sur la hauteur
du tas. Considerons pour simplier un tas complet, cest-`a-dire dans lequel
toutes les branches ont la meme longueur. On denombre
n
2
noeuds `a la hau-
teur 0 (les feuilles),
n
2
2
noeuds `a la hauteur 1,
n
2
3
noeuds `a la hauteur 2,. . . ,
On a donc
n
2
h+1
noeuds `a la hauteur h, pour chacun desquels on eectuera
au plus h echanges dans loperation Entasser. Le co ut de la construction du
tas est donc borne par
log n
h=1
n
2
h+1
| h n
log n
h=1
h
2
h
Puisque
i=0
i x
i
=
x
(1 x)
2
on a
h=0
h
2
h
=
1/2
(1 1/2)
2
= 2
et donc le nombre dechanges est borne par
n
log n
h=0
h
2
h
2 n = O(n)
Tri par tas.
On construit un tas. On utilise le fait que le premier element du tas est le
plus grand : autant de fois quil y a delements, on extrait le premier du tas et
on reconstruit le tas avec les elements restants. Enlever le premier element
consiste simplement `a lechanger avec le dernier du tas et `a decrementer
la taille du tas. On retablit la propriete de tas en appliquant loperation
Entasser sur ce premier element.
4.5. TRI PAR D
EFINITIONS ET TERMINOLOGIE 57
Lucie Matto
Paul
Victor
Naomi
Ginette
Georges
Lilly Tom
Lon Mlanie Tho
Arbre lexicographique. Un arbre lexicographique, ou arbre en parties
communes, ou dictionnaire, represente un ensemble de mots. Les prexes
communs `a plusieurs mots apparaissent une seule fois dans larbre, ce qui
se traduit par un gain despace memoire. De plus la recherche dun mot est
assez ecace, puisquil sut de parcourir une branche de larbre en partant
de la racine, en cherchant `a chaque niveau parmi les ls du noeud courant
la lettre du mot de rang correspondant.
e
e
l
t
r e
o
t
i
l s
m
a
e
i
p
i c
n e
.
male
mie
mite
pic
pile
pis
port
porte
main
5.2 Denitions et terminologie
Denition. Un arbre est un ensemble organise de noeuds dans lequel
chaque noeud a un p`ere et un seul, sauf un noeud que lon appelle la racine.
Si le noeud p est le p`ere du noeud f, nous dirons que f est un ls de p, et
si le noeud p na pas de ls nous dirons que cest une feuille. Chaque noeud
porte une etiquette ou valeur ou cle. On a lhabitude, lorsquon dessine un
58CHAPITRE 5. ARBRES BINAIRES DE RECHERCHE, ARBRES LEXICOGRAPHIQUES
arbre, de le representer avec la tete en bas, cest-`a-dire que la racine est tout
en haut, et les noeuds ls sont representes en-dessous du noeud p`ere.
racine
feuilles
Un noeud est deni par son etiquette et ses sous-arbres. On peut donc
representer un arbre par un n-uplet e, a
1
, . . . , a
k
) dans lesquel e est letiquette
portee par le noeud, et a
1
, . . . , a
k
sont ses sous-arbres. Par exemple larbre
correspondant `a lexpression arithmetique (y/2t)(75+z) sera represente
par
, , /, y), 2)), t)), +, 75), z)))
On distingue les arbres binaires des arbres generaux. Leur particula-
rite est que les ls sont singularises : chaque noeud a un ls gauche et un
ls droit. Lun comme lautre peut etre un arbre vide, que lon notera ).
Lecriture dun arbre sen trouve modiee, puisquun noeud a toujours deux
ls. En reprenant lexpression arithmetique precedente, larbre binaire qui
la represente secrit
, , /, y, ), )), 2, ), ))), t, ), ))), +, 75, ), )), z, ), ))))
On utilise pour les arbres une terminologie inspiree des liens de parente :
les descendants dun noeud p sont les noeuds qui apparaissent dans
ses sous-arbres,
un ancetre dun noeud p est soit son p`ere, soit un ancetre de son p`ere,
le chemin qui relie un noeud `a la racine est constitue de tous ses
ancetres (cest-` a-dire de son p`ere et des noeuds du chemin qui relie
son p`ere `a la racine),
un fr`ere dun noeud p est un ls du p`ere de p, et qui nest pas p.
5.2. D
EFINITIONS ET TERMINOLOGIE 59
hauteur=5
descendant
ancetre
niveaux
profondeur=2
noeud de degr 3
racine
frre
chemin la racine
Les noeuds dun arbre se repartissent par niveaux : le premier niveau
(par convention ce sera le niveau 0) contient la racine seulement, le deuxi`eme
niveau contient les deux ls de la racine,. . . , les noeuds du niveau k sont les
ls des noeuds du niveau k 1,. . . . La hauteur dun arbre est le nombre de
niveaux de ses noeuds. Cest donc aussi le nombre de noeuds qui jalonnent
la branche la plus longue. Attention, la denition de la hauteur varie en
fonction des auteurs. Pour certains la hauteur dun arbre contenant un seul
noeud est 0.
Arbres binaires : hauteur, nombre de noeuds et nombre de
feuilles.
Un arbre binaire est complet si toutes ses branches ont la meme longueur
et tous ses noeuds qui ne sont pas des feuilles ont deux ls. Soit A un arbre
binaire complet. Le nombre de noeuds de A au niveau 0 est 1, le nombre de
noeuds au niveau 1 est 2,. . . , et le nombre de noeuds au niveau p est donc
2
p
. En particulier le nombre de feuilles est donc 2
h1
si h est le nombre de
niveaux.
16
8
4
2
1
racine
n
o
m
b
r
e
d
e
n
o
e
u
d
s
60CHAPITRE 5. ARBRES BINAIRES DE RECHERCHE, ARBRES LEXICOGRAPHIQUES
Le nombre total de noeuds dun arbre binaire complet de h niveaux est
h1
i=0
2
i
= 2
h
1
On en deduit que la hauteur ht(A) (le nombre de niveaux) dun arbre
binaire A contenant n noeuds est au moins egale `a
log
2
n| + 1
Preuve. Si A est arbre de hauteur h et comportant n noeuds, pour tout
entier m, on a : h m1 n < 2
m1
. Par contraposee, on a : n 2
m1
EN
ERAUX OU N-AIRES 69
de substituer la valeur de q `a la valeur de p et de decrocher le noeud q.
Puisque le noeud q a la valeur la plus grande dans le ls gauche, il na
donc pas de ls droit, et peut etre decroche comme on la fait dans les cas
precedents.
5.5 Arbres generaux ou n-aires
Un arbre n-aire est un arbre dans lequel le nombre de ls dun noeud
nest pas limite. On les represente avec des pointeurs, en liant les ls dun
meme noeud entre eux, comme dans une liste chainee. Ainsi, `a partir dun
noeud quelconque, on acc`ede `a son ls aine (la liste de ses ls) et `a son fr`ere.
racine
frres
fils ain
. . . . .
a a a
2 k 1
On denit en C les arbres n-aires de la facon suivante :
typedef struct noeud * ARBRE;
struct noeud
{
TYPE_VALEUR val;
ARBRE fa, fr;
};
5.6 Exercices
Les arbres lexicographiques, ou arbres en parties communes, ou diction-
naires, sont un exemple darbres n-aires. Ils sont utilises pour representer
70CHAPITRE 5. ARBRES BINAIRES DE RECHERCHE, ARBRES LEXICOGRAPHIQUES
un ensemble de mots, en optimisant lespace memoire occupe. Le principe
consiste `a representer une seule fois les prexes communs `a plusieurs mots.
Pour eliminer toute ambiguite lorsquun mot est le prexe dun autre, on
signale les ns des mots `a laide dun caract`ere particulier (* dans la gure
ci-dessous, mais en C le caract`ere de n de chane 0 est tout `a fait
adapte).
*
X
* * *
L
A D
*
I
R
R U E
*
T
racine
*
Question 1. Ecrivez les fonctions dinsertion et de recherche dun mot
dans un arbre lexicographique.
Question 2. Ecrivez une fonction qui ache tous les mots representes
dans larbre.
Question 3. Ecrivez une fonction qui permet de supprimer un mot de
larbre.
Chapitre 6
Programmation dynamique
6.1 Programmation dynamique et probl`emes dop-
timisation
La programmation dynamique est une methodologie generale pour conce-
voir des algorithmes qui permettent de resoudre ecacement certains probl`emes
doptimisation. Un probl`eme doptimisation admet un grand nombre de so-
lutions. Chaque solution a une certaine valeur, et on veut identier une
solution dont la valeur est optimale (minimale ou maximale). Trouver un
plus court chemin pour aller dun point `a un autre dans un reseau de trans-
port est un probl`eme doptimisation
La conception dun algorithme de programmation dynamique se decompose
en quatre etapes.
1. Caracterisation de la structure dune solution optimale.
2. Denition recursive de la valeur de la solution optimale.
3. Calcul ascendant de la valeur de la solution optimale.
4. Construction de la solution optimale `a partir des informations obtenues
`a letape precedente.
Letape 4 peut etre omise si on a seulement besoin de la valeur de la solution
optimale et non de la solution elle meme.
Les sections suivantes decrivent lutilisation de la programmation dyna-
mique pour resoudre en temps polynomial trois probl`emes doptimisation :
le calcul dun parcours optimal dans un atelier de montage, le calcul dune
chane de mulplications matricielles avec un nombre minimum doperations
71
72 CHAPITRE 6. PROGRAMMATION DYNAMIQUE
scalaires, le calcul dun arbre binaire de recherche optimal. Ensuite, nous
mettrons en evidence deux caracteristiques que doit posseder un probl`eme
doptimisation pour que la programmation dynamique soit applicable.
6.2 Ordonnancement optimal dune chane de mon-
tage
Un constructeur automobile poss`ede un atelier avec deux chanes de
montage comportant chacune n postes de montages. Chaque vehicule doit
passer par les n postes dans lordre. Le constructeur cherche `a determiner
quels sont les postes ` a selectionner sur la chane 1 et sur la chane 2 pour
minimiser le delai de transit dune voiture `a travers latelier. Les donnees du
probl`eme doptimisation quil doit resoudre sont les suivantes. Pour i = 1, 2
et j = 1, . . . , n, on note S
i,j
le j`eme poste de la chane i, e
i
le temps dentree
dun vehicule sur la chane i, a
i,j
le temps de montage pour le poste j sur
la chane i, t
i,j
le temps de transfert dun vehicule de la chane i vers lautre
chane apr`es le poste S
i,j
et nallement x
i
le temps de sortie dun vehicule
de la chane i (voir Figure 6.1a).
Chaque solution de ce probl`eme doptimisation est denie par le sous-
ensemble de postes de la chane 1 utilises (les postes restant sont choisis
dans la chane 2). Il y a donc 2
n
solutions possibles, i.e. le nombre de sous-
ensembles dun ensemble `a n elements. Par consequent, lapproche nave
consistant `a considerer tous les chemins possibles est inecace. La program-
mation dynamique permet de resoudre ce probl`eme ecacement.
La premi`ere etape consiste `a identier des sous-probl`emes dont les solu-
tions optimales vont nous permettre de reconstituer une solution optimale
du probl`eme initial. Les sous-probl`emes `a considerer ici consistent `a calculer
un itineraire optimal jusquau poste S
i,j
pour i = 1, 2 et j = 1, . . . , n. Par
exemple, considerons un itineraire optimal jusquau poste S
1,j
. Si j = 1, il
ny a quun seul chemin possible. Pour j = 2, . . . , n, il y a deux possibilites.
Un itineraire optimal jusqu`a S
1,j
est ou bien, un itineraire optimal jusqu`a
S
1,j1
suivi du poste S
1,j
, ou bien, un itineraire optimal jusqu`a S
2,j1
suivi
dun changement de chane et du poste S
1,j
.
La deuxi`eme etape consiste `a denir la valeur optimale de mani`ere recursive
`a partir des valeurs des solutions optimales des sous-probl`emes. Soit f
i
[j] le
delai optimal jusqu`a S
i,j
et f
INE DE MONTAGE73
(a)
j
(b)
9 17 19 32
29 29 22 15 13
1 2 1 1
2 2 1 l
2
[j]
l
1
[j] f
1
[j]
f
2
[j]
j 1 2 3 4 5 2 3 4 5
Entree
S
2,2
S
2,3
S
2,4 S
2,5
Sortie
S
1,5
S
1,4
S
1,3
S
1,1
S
1,2
23
1
f
= 32 l
= 2
3
6 8 2 4 9
8
4 7 7 3
S
2,1
5
2 1 5 3
2
3
3 2 1 2
Figure 6.1 Exemple dinstance du probl`eme dordonnacement optimal
dune chane de montage : (a) les donnees du probl`eme et (b) les tables de
programmation dynamique.
consequent, on a
f
= min(f
1
[n] + x
1
, f
2
[n] + x
2
).
Pour le poste 1 de chaque chane, il ny a quun itineraire possible :
f
1
[1] = e
1
+ a
1,1
f
2
[1] = e
2
+ a
2,1
Pour le poste j = 2, . . . , n de la chane 1, il y a deux possibilites :
f
1
[j] = min(f
1
[j 1] + a
1,j
, f
2
[j 1] + t
2,j1
+ a
1,j
), (6.1)
et symetriquement
f
2
[j] = min(f
2
[j 1] + a
2,j
, f
1
[j 1] + t
1,j1
+ a
2,j
). (6.2)
Les f
i
[j] sont les valeurs des solutions optimales des sous-probl`emes. Pour
pouvoir reconstruire les solutions optimales elles-memes, on denit l
i
[j] le
numero de la chane (1 ou 2) dont le poste j 1 est utilise par un chemin
74 CHAPITRE 6. PROGRAMMATION DYNAMIQUE
optimal jusquau poste S
i,j
pour j = 2, . . . , n, (l
i
[1] nest pas deni car aucun
poste ne vient avant le poste 1).
La troisi`eme etape consiste `a concevoir un algorithme qui calcule en
temps polynomial les valeurs f
i
[j] des solutions optimales et les informations
l
i
[j] necessaires `a la construction de ces solutions. Lalgorithme suivant fait
ce travail en O(n) en utilisant les formules recursives (6.1) et (6.2). Il revient
`a remplir les tables de la Figure 6.1b de la gauche vers la droite.
Plus-Rapide-Chemin(a, t, e, x, n)
1. f
1
[1] e
1
+ a
1,1
2. f
2
[1] e
2
+ a
2,1
3. pour j 2 `a n faire
4. si f
1
[j 1] f
2
[j 1] + t
2,j1
5. alors f
1
[j] f
1
[j 1] + a
1,j
6. l
1
[j] 1
7. sinon f
1
[j] f
2
[j 1] + t
2,j1
+ a
1,j
8. l
1
[j] 2
9. si f
2
[j 1] f
1
[j 1] + t
1,j1
10. alors f
2
[j] f
2
[j 1] + a
2,j
11. l
2
[j] 2
12. sinon f
2
[j] f
1
[j 1] + t
1,j1
+ a
2,j
13. l
2
[j] 1
14. si f
1
[n] + x
1
f
2
[n] + x
2
15. alors f
= f
1
[n] + x
1
16. l
= 1
17. sinon f
= f
2
[n] + x
2
18. l
= 2
Finalement, la quatri`eme et derni`ere etape consiste `a reconstruire une
solution optimale en utilisant les informations sauvegardees `a letape 3. La
procedure suivante ache les postes utilises par une solution optimale par
ordre decroissant de numero de poste.
Afficher-Postes(l, n)
1. i l
0 si i = j,
min
ik<j
m[i, k] + m[k + 1, j] + p
i1
p
k
p
j
si i < j.
(6.4)
Les valeurs m[i, j] donnent les co uts des solutions optimales des sous-probl`emes.
Pour reconstituer une solution optimale a posteriori, nous allons stocker dans
s[i, j] une valeur de k qui minimise m[i, k] +m[k, j] +p
i1
p
k
p
j
, i.e. telle que
m[i, j] = m[i, k] + m[k, j] + p
i1
p
k
p
j
.
6.3.3 Calcul du co ut optimal
Au point o` u nous en sommes, on peut ecrire facilement un programme
recursif base sur la relation de recurrence (6.4) pour calculer le co ut de la
solution optimale. Cependant, cet algorithme na pas une meilleure com-
plexite quun algorithme enumeratif brutal.
78 CHAPITRE 6. PROGRAMMATION DYNAMIQUE
En fait, on peut remarquer quil existe relativement peu de sous-probl`emes :
un pour chaque choix de i et j qui satisfasse 1 i j n, cest `a dire `a peu
pr`es n
2
(en fait, n(n + 1)/2 + n exactement). Un algorithme recursif peut
rencontrer plusieurs fois chacun de ces sous-probl`emes dans larbre des ap-
pels recursifs. Cette propriete est la deuxi`eme caracteristique des probl`emes
pour lesquels la programmation dynamique est applicable.
Au lieu de calculer la solution de la recurrence (6.4) recursivement, nous
allons eectuer la troisi`eme etape la troisi`eme etape dune approche de type
programmation dynamique en calculant le co ut optimal de facon ascendante.
Lalgorithme suivant remplit la table en commen cant par resoudre le
probl`eme de parenthesage sur les chanes de matrices les plus courtes et
en continuant par ordre croissant de longueur de chanes. Lequation de
recurrence (6.4) montre que lon peut calculer le co ut optimal pour une
chane en connaissant les co uts optimaux pour les chanes de longueurs
inferieures.
Ordre-Chane-Matrices(p)
1. n longueur(p)
2. pour i 1 `a n
3. faire m[i, i] 0
4. pour l 2 `a n
5. faire pour i 1 `a n l + 1
6. j i + l 1
7. m[i, j]
8. pour k i `a j 1
9. faire q m[i, k] + m[k + 1, j] + p
i1
p
k
p
j
10. si q < m[i, j]
11. alors m[i, j] q
12. s[i, j] k
13. retourner m et s
Cet algorithme est en O(n
3
). En eet, il contient trois boucles imbriquees
dans lesquelles chaque index peut prendre au plus n valeurs. De plus, le
traitement est eectue `a linterieur de ces boucles se fait en temps constant.
Par consequent, cet algorithme est beaucoup plus ecace que la methode
exponentielle qui consiste `a examiner chaque parenthesage.
6.4. ARBRE BINAIRE DE RECHERCHE OPTIMAL 79
6.3.4 Construction dune solution optimale
Lalgorithme que nous avons decrit precedemment donne le nombre mi-
nimum de multiplications scalaires necessaires pour calculer la chane de
produits matriciels mais ne montre pas directement comment multiplier ces
matrices en utilisant ce nombre minimum de multiplications. La derni`ere
etape dans notre approche de type programmation dynamique consiste `a
reconstruire une solution optimale `a partir des informations que nous avons
sauvegardees `a letape precedente.
Nous allons utiliser une table s[ , ] pour determiner la meilleure facon
de multiplier les matrices. Chaque entree s[i, j] de cette table contient la
valeur k telle quun parenthesage optimal separe le produit A
i
A
i+1
. . . A
j
entre A
k
et A
k+1
. Par consequent, nous savons que la derni`ere multipli-
cation matricielle dans la solution optimale que nous voulons reconstruire
est A
1..s[1,n]
A
s[1,n]+1..n
. Les multiplications precedentes peuvent etre recons-
tituees recursivement de la meme fa con. La procedure suivante ache le pa-
renthesage optimal du produit matriciel A
i..j
etant donnee la table s[ , ] cal-
culee `a letape precedente et les indices i et j. Pour calculer le parenthesage
complet, le premier appel de cette procedure sera Affichage-Parenth esage-
Optimal(s, 1, n).
Affichage-Parenth esage-Optimal(s, i, j)
1. si i = j
2. alors Acher A
i
3. sinon Acher (
4. Affichage-Parenth esage-Optimal(s, i, s[i, j])
5. Affichage-Parenth esage-Optimal(s, s[i, j] + 1, j)
6. Acher )
6.4 Arbre binaire de recherche optimal
Le probl`eme doptimisation que nous allons considerer dans cette section
consiste etant donne un ensemble de clefs a
1
, a
2
, . . . , a
n
et p
1
, p
2
, . . . , p
n
les
frequences de recherche de ces clefs, `a calculer un arbre binaire de recherche
qui permettent de minimiser le temps de recherche de ces clefs. En remar-
quant que le temps necessaire pour rechercher une clef est proportionnel `a
sa profondeur dans larbre binaire de recherche, on deduit que larbre bi-
naire de recherche que nous souhaitons identier doit minimiser la quantite
80 CHAPITRE 6. PROGRAMMATION DYNAMIQUE
suivante
co ut(T) =
n
i=1
p
i
(prof
T
(a
i
) + 1)
o` u prof
T
(a
i
) est la profondeur de la clef a
i
dans larbre T.
clefs a
1
a
2
a
3
a
4
a
5
frequences 20 40 12 16 12
a
2
a
1
co ut(T
1
) = 60 + 36 + 80 + 32 + 12 = 220
T
1
12
40 2 = 80
20 3 = 60
12 3 = 36
16 2 = 32
20 2 = 40
T
2
40
16 2 = 32
co ut(T
2
) = 36 + 36 + 40 + 32 + 40 = 184
a
3
a
4
a
5
a
1
a
2
a
4
a
5
a
3
12 3 = 36 12 3 = 36
Figure 6.2 Un exemple dinstance du probl`eme de larbre binaire de
recherche optimal avec deux arbres et leurs co uts respectifs.
Pour commencer, nous allons mettre en evidence la presence de sous-
structures optimales dans un arbre binaire de recherche (ABR) optimal.
Pour cela, nous allons introduire les notations suivantes :
T
i,j
un ABR optimal pour les clefs a
i+1
, . . . , a
j
;
w
i,j
= p
i+1
+. . . +p
j
la somme des frequences des clefs de larbre T
i,j
;
r
i,j
la racine de larbre T
i,j
;
c
i,j
le co ut de larbre T
i,j
;
T
i,i
larbre vide (c
i,i
= 0).
Considerons un ABR optimal T
i,j
et notons k lentier compris entre i +1
et j tel que la racine r
i,j
de larbre T
i,j
soit a
k
. Le sous-arbre gauche de
la racine a
k
est constitue des clefs a
i+1
, . . . , a
k1
. De plus, ce sous-arbre T
doit necessairement etre un ABR optimal sur cet ensemble de clefs car sil
existait un meilleur ABR T
dans T
i,j
on obtiendrait un arbre meilleur que T
i,j
, ce qui contredirait
loptimalite de T
i,j
. Le meme raisonnement sapplique au sous-arbre droit
et montre que larbre optimal T
i,j
est constitue dune racine a
k
dont le ls
gauche est la racine dun ABR optimal T
i,k1
et le ls droit est la racine
dun ABR optimal T
k,j
(voir Figure 6.4).
6.4. ARBRE BINAIRE DE RECHERCHE OPTIMAL 81
T
i,k1 T
k,j
a
k
Figure 6.3 Sous-structures optimales dans un ABR optimal
Voyons maintenant comment donner une denition recursive du co ut
dun ABR optimal. Si a
k
est la racine de T
ij
, on a :
c
i,j
= (c
i,k1
+ w
i,k1
) + p
k
+ (c
k,j
+ w
k,j
)
= (w
i,k1
+ p
k
+ w
k,j
) + c
i,k1
+ c
k,j
= w
i,j
+ c
i,k1
+ c
k,j
La premi`ere ligne sexplique par le fait que lorsque lon place larbre
T
i,k1
sous la racine a
k
, la profondeur de chacun de ces noeuds augmente
de un, donc globalement le co ut c
i,k1
de ce sous-arbre augmente de la
somme des frequences des clefs quil contient, cest `a dire w
i,k1
. Idem pour
la contribution du sous-arbre droit T
k,j
qui augmente de w
k,j
. Finalement,
il reste `a compter la contribution de a
k
qui est egale `a p
k
. La deuxi`eme ligne
est juste une reecriture de la premi`ere et la troisi`eme utilise simplement la
denition de w
i,j
.
Pour nir, on remarque que la valeur de k `a utiliser est celle qui minimise
le co ut c
i,k1
+ c
k,j
, ce qui donne la denition recursive suivante :
c
i,j
= w
i,j
+ min
k{i+1,...,j}
(c
i,k1
+ c
k,j
)
Lalgorithme suivant consiste `a resoudre les sous-probl`emes sur des sous-
ensembles de clefs de longueurs l croissantes, en utilisant la formule recursive
ci-dessus :
ABR-Optimal(p, n)
1. Pour i 0 `a n faire
2. w
i,i
0
3. c
i,i
0
4. Pour l 1 `a n faire
82 CHAPITRE 6. PROGRAMMATION DYNAMIQUE
5. Pour i 0 `a n l faire
6. j i + l
7. w
i,j
w
i,j1
+ p
j
8. Soit m la valeur de k telle que c
i,k1
+ c
k,j
soit minimum
9. c
i,j
= w
i,j
+ c
i,m1
+ c
m,j
10. r
i,j
m
Lalgorithme precedent permet dobtenir le co ut dun ABR optimal et
sauvegarde les informations necessaires pour le contruire grace `a lalgorithme
suivant.
Const-ABR-opt(i, j)
1. p CreerNoeud(r
i,j
, NULL, NULL)
2. Si i < r
i,j
1 alors
3. pls-gauche = Const-ABR-opt(i, r
i,j
1)
4. Si r
i,j
< j alors
5. pls-droit = Const-ABR-opt(r
i,j
, j)
6.5 Applicabilite de la programmation dynamique
Dans cette section, nous presentons deux caracteristiques que doit avoir
un probl`eme doptimisation pour que la programmation dynamique soit ap-
plicable : la presence de sous-structures optimales et le recouvrement des
sous-probl`emes.
6.5.1 Sous-structures optimales
La premi`ere etape dans une approche de type programmation dyna-
mique consiste `a identier la structure des solutions optimales. On dit que
le probl`eme presente une sous-structure optimale si une solution optimale
contient des solutions optimales de sous-probl`emes.
Cetait le cas dans les trois probl`emes precedents. Par exemple, chaque
parenthesage optimal de A
1
. . . A
n
etait obtenu comme le produit du pa-
renthesage optimal de A
1
. . . A
k
et de A
k+1
. . . A
n
pour un certain k et que
chaque ABR optimal peut etre obtenu en attachant `a une racine bien choisie
deux ABR optimaux pour des sous-probl`emes.
6.5. APPLICABILIT
E DE LA PROGRAMMATION DYNAMIQUE 83
6.5.2 Recouvrement des sous-probl`emes
Pour que la programmation dynamique soit applicable, il faut egalement
que lespace des sous-probl`emes soit susament petit. Dans le sens o` u
un algorithme recursif qui resoudrait le probl`eme rencontrerait les memes
sous-probl`emes plusieurs fois, au lieu de generer toujours de nouveaux sous-
probl`emes. Pour que la programmation dynamique permette de concevoir
un algorithme polynomial, il faut bien s ur que le nombre de sous-probl`emes
`a considerer soit polynomial.
84 CHAPITRE 6. PROGRAMMATION DYNAMIQUE
Chapitre 7
Graphes
85
86 CHAPITRE 7. GRAPHES
Chapitre 8
Annexes mathematiques
8.1 Les notations de Landau pour la comparaison
asymptotique des fonctions
On sinteresse le plus souvent `a la mani`ere dont la complexite T(n) dun
algorithme crot en fonction de n, plutot quaux valeurs de T pour des
valeurs particuli`eres de n. Autrement dit, on sinteresse au comportement
asymptotique de la fonction T au voisinage de linni et lon souhaite le
comparer aux comportements de fonctions de references : n, nlog n, n
2
, e
n
,
etc. Les notations de Landau permettent dexprimer ces comparaisons de
mani`ere simple.
Soit f, g : N R o` u g joue le role de la fonction de reference et f celui
de la fonction quon souhaite lui comparer.
On ecrit
f(n) = O(g(n)) si C, n
0
n n
0
f(n) Cg(n).
La fonction g majore f, `a partir dune certaine valeur, et `a une constante
multiplicative pr`es.
On ecrit
f(n) = (g(n)) si C, n
0
n n
0
f(n) Cg(n).
La fonction g minore f, `a partir dune certaine valeur, et `a une constante
multiplicative pr`es.
On ecrit
f(n) = (g(n)) si C
1
, C
2
, n
0
n n
0
C
1
g(n) f(n) C
2
g(n).
1
2 CHAPITRE 8. ANNEXES MATH
EMATIQUES
n
n
T(n) = O(f(n))
x
0
c f(n)
Figure 8.1 La notation O(f(n)).
n
0
T(n)
2
x
1
x c f(n)
c f(n)
n
Figure 8.2 La notation (f(n)).
8.2. COMPORTEMENT ASYMPTOTIQUE DES FONCTIONS D
EFINIES PAR R
ECURRENCE3
La fonction g decrit la croissance de f, `a partir dune certaine valeur, et
`a une constante multiplicative pr`es. On peut remarquer que
f(n) = (g(n)) ssi f(n) = O(g(n)) et f(n) = (g(n)).
Enn, on ecrit
f(n) = o(g(n)) si > 0 n
n n
f(n) g(n).
Si g ne prend que des valeurs non nulles, cela signie que lim
n
f(n)
g(n)
=
0.
Exemple 4
1. 3n
2
+ 5n 1 = O(n
2
) ; en eet, 3n
2
+ 5n 1 = 3n
2
(1 +
5
3n
1
3n
2
) et
5
3n
1
3n
2
1 d`es que n 2 ; on peut donc prendre C = 6 et N = 2.
2. n + sinn = (n) ;
3. n(2 log n + log log n 1) = (nlog n) ;
4. nlog n = o(n
2
).
8.2 Comportement asymptotique des fonctions denies
par recurrence
La complexite des algorithmes recursifs est naturellement decrite par
des equations de recurrence. Par exemple, lalgorithme de recherche dicho-
tomique conduit `a lequation
T(n) = T(
n
2
|) + c,
lalgorithme de tri par fusion conduit `a lequation
T(n) = 2 T(
n
2
|) + n
Theor`eme 1 Soit T : N R deni par la formule
T(n) = aT(i(n/b)) + f(n)
o` u a 1, b > 1 et f : N R et o` u i(n/b) est egal `a n/b| o` u `a n/b|.
1. Sil existe > 0 pour lequel f(n) = O(n
log
b
a
), alors T(n) = (n
log
b
a
).
2. Si f(n) = O(n
log
b
a
), alors T(n) = (n
log
b
a
log
2
n).
3. Sil existe > 0 pour lequel f(n) = (n
log
b
a+
) et sil existe c < 1 tel
que af(n) cf(n) d`es que n est susamment grand, alors T(n) =
(f(n)).
Ce theor`eme est prouve dans le livre de reference.