Professional Documents
Culture Documents
DALGORITHMIQUE
http://www.free-livres.com/
Rappels de cours
X Exercices et problmes
avec corrigs dtaills
X Solutions en pseudo code
et en langage C
X
Nicolas Flasque
Enseignant mathmatiques et informatique, EFREI
Helen Kassel
Enseignant mathmatiques et informatique, EFREI
Franck Lepoivre
Enseignant-chercheur
Boris Velikson
Enseignant mathmatiques et informatique, EFREI
TABLE
DES MATIRES
AVANT-PROPOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
IX
INTRODUCTION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
7
7
8
8
9
9
9
12
1.6 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.1 Dfinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.2 Reprsentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.3 Relation entre tableaux et boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.4 Les tableaux plusieurs dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
14
15
16
17
1.7 Pointeurs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7.1 Notion dadresse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7.2 Dfinition et contenu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7.3 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
18
19
20
23
24
V
25
27
28
29
30
31
31
31
32
33
34
35
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
35
35
35
37
43
45
47
87
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
87
3.1 Piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1 Reprsentation contigu des piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.2 Reprsentation chane des piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.3 Manipulation dune pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
87
87
88
88
90
90
91
91
98
99
VI
127
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
127
127
128
128
129
132
142
146
CHAPITRE 5 AUTOMATES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
169
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
169
5.1 Historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
169
170
187
191
BIBLIOGRAPHIE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
215
INDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
217
VII
AVANT-PROPOS
Cet ouvrage sadresse aux lves des coles dingnieurs, aux lves dIUT, de DUT, de BTS, aux
auditeurs des organismes de formation continue et aux autodidactes qui souhaitent se doter de bases
pratiques et thoriques en algorithmique. Le niveau de matrise attendu correspond la seconde
anne de licence.
MODE DEMPLOI
Une place
intermdiaire
contenant llment 6
IX
La liste vide ( un
pointeur ne pointant
sur rien)
Le cas particulier du
couple (liste deux
lments)
Reprsentation des
modifications effectues
(pointills (aprs) vs.
traits pleins (avant))
Ingnieur IIE depuis 1992 et docteur en informatique depuis 2001. Aprs avoir travaill une
anne en tant que responsable logiciel sur les systmes embarqus automobiles, il reprend ses
tudes et obtient un doctorat de luniversit de Caen sur la reconnaissance de vaisseaux sanguins
pour limagerie mdicale. En poste lEFREI depuis septembre 2001, il enseigne lalgorithmique
ainsi que la programmation dans des langages ncessitant des approches diffrentes (C, C++,
C#, Java).
Avant-propos
Helen Kassel
REMERCIEMENTS
Nous remercions nos tudiants de lEFREI, sans qui llaboration de ce contenu naurait pu trouver
le juste diapason pdagogique. Cest par la somme de nos interactions qumergent et samliorent
nos contenus dapprentissage par la pratique.
Nous remercions notre diteur, Jean-Luc Blanc, qui nous a donn la chance de produire ce cahier
sur la base de nos existants pdagogiques dans le cadre de la collection Exercices & Problmes o
il trouve une place cohrente par rapport dautres ouvrages de mathmatiques appliques.
Nous remercions nos familles et nos amis, pour avoir tolr ce temps supplmentaire que nous
leur avons soustrait, et pour leur soutien pourtant indfectible.
XI
INTRODUCTION
1. Notion prleve sur le vocabulaire cyberntique et stratgique, lquifinalit traduit la possibilit pour un systme
datteindre un mme but par diffrents chemins, i.e. une seule stratgie (le but), mais plusieurs tactiques pour raliser la
stratgie.
1
1. TAOCP, The Art of Computer Programming, la Bible de lalgorithmique en quatre volumes par Donald E. Knuth,
professeur mrite de Stanford et inventeur de TEX. TAOCP est une encyclopdie algorithmique plus proche des
mathmatiques pures que de la programmation informatique.
2
Introduction
Un programme destin tre excut par un ordinateur, est la plupart du temps, la description
dun algorithme dans un langage accept par cette machine.
Dfinissons plus formellement le concept :
Un algorithme dcrit un traitement sur un certain nombre, fini, de donnes.
Un algorithme est la composition dun ensemble fini dtapes, chaque tape tant forme dun
nombre fini doprations dont chacune est :
dfinie de faon rigoureuse et non ambigu ;
effective, cest--dire pouvant tre effectivement ralise par une machine : cela correspond
une action qui peut tre ralise avec un papier et un crayon en un temps fini ; par exemple
la division entire est une opration effective, mais pas la division avec un nombre infini de
dcimales.
Quelle que soit la donne sur laquelle on travaille, un algorithme doit toujours se terminer aprs
un nombre fini doprations, et fournir un rsultat.
Une structure de donnes est caractrise par ses composantes et leur arrangement mais surtout
par son mode de traitement.
Ainsi deux structures ayant les mmes composantes, les mmes arrangements comme les PILES
et FILES dATTENTE sont considres comme diffrentes car leurs modes dexploitation sont
fondamentalement diffrents.
LES
BASES
DE LA PROGRAMMATION
Remarque
Le type utilis pour stocker des caractres est un peu particulier, car un caractre est en fait un
nombre entier ! Lordinateur utilise une table de correspondance qui associe une valeur entire (un
code) un caractre quil sagit de manipuler, cest--dire, la plupart du temps, pour lafficher
lcran. Cette table de correspondance se nomme la table de symboles (table ASCII, Unicode).
Pour la table ASCII, un caractre est stock dans un octet (un groupement de 8 bits), la valeur
entire peut donc aller de 0 255. Dans le tableau ne sont prsentes que les valeurs de 32 127 :
en de de 32, il sagit de caractres non imprimables, au-del de 127, ce sont des caractres
optionnels, qui sont adapts un type de clavier ou de langue particulier, notamment les caractres
accentus (, , , , , etc.).
5
Lidentificateur ou nom de la variable peut tre quelconque, mais doit respecter les critres
suivants :
un identificateur commence toujours par une lettre minuscule ;
lexception du premier caractre, il peut contenir : des lettres, des chiffres, et le symbole _
(soulign ou underscore) ;
les majuscules et les minuscules sont des lettres diffrentes : les identificateurs toto et Toto
sont diffrents ;
le nom de variable doit avoir une relation avec le rle de cette variable et tre comprhensible.
Cette dfinition cre une variable nomme taux dans laquelle peuvent tre stocks des nombres
virgule.
Quelques exemples de dfinition de variables
VAR solution_equation : rel dfinit une variable nomme solution_equation dont le type
est rel ;
VAR val_1 : entier dfinit une variable nomme val_1 dont le type est entier ;
VAR lettre : caractre dfinit une variable nomme lettre dont le type est caractre.
Il est possible de dfinir plusieurs variables dun mme type en spcifiant le nom du type,
puis la liste des noms de variables spars par des virgules ,. Ainsi, les dfinitions suivantes
sont strictement quivalentes : VAR val_a : entier, VAR val_b : entier, VAR val_c : entier
VAR val_a, val_b, val_c : entier
1.4.2 Constantes
Il est possible daffecter des valeurs numriques, appeles constantes, dans une variable. Comme
toute valeur, une constante est type, et ce type a une influence sur la syntaxe :
Constantes de type entier : il suffit juste dcrire la valeur en base dix, cette valeur peut tre
positive ou ngative. La variable recevra alors la valeur choisie.
Constantes de type rel : elles sont crites sous la forme mantisse exposant, cest--dire
la notation scientifique avec les puissances de dix, utilise par les calculatrices. La virgule est
reprsente par un point (notation anglo-saxonne), et lexposant, qui est un nombre positif ou
ngatif, est prcd du symbole E. Il est possible de ne pas indiquer :
lexposant lorsque celui-ci est nul ;
le signe + devant lexposant si celui-ci est positif ;
la partie dcimale dun nombre si celle-ci est nulle, par contre on fera toujours figurer le point
dcimal.
Constantes de type caractre Il est possible daffecter un entier un caractre en utilisant
une constante entire ou en indiquant le caractre souhait entour de simples guillemets. Ces
simples guillemets se lisent alors : "code ASCII de".
Exemple de programme daffectation
programme affectations
VAR a : entier
VAR x : rel
VAR ma_var : caractre
a -6
x 6.022E+23
ma_var Y
SAISIR permet dinitialiser une variable partir dune saisie faite au clavier. On utilise saisir,
suivi entre parenthses du nom de la variable que lon veut saisir. Linstruction saisir a pour
seul effet dattendre que lutilisateur entre une valeur au clavier et la valide en appuyant sur la
touche entre ou enter ; aucun message ne saffiche pour indiquer lutilisateur ce quil
doit faire ; cest donc au programmeur de penser afficher un message pour indiquer quune
saisie doit tre faite !
Exemple
VAR a : entier
// dfinition de la variable entire a
SAISIR(a)
// saisie de la variable
// lutilisateur doit entrer une valeur au clavier
// et la valider par la touche entre
Contrairement loprateur AFFICHER, on ne peut saisir que des variables avec SAISIR.
Tableau 1.1
Nom
Utilisation
Rle
Rsultat
valeur1 = valeur2
galit
6=
valeur16= valeur2
Ingalit
>
Suprieur strictement
<
Infrieur strictement
>
Suprieur ou gal
valeur1 6 valeur2
Infrieur ou gal
if (condition)
{
instruction(s);
}
Structure SI...ALORS...SINON
Il arrive assez souvent quen fonction de la valeur dune condition, le programme doive excuter
des instructions si elle est vraie et dautres instructions si elle est fausse. Plutt que de tester une
condition puis son contraire, il est possible dutiliser la structure SI...ALORS...SINON, dont la
syntaxe est la suivante :
Langage algorithmique
SI condition ALORS
instructions 1
SINON
instructions 2
FINSI
10
Implantation C
if (condition)
{
instructions 1;
}
else
{
instructions 2;
}
La partie SI...ALORS est identique la structure de contrle simple : si la condition est vraie,
alors les instructions 1 sont excutes, et pas les instructions 2. Les instructions 2, concernes par
le SINON, sont excutes si et seulement si la condition est fausse.
Dans un programme, lutilisation de tests est trs frquente. Quelle forme aurait un programme
dans lequel il serait ncessaire de vrifier deux, quatre, voire dix conditions avant de pouvoir
excuter une instruction ? En vertu des rgles dindentation et de prsentation, il deviendrait
rapidement illisible et moins comprhensible. Afin dviter cela, il est possible de regrouper
plusieurs conditions en une seule en utilisant dautres oprateurs logiques (boolens) encore appels
connecteurs logiques.
Ces oprateurs permettent deffectuer des calculs avec des valeurs de type VRAI ou FAUX et de
fournir un rsultat de type VRAI ou FAUX. Nous en prsentons trois, nomms ET, OU et NON en
indiquant simplement les rsultats quils produisent. Ces tableaux sont nomms tables de vrit :
ET : intuitivement, la condition (c1 ET c2) est VRAI si et seulement si la condition c1 est VRAI
ET si la condition c2 est VRAI.
OU : la condition (c1 OU c2) est VRAI si et seulement si la condition c1 est VRAI OU si la
condition c2 est VRAI.
NON : loprateur NON change quant lui la valeur de la condition quil prcde. Il se note
galement .
Tableau 1.2
Oprateur OU
Oprateur ET
Oprateur NON
c1
c2
c1 ET c2
c1
c2
c1 OU c2
c1
NON c1
FAUX
FAUX
FAUX
VRAI
FAUX
FAUX
FAUX
FAUX
FAUX
VRAI
FAUX
VRAI
FAUX
VRAI
Implantation C
ET
OU
se note &&
se note ||
se note !
11
while (condition)
{
instruction 1;
...
instruction n;
}
instructions suivantes;
FAIRE
instruction 1
...
12
instruction n
TANTQUE condition
instructions suivantes
Implantation C
do
{
instruction 1;
...
instruction n;
}
while (condition);
instructions suivantes;
Langage algorithmique
1.6 TABLEAUX
Un programme peut tre amen manipuler de nombreuses variables reprsentant des valeurs
distinctes mais de mme nature. Par exemple, un relev de plusieurs tempratures en plusieurs
endroits et plusieurs dates ncessitera autant de valeurs entires que de tempratures stocker.
Il est difficilement envisageable de dfinir manuellement autant de variables que de valeurs
stocker. Les tableaux, en informatique, permettent de rsoudre ce problme en proposant la
cration de plusieurs variables de mme type, dune manire trs compacte.
1.6.1 Dfinition
Un tableau se dfinit en indiquant son nom, le type des lments stocks dans le tableau, ainsi que
leur nombre, crit entre crochets. Ce nombre se nomme galement la taille maximale du tableau.
Syntaxe : VAR nom_du_tableau : type_des_lments[taille_maximale]
Exemple
VAR tab : entier[100] est la dfinition dun tableau nomm tab qui peut stocker 100 valeurs
de type entier au maximum.
1.6. Tableaux
Il nest pas possible de manipuler un tableau en utilisant les oprateurs arithmtiques et logiques
que nous avons rencontrs jusqu prsent, ceux-ci tant limits aux types de base. Il est trs
important de se rappeler cela, cest un bon moyen dviter les erreurs lors de lcriture dun
programme.
1.6.2 Reprsentation
Un tableau peut tre vu comme un ensemble de cases o chaque case stocke une valeur. Soit la
dfinition suivante :
VAR vals : rel[15]
Cette dfinition cre un tableau nomm vals qui stocke au maximum quinze rel dans quinze
cases diffrentes. Aucune de ces cases nest initialise, ce qui est indiqu par un ? .
Tableau 1.3
?
vals
Chacune de ces cases est repre par son numro ou indice lintrieur du tableau. Un tableau
de taille maximale N verra ses cases numrotes de 0 N1. En effet, pour un ordinateur, la valeur
0 est une valeur comme une autre.
Soit i un indice (i est donc de type entier puisquil sagit dun numro de case). La valeur stocke
dans la case dindice i dun tableau tab se nomme tab[i].
Tableau 1.4
0
10
11
12
13
tab[0]
tab[2]
tab[6]
14
?
tab[14]
Remarque
Cette schmatisation sera employe de nombreuses reprises dans cet ouvrage. Nhsitez pas
lutiliser lors de la rsolution des exercices, elle aide visualiser le droulement des algorithmes.
Il ne faut pas confondre la valeur note entre crochets lors de la dfinition du tableau (la taille
maximale) et la valeur note entre crochets lors des instructions (lindice).
Toute expression qui donne une valeur entire peut jouer le rle dindice lors de laccs aux
valeurs stockes dans un tableau.
La notation t[i], o t est un tableau et i un indice est quivalente une variable et peut tre
utilise comme telle dans un programme. Cette variable est considre comme ayant le type des
lments stocks dans le tableau.
Soient les dfinitions de variables suivantes :
VAR x : rel
VAR z : rel[20] // z est un tableau stockant au plus 20 rels
VAR idx : entier // variable utilise comme un indice
15
Taille utile
Le fait de donner la taille maximale dun tableau sous la forme dune constante est une contrainte
lourde, puisque cette valeur est fixe lors de lcriture du programme. Or un tableau ne stockera
pas, en pratique, toujours autant dlments que sa taille maximale. Il est donc ncessaire de savoir
combien de variables sont rellement intressantes traiter. Ce nombre de variables doit tre stock
dans une variable de type entier nomm taille utile du tableau par opposition la taille maximale
fournie la dfinition du tableau. Dans un tableau dont la taille utile est P et dont la taille maximale
est N, les variables intressantes seront ranges aux indices compris entre 0 et P1. Les cases dont
les indices vont de P N1 stockent des valeurs (car une variable nest jamais vide) mais elles ne
seront pas concernes par les traitements ou instructions effectues.
chaque dfinition dun tableau est associe la dfinition de sa taille utile. Ceci doit tre
systmatique.
Cette taille utile peut voluer lorsque :
Un lment est ajout au tableau, dans ce cas la taille utile est augmente de 1 (ou encore
incrmente). Avant dajouter un lment, il faut vrifier quil reste au moins une case disponible :
la taille utile doit tre infrieure strictement la taille maximale du tableau. Cette vrification
est ralise par lemploi dun test (instruction si).
Un lment est retir du tableau, dans ce cas la taille utile est diminue de 1 (ou dcrmente).
Avant denlever un lment du tableau, il faut vrifier que la taille utile est strictement positive,
cest--dire quil y a au moins un lment dans le tableau.
Enfin, cette variable stockant la taille utile dun tableau na pas de nom qui lui est spcifiquement
ddi. Pour des raisons de lisibilit, cette variable sera nomme util ou tai_ut par exemple.
1.6.3 Relation entre tableaux et boucles
Les boucles sont extrmement utiles pour les algorithmes associs aux tableaux. En effet, de
nombreux algorithmes relatifs au tableau ncessitent de parcourir les lments du tableau dans
un certain ordre, le plus souvent dans le sens des indices croissant. Le traitement de chacun des
lments tant souvent le mme, seule la valeur de lindice est amene changer. Une boucle est
donc parfaitement adapte ce genre de traitements.
Illustration par un algorithme de recherche de la plus grande valeur stocke dans un tableau dentiers. Cet algorithme est abondamment comment car il est une synthse des notions rencontres
pour les tableaux.
16
1.6. Tableaux
PROGRAMME recherche_max
VAR maxi :entier
// stocke la valeur du maximum
VAR tabloval :entier [50]
// un tableau stockant des valeurs
VAR t_ut : entier
// la taille utile du tableau
VAR cpt : entier
// index des lments du tableau
VAR rep : caractere
// pour la saisie des valeurs
// tape numro 1 : initialisation des variables
t_ut 0 // ce stade, pas de variable dans le tableau
FAIRE
AFFICHER("entrez une valeur dans le tableau : ")
// valeur range dans la case dindice t_ut
SAISIR(tabloval[t_ut])
// incrmentation de t_ut (car ajout dun lment)
t_ut t_ut+1 ;
AFFICHER("une autre saisie ? (o/n) :")
SAISIR(rep);
// la boucle reprend sil reste de la place
// dans le tableau ET si lutilisateur souhaite continuer
TANTQUE ((t_ut < 50) ET (rep=o))
// pour linstant, le plus grand est dans la case 0
maxi tabloval[0]
// cherchons case par case (de lindice 1 t_ut-1)
POUR cpt DE 1 A t_ut-1 FAIRE
// si lon trouve plus grand :
SI (tabloval[cpt] > maxi) ALORS
// la valeur est mmorise dans maxi
maxi tabloval[cpt]
FINSI
FAIT
17
1.7 POINTEURS
Abordons maintenant un point souvent redout tort : les pointeurs. Un peu de logique et de
rigueur suffisent bien comprendre cette notion fondamentale. Une fois de plus, la notion de type
sera essentielle dans cette partie.
1.7.1 Notion dadresse
Toute variable possde trois caractristiques : un nom et un type, qui ne peuvent tre modifis au
cours du programme, car fixs au moment de la dfinition de la variable ; et une valeur qui au
contraire volue au cours du programme. En ralit, toute variable possde galement une autre
caractristique fondamentale, utilise en interne par la machine : une adresse. En effet, afin de
mmoriser la valeur dune variable, lordinateur doit la stocker au sein de sa mmoire, mmoire
dont les lments ou cellules sont reprs par des numros (de manire analogue ce qui se passe
dans un tableau en ralit). Le nom de la variable nest en fait pas utile la machine, qui se contente
de son adresse ; cest pour le confort du programmeur que le choix du nom est rendu possible.
Ainsi, il existe une dualit entre le nom de la variable (ct programmeur) et son adresse (ct
machine).
Ladresse dune variable nest autre que le numro de la case ou cellule mmoire que celle-ci
occupe au sein de la machine. Au mme titre que son nom, ladresse dune variable ne peut pas
varier au cours dun programme. Il nest mme pas possible de choisir ladresse dune variable,
cette adresse est attribue automatiquement par lordinateur.
Chose curieuse, ces adresses de variables seront manipules sans mme connatre leur valeur
exacte. Il suffira de savoir les nommer pour les utiliser.
Pour ce faire, un nouvel oprateur est ncessaire : loprateur &. Il se lit tout simplement adresse
de et prcde uniquement un nom de variable.
Exemple
Soit la dfinition de variable suivante : VAR x : rel
Cette simple dfinition attribue la variable : un nom (x), un type (rel), une valeur (inconnue),
et galement une adresse (de manire automatique).
18
1.7. Pointeurs
La notation &x signifie donc : adresse de x . La valeur prcise de cette adresse ne nous est en
ralit daucun intrt, mais il sera parfois ncessaire de la manipuler.
Notion de contenu
Soit p un pointeur (il sera temps de passer la syntaxe exacte de dfinition de pointeur lorsque
toutes les notions ncessaires auront t abordes). p tant un pointeur, il est galement une variable,
et donc possde un nom, une valeur (qui est une adresse) et un type.
Un pointeur possde en plus une autre caractristique, qui est son contenu.
Une adresse est le numro dune cellule mmoire, et dans cette cellule, se trouve une valeur.
Cette valeur est appele le contenu du pointeur, et ne doit pas tre confondue avec sa valeur.
Le contenu dun pointeur est la valeur de la cellule mmoire dont ce pointeur stocke ladresse.
Puisque ces deux notions sont diffrentes, il existe une nouvelle notation pour indiquer laccs
un contenu : cette notion est loprateur * (lire toile). Cet oprateur est le mme que loprateur
de multiplication, mais le contexte dcriture permet lordinateur de reconnatre quelle est la
signification exacte de cet oprateur.
Voici une rgle trs simple et trs utile pour lutilisation de cet oprateur *: il se lit toujours
comme contenu de , et non pas pointeur . Cette rgle vitera de nombreuses confusions par
la suite.
Exemple
Soit ptr un pointeur. Supposons que la valeur de ce pointeur soit 25040 (rappelons que cette
valeur est tout fait arbitraire). Supposons galement que dans la mmoire, lemplacement
25040 se trouve la valeur 17.
Dans ce cas, la valeur de ptr, note ptr, est 25040.
Le contenu de ptr, not *ptr, est la valeur de la cellule mmoire numro 25040, cest--dire 17.
*ptr vaut donc 17
Type point
Quelle est la syntaxe permettant de dfinir un pointeur ? Cette syntaxe nest hlas pas immdiate
(sinon elle aurait dj t prsente), car le type pointeur nexiste pas en tant que tel. Il
19
est ncessaire dajouter des informations supplmentaires afin que lordinateur puisse exploiter
correctement ces pointeurs.
En ralit, un pointeur a besoin dinformations sur son contenu, sinon il ne peut rien en faire. La
valeur dun pointeur nest quune adresse, mais cette information nest pas suffisante pour grer les
contenus possibles.
Les diffrents types dj voqus auparavant, entier, reel et caractere, ont des tailles
diffrentes : un entier occupe quatre octets (ou cellules), un rel en occupe huit et un caractre un
seul. Lorsque lordinateur cherche accder au contenu dun pointeur en mmoire, il doit savoir
combien de cellules seront concernes par cette opration. Le type de contenu, encore appel type
point, est indispensable la dfinition dun pointeur. Il lui est mme tellement li quun pointeur
ne peut pointer quun seul type de contenu.
Il nexiste donc pas de pointeur gnrique vers nimporte quel type de contenu, mais des
pointeurs sur (ou vers) des entier, des pointeurs sur des reel, des pointeurs vers des caractere.
Un pointeur se dfinit par le type de son contenu. Pour la cration dun pointeur nomm ptr qui
pointe sur des entiers, sa dfinition scrit : le contenu de ptr est de type entier . En langage
informatique, cest cette notation qui sera traduite en dfinition de variable, de la manire suivante :
VAR *ptr : entier (se lisant le contenu de ptr est de type entier ). Puisque le contenu de
ptr est de type entier, il est ais den dduire que ptr est un pointeur vers un entier. Attention ce
point qui ne semble pas trs important, mais qui est fondamental.
Il en est de mme pour les autres types points : la dfinition dun pointeur nomm ptr_reel
vers un rel est la suivante : VAR *ptr_reel : rel (se lisant : le contenu de ptr_reel est de
type reel ). Enfin, la dfinition dun pointeur nomm p_cgs vers un caractre est la suivante :
VAR *p_cgs : caractere (se lisant : le contenu de p_cgs est de type caractere ).
Dans les exemples de dfinition de pointeurs prcdents, la convention de nommage dun
pointeur veut que son nom dbute par ptr ou p_. Cependant un pointeur tant une variable, le
choix de son nom nest contraint que par les rgles de nommage de variables exposes en dbut de
chapitre.
1.7.3 Initialisation
Les dangers de loprateur *
Comme pour toute variable, un pointeur doit tre initialis avant dtre manipul. Comme un
pointeur stocke une adresse, il ne peut tre initialis comme une simple variable de type entier,
reel ou caractere car les adresses sont gres par la machine et non par lutilisateur. Accder
un contenu est une opration dlicate car elle est en relation avec la mmoire, qui est une ressource
indispensable au fonctionnement de lordinateur. Chaque programme y stocke les valeurs dont
il a besoin pour sexcuter correctement. tant donn quun ordinateur de type PC classique fait
cohabiter plusieurs dizaines de programmes diffrents au mme instant, il doit dlguer la gestion
de la mmoire un programme particulier nomm le systme dexploitation. Le rle de ce dernier
est de veiller au bon fonctionnement de la partie hardware de lordinateur, partie qui concerne la
mmoire.
20
1.7. Pointeurs
En imaginant quun pointeur puisse stocker nimporte quelle valeur, accder au contenu se
rvlerait particulirement dangereux pour la stabilit du systme. Un simple exemple devrait vous
en convaincre.
Exemple
Soient la dfinition et linitialisation de pointeur suivantes :
VAR *ptr : entier
// lire : "le contenu de ptr est de type entier"
ptr 98120
// initialisation de la valeur du pointeur
// (ptr donne accs la valeur de ptr)
*ptr -74
// initialisation du contenu du pointeur ptr
// (*ptr est le contenu de ptr)
Ces simples instructions permettraient au programme dcrire une valeur arbitraire nimporte
quelle adresse de la mmoire de lordinateur, ce qui nest pas du got du systme dexploitation.
Dans le cas dun accs une adresse non autorise, ce dernier met une fin brutale lexcution du
programme responsable de cet accs : cest une des sources des plantages des programmes,
et mme la source la plus frquente de ce phnomne.
Afin dviter un trop grand nombre derreurs de ce type, lordinateur (le compilateur en ralit),
refusera dinitialiser un pointeur avec des valeurs arbitraires.
Le seul cas o un pointeur stocke une valeur arbitraire est lorsquil vient dtre dfini : comme
toute autre variable, il stocke alors une valeur alatoire. La rgle dor dutilisation de loprateur *
(accs au contenu, ou encore prise de contenu) est la suivante :
laccs au contenu dun pointeur ne se fait en toute scurit que si ce dernier a t correctement
initialis.
La valeur NULL
Il est possible dinitialiser un pointeur avec une adresse qui est forcment inaccessible, quel que
soit le programme crit. Lutilit dune telle initialisation est quelle permet de savoir par un simple
test si le pointeur a t initialis avec une adresse valide, et ainsi viter les accs malencontreux
des adresses invalides. Ladresse qui est forcment inaccessible est ladresse 0. Selon le type de
machines, cette adresse stocke des informations plus ou moins vitales pour le compte du systme
dexploitation. En informatique, ladresse 0 porte un nom particulier : NULL, qui nest quun autre
nom qui est donn la valeur 0, mais NULL ne sutilise que dans des contextes particuliers. De
nombreuses instructions utilisent dailleurs cette valeur note NULL.
Exemple dutilisation de NULL
PROGRAMME test_NULL
// lire "le contenu de p_val est de type rel" :
VAR *p_val : rel
// initialisation de p_val avec NULL
// que lon sait inaccessible
p_val NULL
// plus loin dans le programme...
SI (p_val 6= NULL)
21
Dans ce cas, le programme affiche un message davertissement plutt que de provoquer une erreur
qui nest pas vidente reprer et corriger : ce type de programmation est privilgier dans la
mesure o le programmeur matrise beaucoup mieux ce qui se passe au sein du programme.
Les types sont compatibles, car ptr_c pointe vers un caractre, autrement dit stocke ladresse
dune valeur de type caractre, et &lettre est ladresse dune variable de type caractre. Que se
passe-t-il exactement dans ce cas ? Continuons le programme pour illustrer les relations entre
les variables ptr_c et lettre.
lettre Y
AFFICHER((*ptr_c)+1) // cette instruction affiche Z
Le rle de cette commande est dallouer un espace mmoire pour stocker le contenu qui sera
point par le pointeur qui est fourni la commande.
Exemple :
Soit le pointeur ptr dfini de la manire suivante :
VAR *ptr : rel
A priori, ptr nest pas initialis et il est donc hasardeux daccder son contenu. Afin dinitialiser
ptr correctement, il est possible de reserver de lespace mmoire pour son contenu.
RESERVER(ptr) aura cet effet.
La fonction propose en exemple a comme entres deux valeurs de type rel, effectue un calcul,
et a comme sortie une valeur de type rel.
Len-tte dune fonction contient les informations ncessaires pour identifier la fonction : son nom,
ses entres et leur type, le type de sa sortie (sans la nommer, car cest inutile).
Syntaxe de len-tte
FONCTION nom(liste entres avec leurs types ): type de la sortie
La liste des entres avec leur type suit exactement la mme syntaxe que les dfinitions de
variables, et ceci nest pas un simple hasard, car les entres sont des variables de la fonction.
La seule diffrence rside dans le fait que si plusieurs entres ont le mme type, on ne peut pas
les crire comme on le ferait pour une dfinition multiple de plusieurs variables du mme type. Il
faut rappeler le type de lentre pour chacune des entres.
La sortie ne ncessite pas dtre nomme, car cest la fonction ou au programme appelant la
fonction de rcuprer cette valeur. Le rle de la fonction se limite uniquement, dans le cas de la
sortie, pouvoir fournir une valeur numrique, seul son type est donc important.
Corps
Le corps de la fonction est constitu des instructions de la fonction, et est plac directement la
suite de len-tte de la fonction, entre les mots DEBUT et FIN.
Dans le corps de la fonction, outre les instructions, on peut galement trouver des dfinitions de
variables qui peuvent tre utiles pour faire des calculs intermdiaires lorsque lon utilise la fonction.
Les dfinitions des variables se trouvent avant le mot DEBUT.
Les variables locales
Ces variables, dfinies lintrieur de la fonction, sont des variables dites locales, car elles ne
sont connues que par la fonction. Cest logique, car cest la fonction qui les dfinit (en quelque
24
sorte) pour son usage propre. Une autre fonction ou un programme extrieur ne connaissent pas
lexistence de ces variables, et donc plus forte raison ne connaissent ni leur nom, ni leur type, ni
leur valeur, ni leur adresse. Elles ne peuvent les utiliser.
Les entres de la fonction, prcises dans len-tte, sont galement considres comme des
variables locales de la fonction : cest pour cela que la syntaxe dcriture des entres est si proche
de la syntaxe dune dfinition de variable.
Enfin, les dfinitions des variables locales la fonction suivent exactement les mmes rgles que
celles que nous avons dj rencontres pour les dfinitions de variables dun programme.
Linstruction de retour
Pour traiter intgralement les fonctions, nous avons besoin dune nouvelle instruction permettant
que la fonction communique sa sortie la fonction qui lutilise. La sortie dune fonction (il y en
a au maximum une) est en fait une valeur numrique que celle-ci fournit, et la seule information
ncessaire la bonne interprtation de cette valeur numrique est son type : cest pourquoi on ne
prcise que le type de cette sortie, il est inutile de lui associer un nom. Il nous faudrait donc une
instruction dont leffet serait : rpondre la fonction appelante la valeur quelle demande .
Cette instruction existe, son nom est RETOURNER.
Exemple
Syntaxe : RETOURNER expression
Effet : transmet la valeur de lexpression et met fin lexcution de la fonction.
Toute instruction place aprs linstruction RETOURNER est purement et simplement ignore, ce
qui fait que cette instruction semploie forcment comme dernire instruction dune fonction.
La seule contrainte respecter est que le type de lexpression soit le mme que celui de la sortie,
qui est indiqu dans len-tte de la fonction.
Lorsquune fonction ne possde pas de sortie, il nest pas indispensable dutiliser linstruction
retourner, part pour indiquer que la fonction se termine, ce qui peut aussi tre repr par le mot
FIN de la fonction.
Dunod La photocopie non autorise est un dlit
25
o fonc est le nom de la fonction, et expri, o i est compris entre 1 et n, est une expression dont
le type est celui de lentre i.
Dans tous ces cas, lordinateur ralise les deux tapes suivantes :
Calcul de la valeur de lexpression pour chacun des arguments.
Transmission de cette valeur lentre de la fonction correspondante pour que la fonction
sexcute avec les valeurs transmises.
Gestion de la valeur de sortie de la fonction : affectation
Lorsquune fonction est appele de cette manire, la valeur de la sortie retourne par la fonction
appele est obtenue ainsi : la valeur numrique de lexpression suivant linstruction RETOURNER
est calcule puis transmise la fonction appelante. Au sein de cette fonction appelante, lcriture
nom_de_fonction(liste_des_arguments) est en ralit une expression dont le type est celui de
la sortie de la fonction, qui est prcis dans len-tte de cette dernire. Il sagit donc dune valeur
numrique que lon doit affecter une variable si on veut la conserver.
Lorsque vous voulez conserver le rsultat retourn par une fonction appele, il faut affecter ce
rsultat (obtenu par un appel) dans une variable du mme type que celui de la sortie de la fonction
appele.
Le passage des paramtres
Nous allons tudier, dans cette partie, le mcanisme de passage des paramtres, qui est un point
fondamental concernant les fonctions. Nous allons voir en dtail la manire dont se fait la communication entre les arguments fournis par la fonction appelante et les paramtres (ou entres) reus
par la fonction appele. Ce passage de paramtre est effectu chaque appel dune fonction.
Le mcanisme de recopie
Les actions effectues par lordinateur lors du passage des paramtres (transmission de la valeur
des arguments au moment dun appel de fonction) sont les suivantes :
les valeurs des arguments sont calcules (ou values) ;
26
ces valeurs sont recopies dans les paramtres correspondants de la fonction : lordre de recopie
est celui dans lequel les entres ou paramtres de la fonction sont crits dans len-tte de la
fonction : largument 1 dans le paramtre 1, et ainsi de suite ;
la fonction est excute et ralise ses calculs et instructions ;
linstruction RETOURNER est excute : lexpression contrle par cette instruction est value et
retourne au surprogramme qui a appel cette fonction en tant que sous-programme.
Intgrit des variables locales
Les variables locales un programme ou une fonction ne peuvent pas tre modifies par une
autre fonction lorsque lon applique ce mcanisme pour des paramtres de type simple (entier,
caractere ou reel). En effet, il ne faut pas confondre les arguments dune fonction appele et
les variables du programme qui appelle cette fonction. Un argument nest pas ncessairement une
variable, et mme si cest le cas, cest la valeur de largument qui est transmis la fonction et non
la variable elle-mme. On peut donc en conclure quune variable utilise comme argument dun
appel de fonction ne sera pas modifie par lappel, car cest simplement sa valeur qui est recopie.
1.8.3 Les fonctions et les tableaux
Syntaxe utilise pour les entres de type tableau
Un tableau est une adresse, et cest donc une adresse qui sera transmise par le biais dune entre
dun tel type. Il nest pas possible de transmettre, avec un seul paramtre, dautres informations
que ladresse, cest--dire la taille utile ou mme la taille maximum du tableau. Pour transmettre
lune de ces informations, il faudra utiliser un paramtre supplmentaire. Ainsi, un paramtre de
type tableau sera dfini comme un tableau contenant un certain type de valeurs, mais sans fournir
ni la taille maximum, ni la taille utile.
Exemple
Une entre de type tableau sera fournie de la manire suivante :
nom_entree : type_des_valeurs_stockes[]
Les crochets ne contiennent aucune valeur, ils sont juste prsents pour indiquer que le type de
lentre est un tableau contenant des valeurs dun certain type.
Lorsque lon voudra traiter les valeurs stockes dans ce tableau, il faudra par contre avoir une
information concernant la taille utile de ce tableau : il faudra donc associer systmatiquement
cette entre une entre de type tableau.
Noubliez pas que, mme si la taille utile est dfinie dans le programme principal ou dans une
autre fonction que celle qui traite le tableau, cette taille utile sera stocke dans une variable
laquelle la fonction ne pourra pas accder ! Il faudra donc la lui transmettre.
particulier stock dans le tableau. Le tableau lui-mme (cest--dire ladresse laquelle sont
stockes les valeurs), est repr par son nom, sans les crochets.
Exemple
Soit la dfinition suivante :
tab_car : caractere[20]
cela indique que tab_car est un tableau contenant au plus 20 caractres. Donc tab_car est de
type : tableau de caractere, ou encore pointeur vers des caractres. tab_car est une adresse.
Lorsque lon veut fournir une fonction un argument qui est un tableau, on doit lui fournir une
adresse.
Cela revient donc utiliser la syntaxe de dfinition dune variable de type pointeur. Pour un
paramtre, cette criture sinterprte un peu diffremment, mme si cette interprtation est tout
fait cohrente avec tous les aspects abords avec les pointeurs.
Exemple
Le paramtre nom_du_paramtre est ladresse dune valeur de type_point.
Pourquoi faire cette distinction ici ? Tout simplement parce quun argument fourni une fonction
lors de son appel est une expression, et non une variable : cela signifie, entre autres, que lors
de lappel une fonction dont un paramtre est un pointeur, largument associ ne devra pas
obligatoirement tre un pointeur, mais tout simplement une adresse.
Il pourra donc sagir : de ladresse dune variable existante ou dune adresse stocke dans un pointeur.
28
COMPOSS OU STRUCTURES
linstar des tableaux permettant de regrouper des variables de mme type, il existe la possibilit
de regrouper des variables de types quelconques au sein de types cres par le programmeur en
fonction de ses besoins. En effet, les types de base que sont rel, entier, caractre et pointeur
(adresses) sont souvent trs limitatifs lorsque le programmeur souhaite dvelopper des applications
professionnelles. Sans entrer dans les dtails de lutilisation prcise de ces nouveaux types, il est
possible de dire que ces types sont issus dun travail de modlisation, travail dont le but est de
choisir, pour un objet du monde rel qui doit tre manipul dans une application informatique, la
liste des proprits (valeurs) qui vont reprsenter cet objet.
Exemple
Lobjet personne du monde rel possde normment de proprits (vous pourriez en dresser
une liste trs longue); mais toutes ne sont pas forcment adaptes pour une application prcise.
Un logiciel de gestion de compte bancaire naura par exemple pas besoin de connatre la taille, le
poids, la couleur des cheveux ni le plat prfr dune personne.
Pour dfinir un type compos, il faut en premier lieu lui choisir un nom, puis dresser la liste de
toutes les proprits, galement appeles champs, que lon souhaite stocker lintrieur de ce type.
Chacun des champs est identifi par un nom, ce qui permet au programmeur de le choisir parmi
ceux qui sont stocks dans le type compos, et dun type, pour que lordinateur sache le manipuler.
29
Par convention de nommage, un nom de type compos doit systmatiquement commencer par
t_.
Pour dfinir un type compos, aussi appel structure, on utilise simplement la syntaxe suivante :
STRUCTURE nom_du_type
nom_champ_1 : type_champ_1
...
nom_champ_n : type_champ_n
Le mot STRUCTURE permet de dfinir un nouveau type, et non pas une variable.
Une fois ce type compos dfini, il est utilisable dans un programme presque au mme titre que
les types de base de lalgorithmique, qui sont entier, rel et caractre. Il est notamment possible de
dfinir des variables dont le type est un type compos ou structure, en utilisant la syntaxe classique
de dfinition dune variable :
VAR nom_de_variable : type
VAR var_comp : t_complexe est une dfinition de variable tout fait correcte, et dont la
signification est : la variable nomme var_comp est de type t_complexe.
30
Pour stocker la valeur 1-2i dans la variable z, il faut en ralit stocker la valeur 1 dans la partie
relle de z et -2 dans la partie imaginaire de z, ce qui scrit :
z.re 1.0
z.im -2.0
Une confusion frquente consiste faire prcder le . du nom du type dfini laide de la
structure. Le . doit systmatiquement tre prcd dun nom de variable.
31
Pour initialiser ce pointeur ptr, les rgles ne changent pas, il faut lui donner une adresse valide :
celle dune variable existante ou ladresse dune zone de mmoire obtenue par lemploi de la
rservation.
Accs aux champs
Comment accder aux champs dune variable de type structure lorsque lon ne dispose que dun
pointeur vers cette variable et non de sa valeur ? Dans ce cas, la valeur de la variable nest autre
que le contenu du pointeur.
32
Exemple
Soit p_tot un pointeur dfini de la manire suivante :
VAR *p_tot : t_complexe
Son initialisation est la suivante :
RESERVER(p_tot)
Cest donc le contenu de la zone mmoire pointe par p_tot et not *p_tot, qui est de type
t_complexe (regardez la dfinition du pointeur p_tot, elle ne dit pas autre chose). Il est alors
possible daccder ses champs comme pour nimporte quelle autre variable de ce type, laide
des notations classiques.
(*p_tot).re une valeur
(*p_tot).im une valeur
La notation (flche)
Dernier oprateur abord pour ce qui concerne les structures, loprateur nest pas proprement parler indispensable, son rle est de procurer une syntaxe plus intuitive que le couple * et
. voqu dans le paragraphe prcdent. Lutilisation de cet oprateur est des plus simples.
Exemple
Soit ptr un pointeur vers une variable de type structure, et soit ch un des champs de cette variable.
Dans ce cas, deux critures sont quivalentes :
(*ptr).ch
ptrch
Ainsi, lexemple prcdent peut tre crit :
p_totim une valeur
p_totim une valeur
Il est parfois pratique de disposer dun nom de type raccourci pour dsigner un pointeur vers une
structure. Ce nom raccourci de type se dfinit avec la syntaxe suivante :
TYPE *type_pointeur : type
Exemple :
TYPE *ptr_comp : t_complexe
Le raccourci de type prcdent signifie que le type ptr_comp dsigne un pointeur vers un
t_complexe. Ainsi, les deux dfinitions de variables suivantes sont quivalentes :
VAR *pz : t_complexe
VAR pz : ptr_comp
Dans les deux cas, la variable pz stocke ladresse dune valeur de type t_complexe.
33
34
STRUCTURES
SQUENTIELLES
SIMPLES
RAPPELS DE COURS
On va prsenter ici les listes linaires qui sont la forme la plus commune dorganisation des
donnes. On organise en liste linaire des donnes qui doivent tre traites squentiellement. De
plus une liste est volutive, cest--dire quon veut pouvoir ajouter et supprimer des donnes.
2.1.1 Dfinition
Une liste linaire est une structure de donnes correspondant une suite dlments. Les lments
ne sont pas indexs dans la liste, mais pour chaque lment (sauf le dernier) on sait o se trouve
llment suivant. Par consquent, on ne peut accder un lment quen passant par le premier
lment de la liste et en parcourant tous les lments jusqu ce quon atteigne llment recherch.
2.1.2 Reprsentation
Reprsentation tabulaire
On peut reprsenter une liste par deux tableaux :
le tableau 2.1 contient les lments de la liste, dans un ordre quelconque.
le tableau 2.2 est organis de faon suivante : si la case dindice i du premier tableau contient
llment dont le suivant se trouve dans la case dindice j, alors la case dindice i de second
tableau contient lentier j.
On peut extraire les lments du premier tableau dans lordre alphabtique si on connat le
point de dpart : 1 dans notre cas. Ce point de dpart doit videmment tre prcis lentre de
35
Tableau 2.1
0
\0
Tableau 2.2
3
0
5
2
2
3
\0
4
Tableau 2.4
2
0
3
1
5
2
0
3
Cette reprsentation nest pas trs intressante : les fonctions de traitement sont relativement
lourdes.
Reprsentation chane
On utilise des pointeurs pour chaner entre eux les lments successifs, et la liste est alors dtermine
par ladresse de son premier lment. On va dfinir des enregistrements (structures) dont un des
champs est de type pointeur vers une structure chane du mme type.
Exemple de dfinition dune structure chane
Pseudo code formel
STRUCTURE nud
nom : caractre[20]
prenom : caractre[20]
*suiv : nud
TYPE *ptr_nud : nud
Implantation C
36
char prenom[20];
struct nud *suiv;
} nud;
typedef nud* ptr_nud;
Figure 2.1
Cette reprsentation nimpose pas une longueur maximum sur les listes ; elle permet de traiter
facilement la plupart des oprations sur les listes : le parcours squentiel, linsertion et la suppression dun lment une place quelconque, la concatnation de deux listes, se font par une simple
manipulation des pointeurs. Cependant le calcul de la longueur ncessite le parcours de toute la
liste ; de mme, laccs au kime lment nest plus direct : on doit parcourir k1 pointeurs partir
de la tte pour trouver le kime lment.
Avant de passer aux exemples de fonctions qui traitent les listes chanes il faut faire un petit
rappel sur les variables dynamiques.
2.1.3 Variables dynamiques
Cest une variable dont la place mmoire est alloue en cours dexcution.
On ne prend de la place mmoire que lorsquon en a besoin.
Cette place mmoire est alloue explicitement, cest--dire par une instruction du langage.
La dsallocation est galement effectue par lintermdiaire dune instruction.
Il nest donc pas ncessaire de rserver ds la compilation tout un espace mmoire.
On utilise la place juste lorsquon en a besoin, puis on peut la rutiliser par autre chose.
Exemples
Les exemples sont toujours donns en langage algorithmique.
Les donnes, les donnes modifies et les sorties sont systmatiquement prcises en prambule
des dclarations.
titre dillustration, une ralisation (ou implantation, ou implmentation) est donne en C99
pour chacun des types abstraits et algorithmes proposs.
37
STRUCTURE place
info : T
*suiv : place
TYPE *list : place
Implantation C
38
if (l->content == k) found = 1;
else l = l->succ;
}
if (l->content == k) found = 1;
return found;
}
Remarque
On ne peut pas remplacer la condition ! found dans la boucle while par l->succ->content 6= k parce
que si l->succ pointe sur NULL, l->succ->content nexiste pas.
39
if (getHeadContent(l) == k) count++;
while (hasMoreElements(l))
{
l = nextElement(l);
if (getHeadContent(l) == k) count++;
}
return count;
}
Crer une liste chane par ajouts successifs dlments saisis jusqu fin
Spcification de lalgorithme cration de liste en ligne de commande
DEBUT
l listevide()
FAIRE
SAISIR e
SI e 6= fin ALORS
cons(e, l)
FINSI
TANTQUE e =
6 fin
FIN
Ralisation en C
40
scanf("%s", input);
if (strcmp("fin", input) == 0) return 0;
// allocation mmoire et rattachement de la tte
(*pl) = malloc(sizeof(listNode));
list l = *pl;
l->contenu = atoi(input);
// affectation du contenu
l->prev = NULL;
// en + pour liste doublement chane
printf("%d> ", size++);
scanf("%s", input);
list prev_l;
while (strcmp("fin", input) != 0) //(contenu != 0)
{
prev_l = l;
l = l->succ;
// itration
l = malloc(sizeof(listNode)); // allocation
l->contenu = atoi(input);
// affectation du contenu
// important, car la valeur par dfaut nest pas forcment NULL !!!
prev_l->succ = l;
l->prev = prev_l;
// en + pour liste doublement chane
printf("%d> ", size++);
scanf("%s", input);
}
l->succ = NULL;
return (size - 2);
}
41
42
cour = (*pl)->succ;
while (((! ok) && (cour != NULL)) &
(! circular || (circular & (--perimeter > 0))))
{
if (cour->content == x) // removeFirst et terminaison
{
ok = 1;
prec->succ = cour->succ;
// en + pour la liste doublement chane :
if (cour->succ != NULL) cour->succ->prev = prec;
free(cour);
}
else
// itration
{
prec = cour;
cour = cour->succ;
}
}
}
return ok;
}
Il est parfois commode de dfinir une liste non pas comme un pointeur sur une cellule, mais par un
bloc de cellules du mme type que les autres cellules de la liste. Ce bloc ne contient que ladresse
du premier lment de la liste. Lutilisation dun tel bloc permet dviter un traitement spcial pour
linsertion et la suppression en dbut de liste.
Figure 2.2
Liste circulaire
On peut aussi crer des listes circulaires : on remplace, dans la dernire place de la liste, le pointeur
NULL par un pointeur vers la tte de la liste ; de plus si lon choisit la dernire place comme
point dentre dans la liste, on retrouve la tte de liste en parcourant un seul lien.
43
Figure 2.3
Figure 2.4
STRUCTURE node
content : T
*succ, *prev : node
TYPE *list : node
Implantation C
44
Ralisation en C
**
crire deux algorithmes, lun itratif, lautre rcursif, qui permettent de dterminer la valeur
maximale dune liste chane dentiers positifs.
Exercice 2.2 Concatner deux listes
On considre deux listes chanes L1 et L2 dont les lments sont des entiers. crire un algorithme
qui rattache la liste L2 la suite de la liste L1 . Tous les cas particuliers doivent tre pris en compte.
45
***
Soit une liste de nombres relatifs. crire un algorithme permettant de sparer cette liste en deux
listes : la premire ne comportant que des entiers positifs ou nuls, et la seconde ne comportant que
des nombres ngatifs.
Exercice 2.4 Permuter deux places dune liste
***
crire un algorithme sur une liste simplement chane qui change les positions des nuds donnes
par deux pointeurs t et v.
Exercice 2.5 Supprimer des lments
**
suivantes ;
dans le troisime, pour chaque lment de la liste, on ne laisse que sa premire occurrence.
Concevez le second algorithme en adaptant le premier, puis concevez le dernier en exploitant le
second.
**
crire un algorithme qui inverse une liste chane sans recopier ses lments. Raliser une version
itrative et une autre rcursive.
Exercice 2.7 Construire une liste partir dune source de donnes
Concevoir un premier algorithme qui construit une liste chane linaire partir dun tableau, et un
second qui duplique une liste chane existante.
Exercice 2.8 Refermer une liste sur elle-mme
Concevoir un algorithme qui transforme une liste simplement chane linaire en liste simplement
chane circulaire.
Exercice 2.9 Effectuer et retourner deux calculs sur une liste
**
Concevoir un algorithme qui calcule et retourne respectivement le produit des lments positifs et
celui des lments ngatifs dune liste simplement chane dentiers.
Exercice 2.10 Couper une liste en deux
**
Concevoir un algorithme qui spare une liste simplement chane dentiers en deux listes :
A partir dun pointeur sur le maillon qui devient tte de la seconde liste de sortie ;
B juste devant le premier maillon contenant une valeur donne x ;
C partir de la kime position (cest--dire k maillons dans la premire liste de sortie et le reste
dans la seconde) ;
D juste devant le maillon contenant la valeur minimale de la liste ;
E telles que les deux listes de sortie aient mme longueur n, ou que la premire soit de longueur
n+1 et la seconde de longueur n.
46
Problme
PROBLME
Problme 2.1 Saisir, enregistrer puis valuer un polynme***
Enregistrer les coefficients et les exposants dun polynme dans une liste chane. valuer ce
polynme pour une valeur donne de x.
Utiliser au moins deux modules dans ce programme :
lun pour construire la liste sur la base des coefficients et des exposants, qui sont saisis au clavier ;
et le second afin dvaluer le polynme pour la valeur x, galement saisie au clavier.
PRAMBULE
Pour la ralisation en C de tous les algorithmes spcifis ci-dessous, on dfinit la structure de liste
chane suivante dont on prcisera au cas pas cas, le type <element>. Les structures utilises pour
les spcifications des algorithmes portent le mme nom et sont construites de la mme manire.
typedef struct listNode
{
<element> content;
struct place *succ; // listes simplement chanes
struct place *prev; // listes doublement chanes
} listNode;
typedef listNode* list;
typedef list* plist;
#define ERR -1 // code derreur (lisibilit des exemples)
de ->, * et & qui nuisent leur lisibilit. Vous pourrez ainsi vous rapprocher de la vision
algorithmique du quest-ce que a fait ? plutt que de la vision programmatique du comment
a se fait ? .
<element> content(list l) {return (l->content);}
int isempty(list l) {return (l == NULL);}
list succ(list l) {return l->succ;}
int islast(list l) {return isempty(succ(l));}
48
Enfin, le constructeur suivant pourra vous aider simplifier vos critures pour lallocation dun
nouveau nud de liste :
list newSingletonList(int content)
{
list l = malloc(sizeof(listNode));
l->content = content;
l->succ = NULL;
return l;
}
// allocation mmoire
// affectation du contenu
CORRIGS
DES EXERCICES
Cest une simple adaptation de lalgorithme recherche (b) du rappel de cours vu plus haut.
Entre : liste de positifs (non vide)
Sortie : un positif
FONCTION maxOf(l: liste<positif> 6= ): positif
VAR max: positif, p: place
DEBUT
p tte(l)
max contenu(p)
TANTQUE dernier(p) FAIRE
p succ(p)
SI contenu(p) > max ALORS
max contenu(p)
FINSI
FAIT
RETOURNER max
FIN
49
FINSI
max maxOfRec(succ(p))
SI contenu > max ALORS
RETOURNER contenu
FINSI
RETOURNER max
FIN
int maxOfRec(list<unsigned> l)
{
if (isempty(l)) return ERR;
int content = content(l);
if (islast(l)) return content;
int max = maxOfRec(succ(l));
if (content > max) return content;
return max;
}
La contrainte dentiers positifs du premier exercice disparat, mais pas le cas des listes vides.
Nous avons quatre cas considrer :
l1 et l2 vides,
l1 vide mais pas l2 ,
l2 vide mais pas l1 ,
ni l1 , ni l2 vides.
Si l2 est vide, on laisse l1 inchange. Si l1 est vide mais pas l2 , on affecte directement l2 l1 . Si ni
l1 ni l2 ne sont vides, on parcourt l1 jusqu sa dernire cellule et on y rattache l2 .
51
Figure 2.5
Concatnation standard
Enfin, si on va un peu trop vite dans ltude, on peut oublier le cas l1 = l2 (cest--dire mme liste
et non pas, listes dont les valeurs sont gales), cas particulier qui croise le premier et le dernier des
quatre cas. Ce cas donne une bonne illustration de la diffrence entre copies et rfrences.
Figure 2.6
Concatnation circulaire
p1 tte(l1 )
TANTQUE dernier(p1 ) FAIRE
p1 succ(p1 )
FAIT
succ(p1 ) tte(l2 )
FINSI
FINSI
FIN
Ralisation en C
void concat(list *pl1, list *pl2)
{
if (*pl2 == NULL) return; // ras, l1 soit nulle ou non
if (*pl1 == NULL)
// affectation directe de l2 l1
{
*pl1 = *pl2;
return;
}
// sinon :
list l = *pl1;
while (l->succ != NULL) l = l->succ; // itration l1
l->succ = *pl2;
// rattachement de l2
}
53
Figure 2.7
Nous nous proposons dutiliser le mme algorithme itratif pour raliser lopration, que ce soit par
copie ou par rfrence. Cet algorithme consiste parcourir la liste de dpart et aiguiller chaque
nouvel lment rencontr vers lune ou lautre des deux listes selon sa valeur, en les compltant
ainsi progressivement jusqu puisement de la liste de dpart. Il existe dautres solutions.
Spcification de lalgorithme
Entres modifies :
La liste dentier initiale
Les deux listes dentiers produites
FONCTION separer(l0 , l1 , l2 : liste<entier>)
VAR p0 , p1 , p2 : place
DEBUT
SI estvide(l0 ) ALORS
p0 tte(l0 )
SI contenu(p0 ) > 0 ALORS
p1 tte(l1 ) tte(l0 )
SINON
p2 tte(l2 ) tte(l0 )
FINSI
TANTQUE dernier(p0 ) FAIRE
54
p0 succ(p0 )
SI contenu(p0 ) > 0 ALORS
SI estvide(l1 ) ALORS
p1 tte(l1 ) p0
SINON
p1 succ(p1 ) p0
FINSI
SINON
SI estvide(l2 ) ALORS
p2 tte(l2 ) p0
SINON
p2 succ(p2 ) p0
FINSI
FINSI
FAIT
FINSI
FIN
Ralisation en C
void separate(list *pl0, list *pl1, list *pl2)
{
liste l0 = *pl0, l1 = NULL, l2 = NULL;
if (l0 == NULL) return;
// si l0 vide ne rien faire
if (l0->content >= 0) l1 = *pl1 = *pl0; // tte de l1
else l2 = *pl2 = *pl0;
// tte de l2
while (l0->succ != NULL) // itration de l0
{
l0 = l0->succ;
// itration de l0
if (l0->content >= 0)
// transfert vers l1
{
if (l1 == NULL) *pl1 = l1 = l0; // tte de l1
else
// au-del de la tte
{
l1->succ = l0;
// complte l1
l1 = l1->succ;
// itration de l1
}
}
else
// transfert vers l2
{
if (l2 == NULL) *pl2 = l2 = l0; // tte de l2
else
// au-del de la tte
{
l2->succ = l0;
// complte l2
55
l2 = l2->succ;
// itration de l1
}
}
}
// important pour la clture des listes l1 et l2 :
l1->succ = NULL;
// finalisation de l1
l2->succ = NULL;
// finalisation de l2
*pl0 = NULL;
// libration de l0 (opt.)
}
Figure 2.8
Les deux pointeurs identifient des places contiges (cf. figure 2.9 ce cas est dangereux si trait
comme prcdemment).
Figure 2.9
56
Spcification de lalgorithme
tsucc v
vsucc succ_t
FINSI
SINON
SI ((prec_t = NULL) OU (prec_v = NULL) ALORS
SI prec_t = NULL ALORS
*pl v
prec_vsucc t
FINSI
SI prec_v = NULL ALORS
*pl t
prec_tsucc v
FINSI
SINON
prec_v t
prec_t v
FINSI
tsucc succ_v
vsucc succ_t
FINSI
FIN
Ralisation en C de la fonction principale
59
t->succ = v;
v->succ = succ_t;
}
}
// cas ->[v]->..->[t]-> ou ->[t]->..->[v]->
else
{
// cas .->[t] => .->[v] vs autres cas
if ((prec_t == NULL) || (prec_v == NULL))
{
if (prec_t == NULL)
{
printf("case : .->[t]->[..]->[v]-> => .->[v]->[..]->[t]->\n");
*pl = v;
prec_v->succ = t;
}
if (prec_v == NULL)
{
printf("case : .->[v]->[..]->[t]-> => .->[t]->[..]->[v]->\n");
*pl = t;
prec_t->succ = v;
}
}
else
{
printf("case : [..]->[t/v]->[..]->[v/t]-> =>
[..]->[v/t]->[..]->[t/v]->\n");
prec_v = t;
prec_t = v;
}
// et dans tous les cas :
t->succ = succ_v;
v->succ = succ_t;
}
return 0; // thats all folks !!
}
Fonction dlgue de rcupration du prdcesseur et du successeur dun nud
{
*prec = NULL;
*succ = l->succ;
return;
}
else
// cas de t en milieu ou en queue
{
while (l->succ != NULL)
{
if (l->succ == t)
{
*prec = l;
*succ = l->succ->succ;
return;
}
else l = l->succ;
}
}
}
Nous proposons de raliser le second algorithme en premier lieu, puis de construire le premier
par un appel rcursif du second avec pour condition darrt, un dernier appel qui laisse la liste
inchange.
Pour supprimer un lment, il sagit de reprer llment, et de court-circuiter celui-ci en reliant le
pointeur succ de son prdcesseur directement sur son successeur.
Algorithme rcursif
Retrait de toutes les occurrences dun lment
statut IDENTITE
SI estvide(l) ALORS
SI premier(l) = x ALORS
l fin(l)
statut SUPPRESSION
SI estvide(l) ALORS
supprimerOccurrences(l, x)
FINSI
SINON
e premier(l)
l fin(l)
SI estvide(l) ALORS
st2 supprimerOccurrences(l, x)
SI statut = IDENTITE ALORS
statut st2
FINSI
FINSI
l cons(e, l)
FINSI
FINSI
RETOURNER statut
FIN
k k -- 1
FINSI
SI estvide(l) ALORS
supprOccurAprsKime(l, x, k)
FINSI
SI k > 0 ALORS
l cons(e, l)
FINSI
SINON
e premier(l)
l fin(l)
SI estvide(l) ALORS
st2 supprOccurAprsKime(l, x, k)
SI statut = IDENTITE ALORS
statut st2
FINSI
FINSI
l cons(e, l)
FINSI
FINSI
RETOURNER statut
FIN
tude
Figure 2.10
RETOURNER
FINSI
prev *pl
curr prevsucc
SI currsucc = NULL ALORS
currsucc prev
prevsucc NULL
*pl curr
RETOURNER
SINON
prevsucc NULL
FINSI
TANTQUE currsucc =
6 NULL FAIRE
succ currsucc
currsucc prev
prev curr
curr succ
FAIT
currsucc prev
*pl curr
FIN
// Inversion dune liste (utilise pour lexo 1.4 (normalisation))
void reverse(list *pl)
{
if (*pl == NULL) return;
// liste vide
if ((*pl)->succ == NULL) return; // singleton
list prev = *pl;
// tte
list curr = prev->succ;
// 2d lment
if (curr->succ == NULL)
// si cas 2 lments
{
curr->succ = prev;
// pointage du 2d lt sur le 1er
prev->succ = NULL;
// et du 1er sur NULL (queue)
*pl = curr;
// le 2d devient tte
return;
// termin
}
else
// sinon, au moins 3 elts :
{
prev->succ = NULL;
// pointage du 1er ->NULL (queue)
}
list succ;
while (curr->succ != NULL)
// Expl. sur 1re itration
{
succ = curr->succ;
// sauv. du succ (le 3me lt)
64
curr->succ = prev;
prev = curr;
curr = succ;
}
// si par exemple le 3me na pas de successeur :
// cest la queue (pas dautre passage dans la boucle) :
curr->succ = prev;
// pointage du 3me sur le 2d
*pl = curr;
// le 3me (dernier) devient tte
}
Algorithme rcursif
FONCTION esreverse(*pl : list) : list*
VAR head : list
DEBUT
SI *pl=NULL ALORS
// cas de la liste vide
RETOURNER
FINSI
SI (*pl) succ = NULL ALORS // cas de la liste un seul lment
RETOURNER
FINSI
head *pl
*esreverse(&(headsucc)) succ head
*pl headsucc
headsucc NULL
RETOURNER &head
FIN
// reverse2 en mthode rcursive
list * esreverse(list* pl)
{
if (*pl == NULL) return;
// cas liste vide
if ((*pl)->succ == NULL) return pl; // cas singleton
// autres cas :
list head = *pl;
(*esreverse(&(head->succ)))->succ = head;
*pl = head->succ;
head->succ = NULL;
return &head;
}
65
66
Implantation C
list duplicate(list l)
{
if (l == NULL) return NULL;
list head = l;
list dupl = newSingletonList(l->content);
list dupl_head = dupl;
while (l->succ != NULL && l->succ != head)
{
l = l->succ;
// itration de la liste de rfrence
dupl->succ = newSingletonList(l->content);
if (l->prev != NULL) dupl->succ->prev = dupl;
dupl = dupl->succ; // itration de la liste copie
}
67
// itration de la liste
Il sagit de parcourir la liste et de faire le produit dune part les entiers positifs et dautre part celui
des entiers ngatifs.
La prsence ventuelle du 0 qui selon le statut quon lui donne est un positif ou non (positifs
strictement ou non) peut poser un problme dinterprtation de lnonc. Le minimum est den
parler. Disons, pour ce corrig quil nest ni positif ni ngatif et quil doit donc ne pas intervenir
dans aucun produit quil annulerait.
Les cas spciaux sont les suivants :
Liste vide ou ne contenant que des zros : le rsultat est indtermin.
Absence de positifs ou (exclusif) absence de ngatifs : le rsultat est partiellement indtermin.
68
La difficult de cet exercice tient galement au problme du double rsultat quil sagit de retourner.
Plusieurs solutions sont possibles, la plus simple consistant utiliser deux entres modifies.
On utilisera un code de statut pour rendre compte des rsultats spciaux vs. standard dont les
valeurs signifient les configurations suivantes :
4 : au moins un positif et au moins un ngatif.
2 : au moins un positif mais aucun ngatif.
1 : au moins un ngatif mais aucun positif.
0 : ni ngatif, ni positif : liste vide ou de zros.
Spcification abstraite
FINSI
SI pos_ok ALORS
SI neg_ok ALORS RETOURNER TOUT
SINON RETOURNER UNIQUEMENT_POSITIFS
FINSI
SINON
SI neg_ok ALORS RETOURNER UNIQUEMENT_NEGATIFS
SINON RETOURNER RIEN
FINSI
FINSI
FIN
Implantation C
neg_prod *= content;
neg_prod_ok = 1;
}
}
if (pos_prod_ok) *pos = pos_prod;
if (neg_prod_ok) *neg = neg_prod;
if (pos_prod_ok && neg_prod_ok) return 4;
if (pos_prod_ok && ! neg_prod_ok) return 2;
if (! pos_prod_ok && neg_prod_ok) return 1;
return 0;
}
tude
Dans les cinq cas de figure, il sagit de vrifier lexistence dune position de coupe dans la chane,
et le cas chant deffectuer celle-ci, puis de savoir retourner les deux chanes.
Le cas vacuer en dbut de fonction est celui de la liste vide.
Ensuite, nous devons, respectivement pour les cinq cas :
A vrifier que le pointeur dsigne bien un maillon de la liste ;
B vrifier que la liste contient bien la donne x ;
C vrifier que la liste contient au moins k lments ;
D identifier la valeur minimale de la liste ;
E mesurer la longueur de la liste.
Les cas A, B et C sont trs similaires et doivent intgrer les cas de tte et de queue de liste.
Les cas D, E ncessitent un premier parcours complet de la liste.
Tous les cas sauf le cas E doivent intgrer la possibilit dune coupure devant la tte de liste.
Le cas E revient quasiment au cas B aprs lidentification du minimum de la liste.
Dans tous les cas, il est pertinent de retourner un statut dexcution qui indique si lopration a t
effective ou bien transparente (auquel cas, il ny a pas eu production dune seconde liste).
Il peut y avoir erreur si lun ou lautre des deux pointeurs sur les listes modifier est NULL ou bien
dans le cas particulier du A (B et C sont discutables) si le pointeur donn pointe sur une autre liste.
Comme nous sommes scrupuleux, nous indiquons galement par le statut dexcution si la seconde
liste ntait pas initialise NULL avant lopration effective (mise en garde : crasement dune
ancienne valeur).
Implantation C
Schma commun
0
: opration blanche,
1
: une coupe a t effectue,
2
: statut 1 + l2 ntait pas nulle.
int cut<Version>(plist pl1, plist pl2, ...)
{
if (pl1 == NULL || pl2 == NULL) return ERR;
list l1 = *pl1;
if (l1 == NULL) return 0;
// on fixe le statut par dfaut selon que *pl2 est NULL ou non
int status = *pl2 == NULL ? 1 : 2;
/// CODE SPECIFIQUE <Version> ///
// on coupe
*pl2 = l1->succ;
l1->succ = NULL;
return status;
}
Cas du slecteur pointeur
if (min == l1->content)
{
*pl1 = NULL;
*pl2 = l1;
return status;
}
// sinon, on parcourt la liste jusquau prdcesseur du point de coupe
while (l1->succ != NULL && l1->succ->content != min) l1 = l1->succ;
// avoir atteint la queue de liste ne peut logiquement pas arriver..
/// CODE GENERIQUE COMMUN DE FINALISATION ///
}
Cas du slecteur milieu de liste
Spcification abstraite
Une fois nest pas coutume (petit mensonge dingnieur), nous procdons une rtro conception
du principe algorithmique gnral partir des cinq cas tudis et raliss prcdemment.
Donnes modifies : l1 , la liste dentre coupe en deux qui devient la liste
amont, et l2 la liste aval ventuellement produite en sortie.
Donne : c, un paramtre du critre pour reprer la place qui suit
directement le point de coupe et qui devient donc la tte de l2
Rsultat : un statut dexcution
-1 = ERR :ERREUR,
0 = ID : opration blanche,
1 = OK : une coupe a t effectue,
FONCTION scinder(modif l1 , l2 = : liste; c: critre): statut
VAR p: place
74
DEBUT
c mettre__jour(c, l1 )
SI estvalide(c) ALORS
RETOURNER ERR
FINSI
SI estvide(l1 ) ALORS
RETOURNER ID
FINSI
p tte(l1 )
SI est_successeur_point_de_coupe(p) ALORS
l2 l1
l1
RETOURNER OK
FINSI
TANTQUE dernier(p) est_successeur_point_de_coupe(succ(p)) FAIRE
p succ(p)
c mettre_ventuellement__jour(c)
FAIT
SI dernier(p) ALORS
RETOURNER ID
SINON
tte(l2 ) succ(p)
succ(p)
RETOURNER OK
FINSI
FIN
Cet exercice avec ses cinq cas de figure est une extension du principe algorithmique vu dans
lexercice 2.5 (supprimer des lments). La faon de dfinir le critre de slection des nuds
supprimer change selon les cas, mais le schma gnral ne change pas.
Dans tous les cas, il sagit de traiter spcialement la chane prfixe (de tte) constitu dune
succession de nuds liminer, puis sil reste au moins un lment, dliminer tous les lments
rpondant au critre et situs au-del de la tte.
Le cas A donne lieu une version simplifie de lalgorithme gnral, laquelle profite plein de la
rgularit des intervalles de suppression.
Schma gnral pour les cas B E
La mthode gnrale de rsolution reste la mme, seul le critre de choix des lments supprimer
change. Pour illustrer ce fait, dans la partie Spcification de lalgorithme, le second paramtre
de la fonction removeAll est en ralit une fonction nomme critre. Cette fonction critre a pour
paramtre un entier (la valeur extraite de la liste) et pour sortie un boolen indiquant si la valeur
75
rpond au critre de suppression. Cette fonction critre peut avoir dautres paramtres, notamment
un entier indiquant le seuil partir duquel effectuer la suppression pour les cas D et E.
Spcification de lalgorithme
Les correspondances suivantes permettent de spcialiser le schma gnral pour rpondre au quatre
cas de figure B, C, D, E :
Spcification de lalgorithme
FIN
FONCTION critre_estSup(n : entier, seuil : entier) : boolen // casD
VAR estSup : boolen
DEBUT
SI n > seuil ALORS
estSup vrai
SINON
estSup faux
FINSI
RETOURNER estSup
FIN
FONCTION critre_estInf(n : entier, seuil : entier) : boolen // casE
VAR estInf : boolen
DEBUT
estInf faux
SI (n < seuil) ALORS
estInf vrai
FINSI
RETOURNER estInf
FIN
Implantation C
Le cas de figure A, du fait de la rgularit des intervalles de suppression conduit une version
simplifie du schma gnral prcdent. On limine notamment un niveau dimbrication de boucles
tantque, traduction du fait quil nexiste pas de chane de plusieurs lments directement successifs
supprimer.
78
Spcification de lalgorithme
Implantation C
79
}
return OK;
}
CORRIG
DU PROBLME
STRUCTURE terme
coeff
: rel
exposant : entier
*suivant : terme
TYPE *polynome : terme
Implantation C
Saisie du polynme
Implantation C
Corrig du problme
char exponStr[11];
printf("Saisissez une serie de coefficient et exposants,\n");
printf("puis terminez votre saisie par le coeff fin :\n");
printf("coeff. %d> ", size);
scanf("%s", coeffStr);
if (strcmp("fin", coeffStr) == 0) return;
printf("expon. %d> ", size);
scanf("%s", exponStr);
size++;
*pp = malloc(sizeof(term)); // allocation et rattachement tte
polynomial p = *pp;
p->coeff = atol(coeffStr);
p->expon = atol(exponStr);
p->nextTerm = NULL;
printf("coeff. %d> ", size);
scanf("%s", coeffStr);
if (strcmp("fin", coeffStr) == 0) return;
polynomial prev_p;
while (strcmp("fin", coeffStr) != 0)
{
prev_p = p;
p = p->nextTerm;
// itration
printf("expon. %d> ", size);
scanf("%s", exponStr);
size++;
p = malloc(sizeof(term));
// allocation
p->coeff = atol(coeffStr);
p->expon = atol(exponStr);
// important, car la valeur par dfaut nest pas forcment NULL !!!
prev_p->nextTerm = p;
printf("coeff. %d> ", size);
scanf("%s", coeffStr);
}
p->nextTerm = NULL;
}
Implantation C
82
Corrig du problme
Affichage du polynme
Spcification de lalgorithme
void printPolynomial(polynomial p)
{
if (p == NULL) return;
printf("[");
printTerm(p);
while (p->nextTerm != NULL)
{
p = p->nextTerm;
printTerm(p);
}
printf("]");
}
SI (pexposant = 1) ALORS
AFFICHER("+x")
SINON
AFFICHER("+x^", pexposant)
FINSI
FINSI
RETOURNER
FINSI
SI (pcoeff = -1) ALORS
SI (pexposant = 0) ALORS
AFFICHER("-1")
SINON
SI (pexposant = 1) ALORS
AFFICHER("-x")
SINON
AFFICHER("-x^", pexposant)
FINSI
RETOURNER
FINSI
SI (pcoeff < 0) ALORS
SI (pexposant = 0) ALORS
AFFICHER(pcoeff)
SINON
SI (pexposant = 1) ALORS
AFFICHER(pcoeff)
SINON
AFFICHER(pcoeff, "x^", pexposant)
FINSI
FINSI
SINON
SI (pexposant = 0) ALORS
AFFICHER("+", pcoeff)
SINON
SI (pexposant = 1) ALORS
AFFICHER("+", pcoeff, "x")
SINON
AFFICHER("+", pcoeff, "x^", pexposant)
FINSI
FINSI
FINSI
FIN
84
Corrig du problme
Implantation C
void printTerm(polynomial p)
{
if (p == NULL) return;
if (p->coeff == 0) return;
if (p->coeff == 1) {
if (p->expon == 0) printf("+1");
else if (p->expon == 1) printf("+X");
else printf("+X^(%ld)", p->expon);
return;
}
if (p->coeff == -1) {
if (p->expon == 0) printf("-1");
else if (p->expon == 1) printf("-X");
else printf("-X^(%ld)", p->expon);
return;
}
if (p->coeff < 0)
{
if (p->expon == 0) printf("%ld", p->coeff);
else if (p->expon == 1) printf("%ldX", p->coeff);
else printf("%ldX^(%ld)", p->coeff, p->expon);
}
else
{
if (p->expon == 0) printf("+%ld", p->coeff);
else if (p->expon == 1) printf("+%ldX", p->coeff);
else printf("+%ldX^(%ld)", p->coeff, p->expon);
}
}
86
STRUCTURES
SQUENTIELLES
COMPLEXES
RAPPELS DE COURS
3.1 PILES
Pour beaucoup dapplications, les seules oprations effectuer sur les listes sont des insertions et
des suppressions aux extrmits. Dans les piles les insertions et les suppressions se font une seule
extrmit, appele sommet de pile.
Les piles sont aussi appeles LIFO, pour Last-In-First-Out, cest--dire dernier entr, premier
sorti ; une bonne image pour reprsenter une pile est une ... pile dassiettes : cest en haut de la pile
quil faut prendre ou mettre une assiette !
Les oprations sur les piles sont : tester si une pile est vide, accder au sommet dune pile,
empiler un lment, retirer llment qui se trouve au sommet (dpiler).
On peut utiliser pour implmenter les piles toutes les reprsentations possibles pour les listes.
On va parler en dtail dune reprsentation chane, mais il faut comprendre que la reprsentation
contigu est tout fait possible.
3.1.1 Reprsentation contigu des piles
Les lments de la pile sont rangs dans un tableau, et lon conserve aussi lindice du sommet de
pile.
Dfinition dune structure de pile ralise avec un tableau
Pseudo code formel
STRUCTURE tStack
content : T[n]
top : entier
Implantation C
87
La pile vide est reprsente par tout enregistrement dont le champ top contient 1, car ce champ
reprsente lindice de la dernire case du tableau content de stockage des donnes.
5
STRUCTURE pile
sommet : liste
nb_elt : entier
Implantation C
88
3.1. Piles
89
STRUCTURE file
donne : T[n]
tte : entier
fin : entier
Implantation en C
90
STRUCTURE poste
info : T
*suivant : poste
TYPE *ptr_poste : poste
STRUCTURE file
tte : ptr_poste
fin : ptr_poste
nb_elt : entier
Implantation en C
FONCTION init()
VAR f : file
DEBUT
f.tete NULL
f.fin NULL
f.nb_elt 0
RETOURNER f
FIN
91
92
Algorithme dfiler
Remarque
Une file se reprsente beaucoup mieux en dynamique (reprsentation chane).
Utilisation des files : tous les cas o lon dispose dune ressource destine tre utilise
par plusieurs programmes, les cas o plusieurs donnes sont manipules par cette ressource.
Exemple : le buffer dimprimante est gr en file dattente.
Exemple de gestion dun buffer clavier en liste circulaire (le dernier poste dune
liste circulaire est rattach au premier)
On a un ensemble des donnes homognes gres sur un principe de lecture/criture concurrente :
lecture et criture peuvent se produire de manire simultane mais sont conditionnes par lautre
opration.
Une lecture ne peut tre faite que si une criture a dj t faite, mais une criture ne peut tre faite
que si la lecture a t effectue sous peine de perdre linformation. Plus gnralement : il sagit dune
file dattente particulire o lon na pas besoin de produire toutes les donnes pour commencer les
consommer. Le nombre de postes est fix au dpart et ne varie plus au cours du programme.
Il faut crire deux algorithmes qui utilisent respectivement un pointeur de lecture et un pointeur
dcriture :
Lire : cest prendre linformation rfrence par lecteur, condition que cela soit possible et
dplacer le pointeur de lecture sur le poste suivant.
93
crire : cest mettre une information rfrence par le pointeur Ecriture si cela est possible, et
Figure 3.1
On ne peut pas lire si rien na t crit ou si linformation a t dj lue. On ne peut pas crire si
le poste contient une information encore non lue.
Exemple
Figure 3.2
criture
C\> copy a: .
le fait de taper RC entame la lecture
On tape dir trs rapidement
94
Figure 3.3
Modle statique
Pseudo code formel
STRUCTURE elt
info : T
lire : boolen
Implantation C
SRUCTURE liste_circ
donne : elt[n]
lecteur : entier
crivain : entier
95
Implantation C
Initialisation
Dclarations
Donne : int n
Donne modifie : liste_circ *l
En-tte : void init(liste_circ *l);
Algorithme
Lecture
Donne : int n
Donne modifie : T *elt__lire, liste_circ *l
Rsultat de type boolen
En-tte : int lecture(liste_circ *l, T *elt__lire)
FONCTION lecture(*l : liste_circ, *elt__lire : T)
VAR ok : boolen
DEBUT
SI ldonne[llecteur].lire = faux ALORS
ok faux
SINON
ok vrai
*elt__lire ldonne[llecteur].info
ldonne[llecteur].lire faux
llecteur (llecteur + 1) mod n
FINSI
RETOURNER ok
FIN
96
criture
Donne : int n, T elt_ecr
Donne modifie : liste_circ *l
Rsultat de type boolen
En-tte : int criture(T elt_ecr, liste_circ *l);
FONCTION criture(elt_ecr : T, *l : liste_circ) : boolen
VAR ok : boolen
DEBUT
SI ldonne[lecrivain].lire = vrai ALORS
ok faux
SINON
ok vrai
ldonne[lecrivain].info elt_ecr
ldonne[lecrivain].lire vrai
lecrivain (lecrivain + 1) mod n
FINSI
RETOURNER ok
FIN
Modle dynamique
Pseudo code formel
STRUCTURE elt
info : T
lire : boolen
*suivant : elt
TYPE *ptr_elt : elt
STRUCTURE liste_circ
lire : ptr_elt
ecrire : ptr_elt
Implantation C
Rien dans les types ne traduit que le premier lment de la liste est le successeur du dernier :
ceci devra tre rendu explicite dans les algorithmes.
En approche statique il sagit de lopration modulo N
En approche dynamique il sagit du passage au suivant
****
crire un algorithme pour afficher une liste chane dentiers sous la forme suivante :
Si la liste est linaire :
Nom_liste: .->[elt1 ]->[elt2 ]->...->[eltn ]->[eltlast ] |
Si certains maillons contigus sont doublement chans, afficher ]<->[ au lieu de ]->[.
Apportez une attention particulire aux traitements spcifiques de dbut, milieu et fin de liste ainsi
quaux cas spciaux comme la liste vide ou singleton.
***
crire un algorithme qui cre une liste circulaire ordonne dentiers partir dun tableau non
ordonn dentiers.
Exercice 3.3 Raliser le chanage arrire dune liste doublement chane
Concevoir un algorithme qui ralise le chanage arrire dune liste doublement chane dont seul le
chanage avant a t effectu.
Exercice 3.4 Inverser pile et file
**
Problmes
**
PROBLMES
Problme 3.1 Problme de Joseph
****
crire un algorithme permettant de lire deux entiers positifs n et k. Construire une liste circulaire
dans laquelle seront enregistrs les nombres 1, 2, 3, ..., n, dans cet ordre. En commenant partir
du nud contenant 1, supprimer successivement tous les kimes nuds de la liste, en effectuant un
parcours circulaire dans celle-ci et jusqu ce que tous les nuds aient t supprims. Ds quun
nud est supprim, le suivant dans la boucle est considr comme la nouvelle tte de liste et ainsi
de suite. Si n = 8 et k = 3, par exemple, la suppression des nuds contenant les huit entiers se fait
dans cet ordre :
3, 6, 1, 5, 2, 8, 4, 7
(Ce procd peut tre illustr par un groupe compos lorigine de n personnes formant un cercle,
que lon limine successivement en dsignant le kime de ceux restant dans le cercle que lon
continue de parcourir).
Problme 3.2 Mesurer la longueur dune liste de type Heqat
*****
Soit une liste simplement chane de type Heqat, cest--dire obtenue par lopration concat(&l,
sublist(&l, n)), concevoir un algorithme pour en mesurer la longueur en nombre de nuds, qui
retourne dune part la longueur de la partie amont linaire, et dautre part la longueur (primtre)
de la partie circulaire en aval.
Vous devrez respecter les contraintes suivantes :
pas de modification de la liste pendant le traitement (lecture seule) ;
pas de copie de linformation des nuds traverss.
STRUCTURE lQueue
head : list
queue : list
length : entier
99
STRUCTURE lStack
top : list
depth : entier
// quelques fonctions utiles, on fournit leurs en-ttes
FONCTION newEmptyStack() : lStack*
FONCTION isEmptyStack(*p_s : lStack) : boolen
FONCTION sPush(e : entier, *p_s : lStack)
FONCTION sPop(*pe : entier, *p_s : lStack) : entier
Implantation en C
lStack *newEmptyStack();
int isEmptyStack(lStack *p_s);
void sPush(int e, lStack *p_s);
int sPop(int* pe, lStack* p_s);
CORRIGS
DES EXERCICES
Spcification de lalgorithme
{
list head = l;
printf("%s : .", nom);
if (head->prev == NULL) printf("->[%d]", head->content);
else printf("<->[%d]", head->content);
while (l->succ != NULL && l->succ != head)
{
l = l->succ;
if (l->prev == NULL) printf("->[%d]", l->content);
else printf("<->[%d]", l->content);
}
(l->succ == NULL) ? printf(" |") : printf(" ...");
}
printf("\n");
}
Dans cet exercice, la circularit de la liste produire est accessoire. Il est naturel de penser
travailler sur une liste linaire standard puis de finaliser en la refermant sur elle-mme avec un
concat(&l, &l).
Il sagit de raliser un tri :
Soit en triant avant ou aprs lappel au constructeur (mthode en deux temps) :
soit tri du tableau avant dappeler ce constructeur,
soit tri de la liste aprs lavoir construite.
Soit en triant au fur et mesure de la construction de la liste (mthode en flux tendu) :
soit au moment de choisir le prochain lment insrer en tte de liste (itration non linaire
sur le tableau),
soit au moment de choisir lemplacement dinsertion dans la liste de llment suivant du
tableau (itration linaire du tableau).
Avec une difficult (mais quelle lgance) supplmentaire si on choisit dinsrer dans une liste
Ci-aprs deux solutions (mthode en flux tendu), lune itrative qui parcourt le tableau, et qui pour
chaque tape cre puis insre un nouveau maillon dans la liste, puis se termine en refermant la
chane sur elle-mme, lautre, plus compacte, rcursive, qui travaille sur une liste circulaire initie
avec le premier lment du tableau.
102
Spcification de lalgorithme
103
Implantation C
Mthode rcursive
Spcification de lalgorithme
Implantation C
Remarque
On peut liminer le recours la variable found et mieux factoriser la boucle ditration avec la
forme logique quivalente suivante :
while
(
!(l->succ == l_head)
&&
106
(
!(val > l->content)
||
(val > l->succ->content)
||
(l->content > l->succ->content)
)
&&
(
(val > l->content)
||
!(val > l->succ->content)
||
!(l->content > l->succ->content)
)
) l = l->succ;
Spcification de lalgorithme
FINSI
RETOURNER
FIN
Implantation C
void doubleChain(list l)
{
if (l == NULL) return;
list head = l;
while (l->succ != NULL && l->succ != head)
{
if (l->succ->prev == NULL) l->succ->prev = l;
l = l->succ;
}
if (l->succ != NULL) head->prev = l;
}
FAIT
RETOURNER inverse
FIN
Implantation C
Inversion de pile
Spcification de lalgorithme
{
sPop(&val, p_s);
/// dpilement depuis la pile dentre *p_s
sPush(val, p_rev_s); /// empilement dans la pile rsultat
sPush(val, p_tmp_s); /// : pour reconstruction de *p_s
}
while (! isEmptyStack(p_s));
/// reconstruction de *p_s :
do
{
sPop(&val, p_tmp_s);
sPush(val, p_s);
}
while (! isEmptyStack(p_tmp_s));
free(p_tmp_s); /// Soyons diligents avec la mmoire !!
return p_rev_s;
}
void testStackReversing()
{
printf("\n\nInversion dune file :\n\n");
int val = 10;
lQueue *p_q = newEmptyQueue();
do qPush(val, p_q); while (--val > 0);
printListGraph(p_q->head, "input queue:");
lQueue *p_rev_q = reverseQueue(p_q);
printListGraph(p_rev_q->head, "reversed queue:");
printListGraph(p_q->head, "unchanged input queue:");
}
Spcification de lalgorithme
int triangularSum(unsigned n)
{
if (n == 0) return 0;
pile p = initPile();
int val = n;
int res = 0;
do empiler(val, &p);
while (--val > 0);
do
{
depiler(&val, &p);
res += val;
}
while (! estPileVide(p));
return res;
}
On procde de la mme manire que pour le cas dune liste simplement chane, ceci prs que
lalgorithme est plus simple.
En effet, le problme pour une liste simplement chane consiste reprer le prdcesseur direct
du point dinsertion ou dlimination, et implique donc un parcours linaire en repartant de la tte.
Dans le cas dune liste doublement chane, on dispose dun accs direct sur llment prcdent
de la chane, liminant ainsi cette difficult.
Pour le reste, il sagit de mettre jour les prdcesseurs en sus des successeurs.
Les problmes dinsertion/suppression en dbut ou fin de listes, vs. en milieu de liste restent peu
prs les mmes quavec une liste simplement chane.
Insertion
Spcification de lalgorithme
// k : lment insrer
// status code : 0 : ok, -1 : non ok
// Type abstrait : list inserer(list l, int k, element e);
FONCTION insert(*pl : list, place :list , k : entier) : boolen
VAR noeudk, last, prec : list
DEBUT
// la place concerne est-elle bien dans la liste ?
SI appartient(place, *pl) ALORS
RETOURNER faux
FINSI
noeudk newSingletionList(k) // cration dun nud (cf p 87)
// cas de la liste vide
SI *pl= NULL ALORS
*pl noeudk
RETOURNER vrai
FINSI
// cas de la place en tte de liste
SI place = *pl ALORS // noeudk mis en tte de liste
noeudksucc *pl
noeudkprec NULL
*pl noeudk
RETOURNER vrai
FINSI
// cas de la place en fin de liste
SI place = NULL ALORS
last lastElement(*pl) // on trouve le dernier lment de la liste
lastsucc noeudk
noeudkprev last
RETOURNER vrai
FINSI
// autre cas : place en milieu de liste
prec placeprev
precsucc noeudk
noeudksucc place
nudkprev prec
placeprev noeudk
RETOURNER vrai
FIN
Implantation C
// k : lment insrer
// status code : 0 : ok, -1 : ko
// Type abstrait : list inserer(list l, int k, element e);
int insert(list* pl, list place, int k)
{
// vrifier que la place est bien dans la liste concerne
// (mme mthode que pour lexercice 2.5)
if (!(areConvergent(place, *pl))) return -1;
list K = newSingleton(k);
// cas de la liste vide
if (*pl == NULL)
{
*pl = K;
return 0;
}
// cas tte de liste :
if (place == *pl)
{
K->succ = *pl;
K->prev = NULL;
*pl = K;
return 0;
}
// cas fin de liste :
if (place == NULL)
{
list last = lastElement(*pl);
last->succ = K;
K->prev = last;
return 0;
}
// sinon, milieu de liste
list prec = place->prev;
prec->succ = K;
K->succ = place;
K->prev = prec;
place->prev = K;
return 0;
}
114
Suppression
Spcification de lalgorithme
CORRIGS
DES PROBLMES
Avec n = 12 et k = 3 :
cration de la liste circulaire ;
cration dune liste chane avec les 12 lments en croissante arithmtique de raison un en
partant de un ; puis repli de cette liste sur elle-mme grce au concat(&l, &l).
Figure 3.4
116
Liste Joseph
Phase de suppressions
Suppressions jusqu puisement de la liste : un nouvel ordre merge, celui des liminations.
Appelons donc cette nouvelle liste Josphine.
Figure 3.5
Liste Josphine
Finalisation
Pour la finalisation dune liste circulaire doublement chane (reste deux, puis un lment, il
convient dtre prudent) :
Figure 3.6
117
Architecture fonctionnelle
Figure 3.7
Fonction principale
Spcification de lalgorithme
int i = 2;
while (joseph != NULL)
{
joseph = removeKst(&joseph, k);
printf("%d", i++); printListGraph(joseph, "");
}
}
Spcification de lalgorithme
Implantation C
Implantation C
122
return prev->succ;
}
}
}
}
Spcification de lalgorithme
FAIT
FAIT
*longueurLinaire longueur
*longueurCirculaire 0
RETOURNER longueur
FIN
Implantation C
/**
* Retourne la longueur totale de la liste, et prcise,
* en modifiant les paramtres lineLength et loopLength,
* les longeurs respectives du segment amont (ventuel)
* et de la partie circulaire aval (ventuelle)
* Les valeurs possibles sont :
* 0 = 0 + 0 : liste vide
* l = l + 0 : liste linaire (longueur)
* p = 0 + p : liste circulaire (primtre)
* n = l + p : liste heqat, compose dune liste linaire en amont
*
et dune liste circulaire en aval
*/
int getHLength(list l, int* lineLength, int* loopLength)
{
/// cas de la liste vide
if (l == NULL)
{
*lineLength = 0;
*loopLength = 0;
return 0;
}
/// partir de l, il est certain quil existe au moins un lment
/// cas du sigleton circulaire
if (l->succ == l)
{
*lineLength = 0;
*loopLength = 1;
return 1;
}
/// cest partir de l que le vrai travail commence
int lineL = 0; /// la partie linaire prfixe mesure au moins un
int length = 1;
/// on mmorise la tte
list head = l;
/// tant quil est possible ditrer sans retomber sur la tte :-)
124
125
STRUCTURES
ARBORESCENTES
RAPPELS DE COURS
Un arbre est un ensemble de nuds, organiss de faon hirarchique, partir dun nud
distingu, appel racine. La structure darbre est lune des plus importantes et des plus spcifiques
de linformatique : par exemple, cest sous forme darbre que sont organiss les fichiers dans des
systmes dexploitation tels quUNIX ; cest aussi sous forme darbres que sont reprsents les
programmes traits par un compilateur...
Une proprit intrinsque de la structure darbre est la rcursivit, et les dfinitions des caractristiques des arbres, aussi bien que les algorithmes qui manipulent des arbres scrivent trs
naturellement de manire rcursive.
Figure 4.1
Les rsultats dun tournoi de tennis : au premier tour Jean a battu Jules, Marc a battu Franois, Paul a
battu Yves, et Luc a battu Pierre ; au deuxime tour Jean a battu Marc, et Paul a battu Luc ; et Jean a
gagn en finale contre Paul.
Figure 4.2
Le pedigree dun cheval Zoe ; son pre est Tonnerre et sa mre est Belle ; la mre de Belle est Rose et
le pre de Belle est clair...
127
Figure 4.3
Une expression arithmtique dans laquelle tous les oprateurs sont binaires...
4+5*8
La structure darbre binaire est utilise dans trs nombreuses applications informatiques ; de
plus les arbres binaires permettent de reprsenter les arbres plus gnraux.
4.1.1 Dfinition
Le vocabulaire concernant les arbres informatiques est souvent emprunt la botanique ou la
gnalogie ; tant donn un arbre B = <o, B1, B2> :
o est la racine de B.
B1 est le sous-arbre gauche (sag) de la racine de B, (ou, plus simplement, le sous-arbre gauche
de B), et B2 est son sous-arbre droit (sad).
On dit que C est un sous-arbre de B si, et seulement si : C = B, ou C = B1, ou C = B2, ou C est
un sous-arbre de B1 ou de B2.
On appelle fils gauche (respectivement fils droit) dun nud la racine de son sous-arbre gauche
(respectivement sous-arbre droit), et lon dit quil y a un lien gauche (respectivement droit) entre
la racine et son fils gauche (respectivement fils droit).
Si un nud ni a pour fils gauche (respectivement droit) un nud nj , on dit que ni est le pre de
nj (chaque nud na quun seul pre).
Deux nuds qui ont le mme pre sont dits frres.
Le nud ni est un ascendant ou un anctre du nud nj si, et seulement si, ni est le pre de nj ,
ou un ascendant du pre de nj ; ni est un descendant de nj si, et seulement si ni est fils de nj , ou
ni est un descendant dun fils de nj .
Tous les nuds dun arbre binaire ont au plus deux fils :
un nud qui a deux fils est appel nud interne ou point double
un nud sans fils est appel nud externe ou feuille.
Donc, un arbre binaire est :
soit vide ;
soit constitu dun lment de type T et dau plus deux arbres binaires.
4.1.2 Reprsentation
La reprsentation la plus naturelle reproduit la dfinition rcursive des arbres binaires. Elle peut
tre ralise en allouant la mmoire soit de faon chane soit de faon contigu.
128
chaque nud on associe deux pointeurs, lun vers le sous-arbre gauche, lautre vers le sousarbre droit, et larbre est dtermin par ladresse de sa racine. Lorsque larbre est tiquet, on
reprsente dans un champ supplmentaire linformation contenue dans le nud.
Implantation en C
Si a est un pointeur sur la racine de larbre, alors a = NULL correspond un arbre vide ; a->sag
pointe sur le fils gauche de a ; a->sad pointe sur le fils droit de a ; a->info permet daccder au
contenu du nud.
On ne parle darbres binaires que par lintermdiaire des algorithmes qui utilisent le type arbre.
4.1.3 Algorithmes de parcours dun arbre binaire
Il y a deux types de parcours :
Parcours en profondeur dabord Examiner compltement un chemin et passer au chemin
suivant tant quil en reste.
Figure 4.4
129
Figure 4.5
Exemple
Figure 4.6
Parcours en prordre
On affiche : a c m u t b y o
130
Il ny a pas de priorit sur le parcours des sous-arbres. On pourrait commencer par traiter le sad
avant le sag.
Ordre
Principe
Cet algorithme nous permet dobtenir les informations dans un ordre total. On peut rentrer les
informations dans un arbre binaire de faon trie.
Exemple
Figure 4.7
Parcours en ordre
On affiche : u m c t a y o b
131
Postordre
SI arbre nest pas vide :
parcourir en post-ordre le sag
parcourir en post-ordre le sad
traiter la racine
Algorithme
Donne : ptr_arbre a
Figure 4.8
Parcours en postordre
On affiche : u m t c o y b a
132
Ordre :
croissant : sag < racine <= sad
dcroissant : sad >= racine > sag
Figure 4.9
Pas de notion de retour arrire, on ne parcourt quune branche de larbre car on sait sil est plus
grand ou plus petit. Donc, le parcours est naturellement dichotomique.
Dunod La photocopie non autorise est un dlit
Principe
point ALORS
point ALORS
sag
sad
133
Algorithme
Donne : ptr_arbre a, T x
Rsultat de type boolen
Cet algorithme renvoie la premire occurrence du terme cherch, cest--dire quil ne considre
que la premire fois o il rencontre llment cherch.
Si on recherche la nime occurrence de llment dans larbre, il faut utiliser un compteur.
Ajout dun lment dans un ABOH
Principe
134
Exemple
Figure 4.10
On veut ajouter 10 et 50
Figure 4.11
On veut ajouter 5 et 15
Figure 4.12
On veut ajouter 6 et 12
Figure 4.13
135
*aa est en donne modifie, donc on gre bien le lien avec la rcursivit.
1) Le mode de transmission par rfrence cre automatiquement le lien entre le pre et le fils
2) Ce qui ne marche pas :
q *aasad
ajout(x, &q)
Dans ce cas le lien est cass, car q est une variable locale. Cest ce quil ne faut surtout pas
faire !!!
Suppression simple :
libration mmoire
mise jour du pointeur concern dans le pre
Figure 4.14
136
Figure 4.15
Figure 4.16
On veut supprimer le 8
Figure 4.17
Si un arbre est dsquilibr, ses performances sont instables. Les performances dpendent de la
faon dentrer les informations.
La recherche nest plus rellement dichotomique dans un arbre dsquilibr.
Dfinition dun arbre quilibr
quilibre parfait : pour tout nud de larbre, la valeur absolue de la diffrence entre le nombre
des nuds du sad et le nombre des nuds du sag est infrieure ou gale 1.
|ng nd | 6 1
138
Exemples
Figure 4.18
Figure 4.19
quilibre partiel : pour tout nud de larbre, la valeur absolue de la diffrence entre la hauteur
du sad et la hauteur du sag est infrieure ou gale 1.
|hg hd | 6 1
Il existe des procds pour viter le dsquilibre darbres : par exemple, les arbres AVL.
Principe
Figure 4.20
Balance :
-1 : hsag = hsad + 1
0 : hsad=hsag
+1 : hasd = hsag + 1
Algorithme
Donne ptr_arbre a
Rsultat de type entier
SINON
n 1 + maximum(compter(asag), compter(asad))
FINSI
RETOURNER n
FIN
quilibre parfait
Algorithme
Donne ptr_arbre a
Rsultat de type entier
141
Figure 4.21
**
crire un algorithme permettant dafficher tous les entiers enregistrs dans les feuilles dun arbre
binaire (en ignorant les entiers contenus dans tous les autres nuds).
Exercice 4.3 Rechercher itrativement un lment dans un ABOH
***
crire un algorithme permettant de dterminer si un arbre binaire donn est ou nest pas un arbre
ABOH.
Exercice 4.5 Mesurer la hauteur dun ABOH
**
***
***
****
crire un algorithme qui retourne la moyenne des lments positifs et celle des lments ngatifs
dun arbre (0 est considr ici comme un positif).
142
Problmes
****
crire un algorithme qui retourne la liste chane des lments dun arbre dentiers, qui ne sont pas
divisibles par leur parent et que leur parent ne divise pas.
Citez au moins deux faons de construire des arbres pour lesquels cet algorithme retourne ncessairement la liste de tous ses lments.
Exercice 4.10 Produire des coupes transversales dun arbre
***
Coupe transversale dun arbre sur un niveau donn, restitue sous forme de liste.
crire un algorithme qui retourne la coupe transversale.
PROBLMES
Problme 4.1 Le triangle Chinois de Pascal
****
Isaac Newton a donn une formule gnrale du dveloppement de la puissance nime dun binme :
n
(x + y) =
n
X
Cnk x nk y k
k=0
n!
= Cnnk
k! (n k)!
n
k
n!
= Cnn = 1
0!n!
C00 =
0!
= C00 = 1
0!0!
Il vous est demand de raliser lalgorithme itratif qui construit le triangle sous forme dun arbre
binaire non quilibr, jusqu un niveau de profondeur n donn, dont la racine contient la valeur
C00 , le fils gauche de la racine C10 , le fils droit de la racine C11 , puis, ds la profondeur n = 2, dont
0
les fils gauche et droit du fils le plus gauche de profondeur n 1 (contenant Cn1
) contiennent
respectivement les valeursCn0 et Cn1 , et dont tous les nuds situs droite de ces deux fils (contenant
k1>0
Cnk>1 ) soient les fils droits de leur pre de niveau n 1 (contenant Cn1
).
143
Figure 4.22
Le .h vous est donn, et lalgorithme peut tre ralis en pseudo code ou directement en C :
typedef struct treeNode
{
int contenu;
struct treeNode *sag, *sad;
} treeNode;
typedef treeNode *tree;
void buildPascalTriangle(tree* t, unsigned n);
*****
144
pair s N s
n
n+1
impair snN sn+1
snN
2
= 3snN + 1
Problmes
Dans cet interlude, il vous est demand de raliser une fonction rcursive qui construit un arbre de
Syracuse de racine un entier donn e, de profondeur donne q, dont les branches sont toutes les
suites de Syracuse qui convergent vers e en q tapes.
Pour cela, il suffit de procder une inversion de la suite.
Partons de e, le terme de convergence vis, et racine de larbre : e peut tre le successeur direct de
2e, et ventuellement de (e 1)/3 si cet entier existe et sil est impair. Disons que e a ncessairement
un fils droit 2e, et ventuellement un fils gauche (e 1)/3. En appliquant rcursivement ce procd,
on peut donc construire un arbre binaire de profondeur quelconque dont chaque branche est lune
des suites de Syracuse qui converge vers e.
Figure 4.23
Arbre de Syracuse
crire une fonction qui retourne le prdcesseur entier impair dun entier donn sil existe, et 0
sinon.
145
crire une fonction rcursive qui construit larbre de Syracuse partir dune feuille donne contenant la valeur e et dvelopp jusqu la profondeur p. Si un fils gauche (e 1)/3 vaut 0 ou 1 une
tape quelconque de la rcursion (i.e. e vaut 1 ou 4), ce fils gauche nest pas cr.
PRAMBULE
STRUCTURE treeNode
content : <lment>
*sag, *sad : treeNode
TYPE *tree : treeNode
TYPE *ptree : tree
Implantation en C
Qelques fonctions utilitaires facultatives pour amliorer la lisibilit algorithmique de vos implmentations :
tree newSingletonTree(unsigned content)
{
tree t = malloc(sizeof(treeNode)); // allocation mmoire
t->content = content;
// affectation du contenu
146
t->sag = NULL;
t->sad = NULL;
return t;
}
int isEmptyTree(tree t) {return (t == NULL);}
int isSingletonTree(tree t) {
return isEmptyTree(t->sag) && isEmptyTree(t->sad);
}
CORRIGS
DES EXERCICES
Spcification de lalgorithme
Pour donner une porte plus gnrale la fonction demande, et pour rutiliser des fonctionnalirts
dveloppes dans des exercices prcdents, nous nous proposons dextraire les feuilles dun arbre
sous forme de liste chane, laquelle pourra tre affiche laide dune fonction dimpression de
liste.
Spcification de lalgorithme
Lerreur frquente consiste ne vrifier que la condition fils gauche < parent < fils droit
sur tous les nuds et non pas descendants gauches < parent < descendants droits.
Donnons donc la mauvaise et la bonne solution toutes fins utiles.
Spcification de lalgorithme
isBinarySearchTree(tsag)
FINSI
SI (isEmptyTree(tsag)) ALORS
RETOURNER (minOfTree(tsad) > tcontent) ET
isBinarySearchTree(tsad)
FINSI
RETOURNER
(maxOfTree(tsag) < tcontent) ET isBinarySearchTree(tsag)
ET
(minOfTree(tsad) > tcontent) ET isBinarySearchTree(tsad)
FIN
// Attention, cette fonction nest pas dfinie pour un arbre vide !
FONCTION minOfTree(t : tree) : entier
VAR min, sagMin, sadMin : entier
DEBUT
min t->content
SI ( isEmptyTree(tsag))ALORS
sagMin minOfTree(tsag)
SI (sagMin < min) ALORS
min sagMin
FINSI
FINSI
SI ( isEmptyTree(tsad)) ALORS
sadMin minOfTree(tsad)
SI (sadMin < min) ALORS
min sadMin
FINSI
FINSI
RETOURNER min
FIN
// Attention, cette fonction nest pas dfinie pour un arbre vide !
FONCTION maxOfTree(t : tree) : entier
VAR max, sagMax, sadMax : entier
DEBUT
max tcontent
SI ( isEmptyTree(tsag)) ALORS
sagMax maxOfTree(tsag)
SI (sagMax > max) ALORS
max sagMax
FINSI
FINSI
SI ( isEmptyTree(tsad)) ALORS
sadMax maxOfTree(tsad)
150
tude
Lquilibre partiel dun arbre binaire est dtermin sur la base du diffrentiel des mesures des
hauteurs des fils droit et gauche de la racine.
Par extension, nous proposons une solution qui peut sappuyer sur diffrentes mesures, autres que
la hauteur, pour valuer le statut dquilibre partiel dun arbre dans une varit largie de termes.
Le dnominateur commun de toutes les mesures, est quelles sont calcules selon un procd
rcursif.
Spcification de lalgorithme
/**
* Retourne lcart en valeur absolue entre
* la mesure de la partie droite et celle de la partie gauche
* Le second paramtre utilis indique la mesure utilise
* 1 : masse - 2 : profondeur - 3 : surface
*/
FONCTION getBalance(t : tree, selectedMeasure : entier) : entier
VAR balance : entier
151
DEBUT
SI (isEmptyTree(t) OU isSingletonTree(t)) ALORS
RETOURNER 1
FINSI
balance 0
SI (selectedMeasure = 1) ALORS
balance getMass(tsag) - getMass(tsad)
SINON
SI (selectedMeasure=2) ALORS
balance getDepth(tsag) - getDepth(tsad)
SINON
SI (selectedMeasure=3) ALORS
balance getSurface(tsag) - getSurface(tsad)
FINSI
FINSI
FINSI
SI (balance < 0) ALORS
RETOURNER -balance
SINON
RETOURNER balance
FINSI
FIN
FONCTION getSurface(t : tree) : entier
DEBUT
SI (isEmptyTree(t)) ALORS
RETOURNER 0
FINSI
SI (isSingletonTree(t)) ALORS
RETOURNER 1
SINON
RETOURNER getSurface(tsag) + getSurface(tsad)
FINSI
FIN
FONCTION getMass(t : tree) : entier
DEBUT
SI (isEmptyTree(t)) ALORS
RETOURNER 0
SINON
RETOURNER 1 + getMass(tsag) + getMass(tsad)
FINSI
FIN
152
Spcification de lalgorithme
FINSI
SI (tsad =
6 NULL) ALORS
qPush(tsag, q)
FINSI
FAIT
FIN
Lordre de parcours de larbre importe peu, de mme que la mthode (itrative ou rcursive), mais
il est ncessaire de parcourir lintgralit de larbre pour calculer quatre grandeurs : les sommes et
dnombrements respectifs des lments respectivement positifs et ngatifs de larbre.
Spcification de lalgorithme
Fonction principale simple
/**
* Retourne la moyenne des lments positifs
* et celle des lments ngatifs dun arbre dentiers
* Si larbre est vide, ppo_av et pne_av sont fixs -1,
* Sinon retour respectif des deux moyennes en valeur absolue
*/
FONCTION signedAveragesMain(t : tree, *ppo_av : rel, *pne_av : rel)
VAR po_sum, po_count, ne_sum, ne_count : entier
DEBUT
signedAverages(t, &po_sum, &po_count, &ne_sum, &ne_count)
SI po_count = 0 ALORS
*ppo_av -1
SINON
*ppo_av po_sum / po_count
FINSI
SI ne_count = 0 ALORS
*pne_av -1
SINON
*pne_av ne_sum / ne_count
FINSI
FIN
Fonction secondaire rcursive
/**
* Fonction (prive) rcursive qui calcule respectivement
* les sommes et comptes de positifs et ngatifs
* ppo_sum : somme des positifs, ppo_count : nombre de positifs,
* pne_sum : somme des ngatifs, pne_count : nombre de ngatifs
154
*/
FONCTION signedAverages(t : tree, *ppo_sum : entier,
*ppo_count : entier, *pne_sum : entier, *pne_count : entier)
VAR sag_po_sum, sag_po_count, sag_ne_sum, sag_ne_count : entier
VAR sad_po_sum, sad_po_count, sad_ne_sum, sad_ne_count : entier
DEBUT
/// Condition darrt
SI (t = NULL) ALORS
*ppo_sum 0
*ppo_count 0
*pne_sum 0
*pne_count 0
RETOURNER
FINSI
signedAverages(tsag,
&sag_po_sum, &sag_po_count, &sag_ne_sum, &sag_ne_count)
signedAverages(tsad,
&sad_po_sum, &sad_po_count, &sad_ne_sum, &sad_ne_count)
*ppo_sum sag_po_sum + sad_po_sum
*ppo_count sag_po_count + sad_po_count
*pne_sum sag_ne_sum + sad_ne_sum
*pne_count sag_ne_count + sad_ne_count
SI (tcontent < 0) ALORS
(*pne_sum) (*pne_sum) - tcontent
(*pne_count) (*pne_count)+1
SINON
(*ppo_sum) (*ppo_sum) + tcontent
(*ppo_count) (*ppo_count)+1
FINSI
FIN
Ralisation en C
Une fonction principale ralise et retourne les deux moyennes en sappuyant sur une fonction
rcursive secondaire qui calcule les quatre grandeurs.
Fonction principale simple
155
void signedAverages(tree t,
int *ppo_sum, int *ppo_count, int *pne_sum, int *pne_count)
{
/// Condition darrt
if (t == NULL)
{
*ppo_sum = 0;
*ppo_count = 0;
*pne_sum = 0;
*pne_count = 0;
return;
}
/// Sinon :
int sag_po_sum, sag_po_count, sag_ne_sum, sag_ne_count;
int sad_po_sum, sad_po_count, sad_ne_sum, sad_ne_count;
signedAverages(t->sag,
&sag_po_sum, &sag_po_count, &sag_ne_sum, &sag_ne_count);
signedAverages(t->sad,
&sad_po_sum, &sad_po_count, &sad_ne_sum, &sad_ne_count);
*ppo_sum = sag_po_sum + sad_po_sum;
*ppo_count = sag_po_count + sad_po_count;
*pne_sum = sag_ne_sum + sad_ne_sum;
*pne_count = sag_ne_count + sad_ne_count;
if (t->content < 0)
{
(*pne_sum) -= t->content;
(*pne_count)++;
}
else
{
(*ppo_sum) += t->content;
(*ppo_count)++;
}
}
Pour tester
/**
* Retourne un arbre parfait, de racine de valeur n et profondeur p,
* dont chaque sag a pour valeur celle de son parent -1,
* et dont chaque sad a pour valeur celle de son parent +1
*/
tree treeAveragesTestTree(int n, unsigned p)
156
{
/// Condition darrt
tree t = newSingletonTree(n);
if (p == 0) return t;
/// Sinon
p--;
t->sag = treeAveragesTestTree(n - 1, p);
t->sad = treeAveragesTestTree(n + 1, p);
return t;
}
void testEx2()
{
printf(">> tree averages\n");
tree t = treeAveragesTestTree(0, 10);
simplePrintTreeGraph(t, "ttest");
double po_av, ne_av;
signedAveragesMain(t, &po_av, &ne_av);
int po_sum, po_count, ne_sum, ne_count;
signedAverages(t, &po_sum, &po_count, &ne_sum, &ne_count);
signedAveragesMain(t, &po_av, &ne_av);
printf("ttest positives average : %.4f (%d/%d) -",
po_av, po_sum, po_count);
printf("negatives average : %.4f (%d/%d)\n",
ne_av, ne_sum, ne_count);
}
Analyse
Il sagit dextraire dun arbre tous les fils premiers avec leur parent pour les restituer sous forme de
liste chane. Rien nindique quil sagit dextraire une liste sans rptitions (voir exemple darbre
avec rotation trois lments ci-dessous, dans la section ralisation C/pour tester ).
Nous nous proposons de raliser un algorithme bas sur le calcul du PGCD(parent, enfant) laide
de lalgorithme dEuclide, esprant ainsi obtenir un bonus de la part du correcteur : le PGCD doit
valoir 1 pour caractriser la primalit respective du parent et de lenfant. Mais la simple vrification
de la nullit du modulo de lun par lautre est suffisante pour dterminer cette proprit.
Peu importe le type de parcours choisi (pr, in, ou post fixe), lalgorithme est naturellement
rcursif et bas sur la fusion de proche en proche des listes denfants premiers de chaque sousarbre : les parents des feuilles retournent les premires listes singletons, lesquelles sont enrichies
et concatnes (lopration de fusion) au fur et mesure du dpilement de la pile dappel, et
finalement fusionnes en une seule et unique liste au moment du dpilement final de lappel initial
de la fonction.
157
Mthodes de construction
Un arbre vident dont tous les fils sont premiers avec leur parent est larbre qui ne contient que des
nombres premiers. Une autre faon de construire un tel arbre est deffectuer la rptition dun motif
approprie de trois lments premiers entre eux, par exemple 4, 9 et 25 : par exemple, 4 prend la
place de racine, 9 celle de fils gauche, 25 celle de fils droit, puis 4 prend la place de fils gauche de
9 et de 25, et 25 celle de fils droit de 9 et 9 celle de fils droit de 25, et ainsi de suite...
Il existe une multitude de faons de procder la construction dun arbre qui possde cette proprit
de primalit relative parent/enfant.
Cependant, lexactitude mathmatique invite prciser quen ralit, il nexiste aucune faon de
construire un tel arbre qui nexiste pas. En effet, la racine de larbre ne peut tre premire avec son
parent qui nexiste pas.
Spcification de lalgorithme
/**
* Retourne la liste chane des lments dun arbre dentiers,
* premiers avec leur parent
*/
FONCTION getPrimeChildren(t : tree) : list
VAR sagprimechildren, sadprimechildren, primechild : list
DEBUT
SI (t = NULL) ALORS
RETOURNER NULL
FINSI
sagprimechildren NULL
sadprimechildren NULL
SI (tsag =
6 NULL) ALORS
sagprimechildren getPrimeChildren(tsag)
SI (gcd(tcontent, tsagcontent) = 1) ALORS
primechild newSingletonList(tsagcontent)
SI (primechild 6= NULL) ALORS
primechildsucc sagprimechildren
sagprimechildren primechild
FINSI
FINSI
FINSI
SI (tsad 6= NULL) ALORS
sadprimechildren getPrimeChildren(tsad)
SI (gcd(tcontent, tsadcontent) = 1) ALORS
primechild newSingletonList(tsadcontent)
SI (primechild 6= NULL) ALORS
primechildsucc sadprimechildren
sadprimechildren primechild
FINSI
158
FINSI
FINSI
RETOURNER *concat(&sagprimechildren, &sadprimechildren)
FIN
/** Algorithme dEuclide accessible dans les Elemens **/
FONCTION gcd(numerator : entier, denominator : entier) : entier
DEBUT
SI (denominator = 0) ALORS
RETOURNER numerator
SINON
RETOURNER gcd(denominator, numerator % denominator)
FINSI
FIN
Ralisation en C
list getPrimeChildren(tree t)
{
if (t == NULL) return NULL;
list sagprimechildren = NULL;
list sadprimechildren = NULL;
list primechild;
if (t->sag != NULL)
{
sagprimechildren = getPrimeChildren(t->sag);
if (gcd(t->content, t->sag->content) == 1)
{
primechild = newSingletonList(t->sag->content);
if (primechild != NULL)
{
primechild->succ = sagprimechildren;
sagprimechildren = primechild;
}
}
}
if (t->sad != NULL)
{
sadprimechildren = getPrimeChildren(t->sad);
if (gcd(t->content, t->sad->content) == 1)
{
primechild = newSingletonList(t->sad->content);
if (primechild != NULL)
{
primechild->succ = sadprimechildren;
159
sadprimechildren = primechild;
}
}
}
return *concat(&sagprimechildren, &sadprimechildren);
}
unsigned gcd(unsigned numerator, unsigned denominator)
{
if (denominator == 0) return numerator;
else return gcd(denominator, numerator % denominator);
}
Pour tester
/**
* Retourne un arbre parfait de profondeur p,
* et dont les 3 valeurs v1, v2, v3 initient la rcurrence suivante :
*
v1 est la valeur de la racine,
*
v2 celle de son fils gauche, v3 celle de son fils droit,
*
si un nud a pour valeur v(i),
*
alors son fils gauche a pour valeur v((i + 1) mod 3),
*
et son fils droit v((i + 2) mod 3)
*/
tree primeChildrenTestTree(unsigned p,
unsigned v1, unsigned v2, unsigned v3)
{
/// Condition darrt
tree t = newSingletonTree(v1);
if (p == 0) return t;
/// Sinon
p--;
t->sag = primeChildrenTestTree(p, v2, v3, v1);
t->sad = primeChildrenTestTree(p, v3, v1, v2);
return t;
}
void testPrimeChildren()
{
printf(">> tree prime children\n");
tree p = primeChildrenTestTree(3, 1, 2, 3);
simplePrintTreeGraph(p, "ptest (123)");
list primechildren = getPrimeChildren(p);
printListGraph(primechildren, "primechildren (123)");
p = primeChildrenTestTree(3, 1, 2, 1);
simplePrintTreeGraph(p, "ptest (121)");
160
primechildren = getPrimeChildren(p);
printListGraph(primechildren, "primechildren (121)");
p = primeChildrenTestTree(3, 2, 2, 2);
simplePrintTreeGraph(p, "ptest (222)");
primechildren = getPrimeChildren(p);
printListGraph(primechildren, "primechildren (222)");
}
/**
* Retourne sous forme de liste, la coupe tranversale dun arbre
* du niveau de pronfondeur depth
*/
FONCTION getLevelList(t : tree, depth : entier) : list
VAR sagll, sadll : list
DEBUT
SI (getDepth(t) < depth) ALORS
RETOURNER NULL
FINSI
SI (depth = 1) ALORS
SI (t = NULL) ALORS
RETOURNER NULL
SINON
RETOURNER newSingletonList(tcontent)
FINSI
SINON
depth depth-1
sagll getLevelList(tsag, depth)
sadll getLevelList(tsad, depth)
RETOURNER *concat(&sagll, &sadll)
FINSI
FIN
161
CORRIGS
DES PROBLMES
Figure 4.24
Ce qui est demand
De passer dune structure qui nest pas un arbre mais sen approche un format darbre. Pour cela,
on transforme les filiations croises en filiations simples en supprimant les relations parent-enfant
sur la gauche, sauf pour le ct droit de larbre.
Figure 4.25
162
Stratgie de rsolution
Utilisation de la formule de rcurrence de Pascal
Comme on supprime les filiations gauches, il faut quun parent gauche puisse passer la valeur
du parent droit , non reli, tout nouveau fils droit quil cre.
Si on reste sur une vision arbre , a peut devenir compliqu, en particulier en cherchant une
mthode rcursive.
Or, si lon constate que larbre nest rien dautre quune liste de listes, la premire liste tant la
branche de droite (1, 1, 1, ...), puis la branche (1, 2, 3, ...), puis (1, 3, 6, ...), alors le problme
apparat plus simple et se prte naturellement une procdure itrative deux boucles.
La figure 4.26 (la mme sous une perspective peine diffrente) devrait vous aider vous le
reprsenter.
Figure 4.26
163
Spcification de lalgorithme
Pour que le gain entre le calcul de toutes factorielles, et lexploitation de la formule de rcurrence
de Pascal ne soit pas seulement abstrait, valuons la complexit algorithmique compare entre les
deux approches en fonction dun paramtre n, qui reprsente le niveau de profondeur de larbre,
avec premier niveau pour n = 0.
Pour calculer une factorielle dun entier n, il faut raliser n 1 produits avec affectation de chaque
rsultat intermdiaire dans une variable temporaire.
Notons c(f (n)), le cot du calcul dune fonction f (n) : c (n!) = 2 (n 1)
Pour calculer une combinaison, nous avons trois factorielles, un produit et une division, trois
affectations :
165
Cnk
=c
n!
k! (n k)!
La complexit est donc linaire comparer avec une complexit en temps constant (deux oprations,
une somme et une affectation) pour un calcul par la formule de rcurrence de Pascal.
Si lon passe lchelle de la ligne de profondeur n, il y a n + 1 termes calculer, et donc le cot
en oprations pour une ligne du triangle de Pascal est 2 (2n 1) (n + 1) avec la mauvaise mthode
et 2 (n + 1) avec la bonne.
lchelle dun triangle de profondeur n, nous obtenons donc un cot de :
n
X
2 (2i 1) (i + 1)
i=1
n
X
i=1
Il sagit de vrifier dans un premier temps que (n 1) est divisible par 3. Cela se vrifie facilement
laide du test (n - 1) % 3 = 0, soit m % 3 = 0 avec m = n - 1.
Mais ce nest pas suffisant, puisquil faut par ailleurs que m/3 soit impair, car sinon, le terme trouv
aurait pour successeur sa moiti et non pas n, cest--dire que son successeur serait m/6 et non pas
n.
Limparit de m/3 peut se vrifier laide de (m/3) % 2 = 1 ou 6= 0, mais si le rsultat invalide
cette imparit, une division aura t effectue pour rien.
Il suffit de constater que si m est la fois multiple de 3, et multiple de 2, alors il est multiple de 6,
et donc m % 6 = 0.
La condition dimparit de m/3 peut donc tre vrifie par m % 6 6= 0. Si les deux conditions sont
valides, alors et seulement alors, on calcule et on retourne m/3.
Cette optimisation est importante, car cette fonction utilitaire est appele chaque tape de la
rcursion ci-aprs, et que son cot dexcution reprsente un cot lmentaire facteur direct du
cot global de lexcution de lalgorithme principal.
Ralisation
/** Retourne le prdcesseur impair de n tel que n = prec * 3 + 1
sil existe 0 sinon **/
FONCTION precInf(n : entier) : entier
VAR m : entier
DEBUT
m n - 1
166
SI ((m % 3)
RETOURNER
FINSI
SI ((m % 6)
RETOURNER
FINSI
RETOURNER m
FIN
=
6 0) ALORS
0
= 0) ALORS
0
/ 3
Optimisation
Une solution plus efficace peut tre trouve moyennant un petit raisonnement darithmtique
modulaire :
n N : p N : n = 3 p + 1 impair [ p]
!k N : n = 3 (2k + 1) + 1 = 6k + 4
n 4 (6)
Et donc la fonction se rduit :
FONCTION precInf(n : entier) : entier
DEBUT
SI n%6 = 4 ALORS
RETOURNER (n-1)/3
SINON
RETOURNER 0
FINSI
FIN
t newSingletonTree(1)
*pt t
FINSI
n tcontent
maxDepth maxDepth-1
tsad newSingletonTree(2 * n)
syracuseTreeGrowth(&(tsad), maxDepth)
prec_inf_n precInf(n)
SI (prec_inf_n > 1) ALORS
tsag newSingletonTree(prec_inf_n)
syracuseTreeGrowth(&(tsag), maxDepth)
FINSI
FIN
168
AUTOMATES
RAPPELS DE COURS
5.1 HISTORIQUE
Alan Turing a dcrit 1936 un modle de machine idale auquel il a laiss son nom. Dautres
modles furent proposs, qui sont tous quivalents la machine de Turing. Church a dmontr
dans sa thse que ces modles sont les plus gnraux possibles.
Ces travaux amenrent distinguer entre les fonctions qui sont calculables en thorie de celles
qui ne le seraient mme pas. (Toute fonction calculable peut tre calcule par la machine de Turing
en temps (nombre doprations) fini).
Un modle plus simple mais fort utile est celui dun automate fini. Un automate fini est une
machine abstraite constitue dtats et de transitions. Cette machine est destine traiter des mots
fournis en entre : lautomate passe dtat en tat, suivant les transitions, la lecture de chaque
lettre de lentre. Lautomate est dit fini car il possde un nombre fini dtats distincts : il ne
dispose donc que dune mmoire borne, indpendamment de la taille de la donne sur laquelle on
effectue les calculs.
Un automate fini forme un graphe orient tiquet, dont les tats sont les sommets et les transitions les arcs tiquets.
Les automates finis fournissent un outil de construction dalgorithmes particulirement simple.
Il existe plusieurs types de machines tats finis. Les accepteurs produisent en sortie une
rponse oui ou non , cest--dire quils acceptent (oui) ou rejettent (non) lentre. Les
systmes de reconnaissance classent lentre par catgorie. Les capteurs produisent un certain
rsultat en fonction de lentre.
Les automates finis peuvent caractriser des langages (cest--dire des ensembles de mots) finis
(le cas standard), des langages de mots infinis (automates de Rabin, automates de Bchi), ou encore
divers types darbres (automates darbres).
Les machines tats finis se rencontrent galement dans les circuits digitaux, o lentre, ltat
et le rsultat sont des vecteurs de bits de taille fixe (machines de Moore et machines de Mealy).
Dans les machines de Mealy, les actions (sorties) sont lies aux transitions, tandis que dans les
machines de Moore, les actions sont lies aux tats.
169
Chapitre 5 Automates
Figure 5.1
A = {a, b}; Q = {0,1, 2,3, 4}; I = {0}; T = {4}
170
Tableau 5.1
tat
(entre)
(sortie)
tat rsultant
en lisant a
en lisant b
Remarque
Si vous regardez cet automate de prs, vous verrez que nous avons libell les tats de telle faon
que lautomate se trouve dans ltat numro i ssi le plus long suffixe du mot dj lu qui est en
mme temps un prfixe de abaa, est de longueur i.
On peut montrer que cet automate ralise de faon optimale lalgorithme de recherche de la
squence abaa dans un texte : il rsout pour ce mot lalgorithme du string matching utilis par
exemple dans les diteurs de textes.
La lecture
Lautomate prend un mot (constitu de symboles de son alphabet) en entre et dmarre dans son ou
ses tat(s) initial(-aux). Il parcourt ensuite le mot de gauche droite : si lautomate se trouve dans
un tat p et le symbole lire est a, alors sil existe(nt) un ou des tat(s) qi tels que la transition
( p.a.qi ) existe, il passe aux tats qi , et de suite. (Sil y a plus dun tat qi , on considre chacun
deux sparment). La lecture se termine soit si la lettre suivante ne peut pas tre lue (il ny a pas
de transition y correspondant), soit si on a atteint la fin du mot. Si, la fin de la lecture, lautomate
est dans un tat final (accepteur), on dit quil accepte lentre ou quil la reconnat. Autrement, on
dit quil la rejette.
Notation
w
Pour un mot w A on note p q sil existe un chemin de ltat p ltat q obtenu en lisant w.
Dfinitions formelles
a
Chapitre 5 Automates
p q ssi r Q tq p r et r q.
uv
Un mot w A est donc reconnu par lautomate fini sil existe un chemin i t o i I et
t T c.--d. quen partant dun tat initial et quen lisant le mot w, on atteint un tat terminal
la fin de la lecture.
Le langage L reconnu par lautomate fini est lensemble de tous les mots reconnus.
Tableau 5.2
N
N mod3
reste
reste
3n
6n
6n + 1 = 3 2n + 1
3n + 1
6n + 2 = 3 2n + 2
6n + 3 = 3 (2n + 1)
3n +2
6n + 4 = 3 (2n + 1 ) + 1
6n + 5 = 3 (2n + 1) + 2
Tableau 5.3
tat
tat rsultant
0
Figure 5.2
En fait, cet automate est bon pour reconnatre des nombres en criture binaire avec nimporte
quel reste de la division entire par 3, au prix de choisir quel tat est terminal. Ltat terminal
0 correspond au reste 0 (divisibilit par 3) ; ltat terminal 1, au reste 1 ; ltat terminal 2, au
reste 2.
172
Figure 5.3
Cet automate reconnat tous les mots qui se terminent par abaa. Il est trs facile
construire.
Le fait quil nest pas dterministe se manifeste en ce que, en lisant a partir de ltat 0, on peut
aller et ltat 0, et ltat 1, ce qui ne serait pas possible pour un automate dterministe.
On va montrer dans ce qui suit que lon peut toujours construire, partir dun automate fini
quelconque, un autre automate qui est dterministe et qui lui est quivalent (reconnat le mme
langage).
Chapitre 5 Automates
Figure 5.4
Dterminisation
Tableau 5.4
tat
(entre)
(sortie)
tats rsultant
en lisant a
en lisant b
(0)
(0,1)
(0)
(0,1)
(0,1)
(0,2)
(0,2)
(0,1,3)
(0)
(0,1,3)
(0,1,4)
(0,2)
(0,1,4)
(0,1)
(0,2)
Explications
On commence par ltat initial qui dans ce cas particulier concide avec ltat initial de lautomate
non dterministe (0), car notre automate non dterministe nen a quun seul. (Sil en avait plusieurs,
on les aurait regroups pour former ltat initial de lautomate dterministe).
partir de cet tat (0), en a on va vers les tats (0), (1) de lautomate initial. Pour lautomate
dterministe, on les regroupe dans ltat quon appelle (0,1). En b, on va vers ltat (0) de lautomate
initial. Donc, (0) est lui aussi un tat de lautomate dterminis.
Chaque fois quon obtient un tat de lautomate dterminis, on le met dans la colonne de gauche
pour agir sur cet tat par les lettres de lalphabet. Ainsi on obtient la deuxime ligne, o figure un
nouvel tat (0,2). On procde ainsi tant quil y reste des tats non traits.
Les tats finaux sont ceux qui contiennent un tat final de lautomate initial. Dans notre cas, il
ny en a quun : (0,1,4).
174
Figure 5.5
Automate dterminis
Figure 5.6
Dterminisation
Tableau 5.5
tat
(entre)
(sortie)
tats rsultant
en lisant a
en lisant b
(0)
(0,1)
(0)
en lisant c
(0,1)
(0,1)
(0)
(2)
(2)
Nous remplaons les traits ( pas de transition ) dans ce tableau par un nouvel tat appel
poubelle (P), qui a la proprit que toutes les transitions partir de cet tat reviennent sur luimme :
175
Chapitre 5 Automates
Tableau 5.6
tat
(entre)
(sortie)
tats rsultant
en lisant a
en lisant b
(0)
(0,1)
(0)
en lisant c
P
(0,1)
(0,1)
(0)
(2)
(2)
Voici donc lautomate dterminis complet, dans lequel (0) est rest ltat initial, et le seul tat
terminal reste (2) :
Figure 5.7
Remarque
Il ny a pas trop de sens de parler dun automate non dterministe complet ou pas complet. De
mme, mme si introduire un tat poubelle dans un automate non dterministe est possible
et ne nuit pas son fonctionnement, ceci nest pas utile pour la dterminisation de cet automate.
Normalement, on nintroduit un tat poubelle si besoin est quune fois lautomate est devenu
dterministe.
Un automate est accessible si nimporte quel tat est accessible partir dun tat initial (c..d.
sil existe un chemin y menant partir dun tat initial).
Un automate est coaccessible si partir de nimporte quel tat on peut accder un tat
terminal.
Accessible + coaccessible = mond.
Un ensemble de mots X est un langage reconnaissable ssi il existe un automate fini D qui le
reconnat.
Le complment dun langage reconnaissable (lensemble de mots sur le mme alphabet nappartenant pas au langage en question) est encore reconnaissable.
Soit D un automate dterministe et complet reconnaissant le langage X .
Pour construire un automate reconnaissant le complment de X , il suffit de prendre comme
ensemble dtats terminaux le complment de lensemble dtats terminaux de D.
Exemple
Construisons un automate sur A = {a, b} qui reconnat lensemble des mots nayant pas aba en
facteur.
On commence par la construction dun automate (non dterministe) reconnaissant tous les mots
ayant aba en facteur.
Figure 5.8
Appliquons lalgorithme de dterminisation :
Tableau 5.7
tat
(initial)
tats rsultant
en lisant a
en lisant b
(0)
(0,1)
(0)
(0,1)
(0,1)
(0,2)
(0,2)
(0,1,3)
(0)
(terminal)
(0,1,3)
(0,1,3)
(0,2,3)
(terminal)
(0,2,3)
(0,1,3)
(0,3)
(terminal)
(0,3)
(0,1,3)
(0,3)
Figure 5.9
Cet automate dterministe et complet reconnat tous les mots ayant aba en facteur. Il a trois
tats terminaux : (0,1,3), (0,2,3) et (0,3).
177
Chapitre 5 Automates
Maintenant pour obtenir un automate (lui aussi dterministe et complet) reconnaissant tous les
mots qui nont pas aba en facteur, il suffit de faire de tous les tats terminaux, des tats non
terminaux, et vice versa :
Figure 5.10
Ici, on a obtenu un automate complet sans quil y ait besoin de le complter en introduisant
ltat poubelle. videmment, ceci nest pas toujours le cas. Si on a affaire un automate non
complet, on na pas le droit de remplacer directement les tats terminaux par des tats
non terminaux et vice versa : il faut dabord le complter. Alors ltat poubelle (qui ne
peut pas tre terminal) devient un tat terminal de lautomate reconnaissant le complment du
langage.
Minimisation
Pour tout langage reconnaissable il existe le plus petit (c.--d. contenant le plus petit nombre dtats)
automate dterministe qui le reconnat, et il est unique : cest lautomate minimal du langage.
Algorithme de minimisation par la mthode des quivalences
Choisir un tat dans chaque groupe de la partition Q f inal en tant que reprsentant de ce groupe.
Les reprsentants seront les tats de lautomate fini dterministe rduit D .
a
En ralit, il est parfois plus facile, au lieu de choisir un reprsentant, marquer les groupes finaux
comme tels, ou bien les renommer par, par exemple, A, B, C ... , et remplacer dans les transitions
tout tat par le nom de son groupe :
a
a
Si er , e A, r B o les groupes A, B Q f inal , alors dans lautomate minimis on a AB o
maintenant on considre A et B comme tats de lautomate minimis. Cette remarque deviendra
tout fait claire la fin de lexemple suivant.
Exemple 1
Minimisons lautomate dterministe complet :
Figure 5.11
179
Chapitre 5 Automates
Tableau 5.8
tats rsultant
tat
(initial)
(terminal)
en lisant a
en lisant b
Tableau 5.9
tat
Groupes rsultant
en lisant a
en lisant b
II
Tableau 5.10
tat
Groupes rsultant
en lisant a
en lisant b
II
180
Tableau 5.11
Groupes rsultant
tat
en lisant a
en lisant b
II
II
Tableau 5.12
Groupe
Reprsentant
0 ou 2
II
III
IV
Il devient clair maintenant quon peut marquer les tats de lautomate minimis soit par un
reprsentant, soit par les chiffres I, II, III, IV suivant le groupe du dernier tape, soit par le contenu
du groupe (dans ce cas, ltat initial sera marqu (0,2) ; cette dernire solution est pratique tant
que le groupe consiste en peu dtats).
Lautomate minimis :
Figure 5.12
181
Chapitre 5 Automates
On peut maintenant poser la question suivante : pourquoi les tats 0 et 2 sont rest ensemble
aprs la minimisation ? Quy a-t-il de spcial concernant ces deux tats par rapport aux autres ?
Regardons nouveau lautomate initial :
Figure 5.13
et introduisons la terminologie suivante :
on dit que la chane w distingue ltat s de ltat t si quand on commence dans ltat s et lit w
on arrive dans un tat terminal, et si on commence dans t et lit w on arrive dans un tat non
terminal ou vice-versa.
Ici, tous les tats peuvent tre distingus par une chane ou une autre, sauf les tats 0 et 2. Cest
facile voir : et partir de 0, et partir de 2 on arrive en 1 en lisant un nombre quelconque (y
compris zro) de b et un a ; puis, comme on est dans le mme tat (1), il ne nous reste que les
mmes chanes pour arriver ltat final (ici, il y en a un seul). Donc, les tats non distinguables
se fondent dans un mme tat (en loccurrence (0,2)) de lautomate minimis.
En fait, on peut montrer que lalgorithme de minimisation quon a expliqu, est bas sur la
recherche des tous les groupes qui peuvent tre distingus par une chane ou I = {1}, T = {1,2}
une autre.
Exemple 2
Voici un automate dterministe complet avec deux tats terminaux, avec lalphabet A = {0, 1} :
Figure 5.14
La procdure de minimisation donne :
I = {1},
182
T = {1,2}
Figure 5.15
c : p0 p1 . . . pn q0 q1 . . . qm
et p0 = p qm = q
Figure 5.16
a
cas pn = p0 ou pn = p0 ).
183
Chapitre 5 Automates
Exemple
a) Prenons un automate asynchrone :
Figure 5.17
(A1)
Ici, suivant le raisonnement prcdent, il existent les chemins : 1a1, 1a2, 1b2, 1c3, 2b2, 2c3.
b) Donc cet automate est quivalent lautomate non dterministe suivant :
Figure 5.18
(A2)
Tableau 5.13
tat
Figure 5.19
184
tats rsultant
en lisant a
en lisant b
en lisant c
1,2
1,2
1,2
(A3)
Tableau 5.14
tat
Figure 5.20
tats rsultant
en lisant a
en lisant b
en lisant c
1,2
1,2
1,2
(A4)
d) Mais en ralit, il est plus simple de se passer de ltape (b) et construire un automate
dterministe directement partir dun automate asynchrone.
Redessinons lautomate (A1) :
Figure 5.21
(A1)
Ltat initial de lautomate dterministe correspondant (A1) est (1,2), car en lisant le mot vide on
arrive et en 1, et en 2. Continuons dterminiser en marquant dans les colonnes correspondant
la lecture de chaque caractre, tous les tats o on arrive aprs la lecture du caractre en
question, c..d. non seulement ltat o mne la flche tiquete par ce caractre (disons, a ),
mais aussi tous les tats o on peut arriver en lisant a, a etc. Dans notre cas particulier, il ny
a pas de telles transitions, mais il faut systmatiquement en tenir compte.
185
Chapitre 5 Automates
Tableau 5.15
tat
Figure 5.22
tats rsultant
en lisant a
en lisant b
en lisant c
1,2
1,2
(A5)
On voit que lautomate (A5) nest pas le mme que lautomate (A3) ou (A4) ! Pourquoi ?
Parce que lautomate dterministe reconnaissant un certain langage nest pas unique, ce nest
quen minimisant quon arrive un automate unique. En minimisant (A4) et (A5), on doit obtenir
la mme chose.
Minimisons (A4) donc on rappelle la table de transitions :
Tableau 5.16
tat
tats rsultant
en lisant a
en lisant b
en lisant c
1,2
1,2
1,2
Tableau 5.17
tat
Groupes rsultant
en lisant a
en lisant b
en lisant c
1,2
Tableau 5.18
tat
Groupes rsultant
en lisant a
en lisant b
en lisant c
1,2
(2) se spare.
Groupes rsultant
en lisant a
en lisant b
en lisant c
1,2
Le groupe (1,(1,2)) na aucun chance se scinder en deux, car ds le dbut (1) et (1,2) ont les
mmes transitions. Donc on a termin, et on obtient :
Figure 5.23
ce qui est le mme automate que le rsultat de complter lautomate (A5) ! (en minimisant
lautomate (A5), la seule chose quil reste faire cest de le complter, autrement il est dj
minimal).
possibles dune personne (clibataire, mari, divorc, veuf) et dont les flches correspondent
aux changements de situation possible. tiqueter ces flches par M (mariage), D (divorce) et V
(veuvage).
Exercice 5.2 Construire des automates finis qui reconnaissent les langages suivants :
(a) Lensemble des mots sur lalphabet {0, 1} dont lavant dernier symbole est 0.
187
Chapitre 5 Automates
(b) Lensemble des mots sur lalphabet {0, 1} qui commencent et qui finissent par 0. (On
par cinq.
Exercice 5.4 Construire un automate fini reconnaissant les lignes contenant des commentaires
sont inutiles ?
Figure 5.24
Exercice 5.6 Voici cinq automates nots An (ils nont rien voir les uns avec les autres, ce nest
pas une squence !). Lalphabet A = {a, b} sauf pour A4 o il est {0,1}. Pour chacun de ces
automates :
Dcrire L(An ) en langage ordinaire (sauf pour A5 ).
Caractriser An (dire sil est accessible, coaccessible, mond...)
Calculer lautomate dterministe quivalent An .
188
Figure 5.25
A1
Figure 5.26
A2
Figure 5.27
A3
Figure 5.28
A4
Figure 5.29
A5
189
Chapitre 5 Automates
Figure 5.30
Exercice 5.8 Construire des automates dterministes qui reconnaissent les langages suivants. En
dduire des automates dterministes qui reconnaissent les complmentaires de ces langages :
(a) Lensemble des mots sur lalphabet {0, 1} qui contiennent exactement trois fois le symbole
1.
(b) Lensemble des mots sur lalphabet {0, 1} qui contiennent au moins un 1.
Exercice 5.9 Construire un automate fini reconnaissant lensemble des mots sur lalphabet A =
Figure 5.31
b)
Figure 5.32
190
en = (Q n , I , T , E), n > 1
A
avec :
A = {a, b}
Q n = {0,1, ..., n 1}
I = T = {0}
Comme flches tiquetes par a lensemble de (q.a.((q + 1)mod n)) pour q : 0 6 q 6 n 1
Comme flches tiquetes par b lensemble de (q.b.0) et (q.b.q) pour q : 1 6 q 6 n 1
(attention : la premire ingalit commence par 0, et la seconde, par 1)
Dessiner f
A3 et f
A4 .
Puis montrer que le dterminis complet de f
An a toujours 2n tats.
Exercice 5.1
Figure 5.33
Exercice 5.2
Solution (a)
Lautomate le plus simple qui reconnat lensemble des mots sur lalphabet {0, 1} dont lavant
dernier symbole est 0, est le suivant :
191
Chapitre 5 Automates
Figure 5.34
Solution (b)
Lautomate le plus simple qui reconnat lensemble des mots sur lalphabet {0, 1} qui commencent
et qui finissent par 0, est le suivant :
Figure 5.35
Solution (c)
Un des nombreux automates quivalents qui reconnaissent lensemble des mots sur lalphabet
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +, , .} reprsentant en C les constantes numriques, est le suivant :
Figure 5.36
Solution (d)
Un des nombreux automates quivalents qui reconnat lensemble des mots sur lalphabet {0, 1}
comportant au moins une fois le motif 10 et au moins une fois le motif 01, est le suivant :
192
Figure 5.37
Solution (e)
Un des nombreux automates quivalents qui reconnat le langage {an bm |n + m pair}, est le suivant :
Figure 5.38
Exercice 5.3 Ajout dun 0 la fin dun nombre binaire le multiplie par 2.
Ajout dun 0 la fin dun nombre binaire le multiplie par 2 et lui ajoute 1.
Tableau 5.20
N
N mod 5
Ajout dun 0
la fin de
lcriture binaire
donne N :
N mod 5
Ajout dun 1
la fin de
lcriture binaire
donne N :
N mod 5
5n
10n
10n + 1
5n + 1
10n + 2
10n + 3
5n + 2
10n + 4
10n + 5
5n + 3
10n + 6
10n + 7
5n + 4
10n + 8
10n + 9
Chapitre 5 Automates
Tableau 5.21
tat
En lisant 0
En lisant 1
Figure 5.39
Exercice 5.4
Figure 5.40
Exercice 5.5 tats 2 et 3 ne mnent pas la sortie. Ils sont donc inutiles. Le langage reconnu par
Figure 5.41
Il consiste en mots a, aba, ababa, abababa... quon peut crire comme a(ba)* ou (ab)*a.
194
Exercice 5.6
Solution A1
Cet automate lit tous les mots se terminant par bbb.
Il est mond.
Dterminisation
Tableau 5.22
Figure 5.42
tat
01
01
012
012
0123
0123
0123
A1
Solution A2
Cet automate lit tous les mots avec un b en 3ime position de la fin.
Il est mond.
Dterminisation
Tableau 5.23
tat
Initial
01
01
02
012
012
023
0123
02
03
013
Terminal
0123
023
0123
Terminal
023
03
013
Terminal
03
01
Terminal
013
02
012
195
Chapitre 5 Automates
Figure 5.43
Solution A3
Cet automate lit tous les mots contenant au moins trois caractres ...a...b...a... o ... peut tre
vide.
Il est mond.
Dterminisation
Tableau 5.24
tat
01
01
01
01
012
012
0123
012
0123
0123
0123
Figure 5.44
Solution A4
Cet automate lit tous les mots consistant en des 0 ou en des 0 suivis dun seul 1, ou le mot 1.
196
Tableau 5.25
tat
AB
AB
ABC
BC
ABC
ABC
BC
BC
Figure 5.45
Ici on a obtenu donc un automate dterministe avec poubelle. Mais il y avait une poubelle ds
le dbut ltat C dont on ne pouvait pas revenir. On aurait pu construire dabord un automate non
dterministe plus simple qui reconnat le mme langage quA4 :
Figure 5.46
et le dterminiser. En ce faisant on arrive un automate dterministe nettement plus simple que
tout lheure :
197
Chapitre 5 Automates
Tableau 5.26
tat
AB
AB
AB
Figure 5.47
Dans lexercice 10 on va voir que cet automate est en effet minimal.
Solution A5
On ne dcrit pas le langage (cest trop compliqu ce stade).
Il est mond.
Dterminisation :
Tableau 5.27
Figure 5.48
198
tat
12
12
12
(Maintenant il devient bien plus facile de dcrireL(A5 ) si lon veut : il consiste en tous les mots
commenant par a, finissant par a, et o tout b est entour par des a de faon ne jamais avoir bb).
Exercice 5.7
Tableau 5.28
tat
02
12
01
01
12
12
01
01
Figure 5.49
Exercice 5.8
Solution (a)
Un automate dterministe complet qui reconnat lensemble des mots sur lalphabet {0, 1} qui
contiennent exactement trois fois le symbole 1 se construit directement, sans quon ait besoin de
construire un automate non dterministe dabord :
Figure 5.50
199
Chapitre 5 Automates
Figure 5.51
Solution (b)
Figure 5.52
Cet automate nest pas dterministe, il faut le dterminiser.
Dterminisation
Tableau 5.29
Figure 5.53
200
tat
01
01
01
01
Figure 5.54
Exercice 5.9 Nous construisons dabord un automate qui lit tous les mots qui se terminent par
abaab. Puis nous le dterminiserons et complterons, et puis nous ferons de tous les tats finaux,
tats non finaux et vice versa. La minimisation se fera ensuite.
Figure 5.55
Tableau 5.30
tat
01
01
01
02
02
013
013
014
02
014
01
025
025
013
201
Chapitre 5 Automates
Figure 5.56
Cet automate est dj complet, sinon il faudrait le complter en ajoutant une poubelle.
Lautomate dterministe lisant tous les mots ne se terminant pas par abaab, sobtient comme
suit :
Figure 5.57
Il nest pas minimisable, c.--d. quil est dj minimal. Pour le voir, il faut essayer de le minimiser :
Q0 = {I , I I } avec I = {025}, I I = {0,01, 02,013, 014}
Tableau 5.31
202
tat
01
II
II
01
01
02
II
II
02
013
II
II
013
014
02
II
II
014
01
025
II
025
013
II
II
01
II
II
01
01
02
II
II
02
013
II
II
013
014
02
III
II
01
II
II
01
01
02
II
II
02
013
IV
II
Tableau 5.34
tat
01
II
II
01
01
02
II
203
Chapitre 5 Automates
Tableau 5.35
tat
01
01
012
012
0123
0123
0123
Figure 5.58
Minimisation
01
01
012
012
0123
II
01
01
012
II
Les deux tats du groupe I se sont spars, donc tous les tats se sont spars lautomate tait
minimal ds le dbut.
204
Solution A2
Reprenons lautomate dterministe complet A2 mais modifions la notation des tats pour plus de
clart :
Tableau 5.38
En renommant
les tats
tat
tat
01
01
02
012
012
023
0123
02
03
013
Terminal
0123
023
0123
Terminal
023
03
013
Terminal
03
01
Terminal
013
02
012
Initial
Groupe
non terminal
Groupe terminal
Tableau 5.39
tat
II
II
II
II
II
II
II
II
Q1 = {I, II, III, IV} avec I = {A, B}, II={C, D}, III={E, F}, IV={G, H}.
205
Chapitre 5 Automates
Tableau 5.40
tat
b
I
II
II
III
III
IV
IV
III
III
IV
IV
II
II
01
01
01
01
012
012
0123
012
0123
0123
0123
Figure 5.59
Minimisation
01
01
01
01
012
012
0123
012
II
Tableau 5.43
tat
01
01
01
01
012
II
Tableau 5.44
tat
AB
AB
ABC
BC
ABC
ABC
BC
BC
Figure 5.60
Minimisation
Chapitre 5 Automates
Groupe terminal
Groupe non
terminal
Tableau 5.45
tat
AB
II
II
AB
ABC
BC
II
II
ABC
ABC
BC
II
II
BC
On peut voir immdiatement que les tats A et C se sparent, et que le groupe II se divise en deux
groupes, qui ne pourront plus se sparer. On obtient donc les tats de lautomate minimis : A, (B,
BC), (AB, ABC), et C.
Graphiquement, cet automate minimal se prsente comme :
Figure 5.61
o ltat C sert comme ltat poubelle. Lautomate dterministe correspondant A4 ntait donc
pas minimal. On voit quen le minimisant on est arriv au mme automate quon a obtenu en
dterminisant lautomate A4 initial (non dterministe) duquel on a coup sa poubelle .
Remarque
On rappelle encore une fois que la poubelle dun automate non dterministe (ltat ou le groupe
dtats dont on ne peut pas allez vers la ou les sortie(s) nest pas la mme chose que la poubelle de
lautomate dterministe qui en rsulte aprs la dterminisation.
La seule utilit de la poubelle dun automate non dterministe consiste en la possibilit de la
couper pour simplifier la dterminisation.
208
Solution A5
Figure 5.62
A5
Minimisation
12
II
12
12
II
Les deux tats du groupe I se sont spars ; donc tous les tats se sont spars, lautomate tait
minimal ds le dbut.
Exercice 5.11
Dunod La photocopie non autorise est un dlit
Solution (a)
Dterminisation
Tableau 5.47
tat
01
01
012
012
02
012
02
02
012
209
Chapitre 5 Automates
Figure 5.63
Minimisation
01
b
I
01
012
II
012
02
012
II
II
02
02
012
II
II
Figure 5.64
Solution (b)
Dterminisation
Tableau 5.49
En modifiant les
tiquettes des tats
tat
tat
02
12
01
01
12
012
12
02
012
12
012
02
Tous les tats sont des tats finaux sauf ltat poubelle.
210
Tableau 5.50
tat
II
II
II
II
II
II
II
II
II
Minimisation
III
III
III
III
II
III
III
III
Tableau 5.52
tat
III
IV
III
IV
III
IV
Figure 5.65
211
Chapitre 5 Automates
Exercice 5.12
3
tats 0, 1, 2;
flches 0.a.(1 mod 3) = 0.a.1 ; 1.a.(2 mod 3) = 1.a.2 ; 2.a.(3 mod 3) = 2.a.0 ;
1.b.0; 1.b.1; 2.b.0; 2.b.2.
Tableau 5.53
tat
0,1
0,2
Figure 5.66
4
tats 0, 1, 2, 3;
flches 0.a.(1 mod 4) = 0.a.1 ; 1.a.(2 mod 4) = 1.a.2 ;
2.a.(3 mod 4) = 2.a.3 ; 2.a.(4 mod 4) = 2.a.0 ;
1.b.0; 1.b.1; 2.b.0; 2.b.2; 3.b.0; 3.b.3.
Tableau 5.54
212
tat
0,1
0,2
0,3
Figure 5.67
n
Tableau 5.55
tat
0,1
0,2
0,3
...
...
...
n1
0, n 1
Figure 5.68
213
Chapitre 5 Automates
Dterminisation
Tableau 5.56
tat
01
02
03
...
...
...
n1
(0, n 1)
01
12
01
02
13
02
...
...
...
(0, n 1)
01
(0, n 1)
12
23
012
13
24
013
...
...
...
(1, n 1)
02
(0,1, n 1)
...
...
...
partant de ltat 0), pour que lautomate soit complet, on obtient 2n tats.
214
BIBLIOGRAPHIE
BAYNAT B., C HRTIENNE P., H ANEN C., K EDAD -S IDHOUM S., M UNIER -KORDON A. et P ICOU LEAU C. Exercices et problmes dalgorithmique 2e dition. Dunod, Paris, 2007.
C ARREZ C. Des structures aux bases de donnes. Dunod, Paris, 1994.
C ORMEN T., L EISERSON C., R IVEST R. et S TEIN C. Introduction lalgorithmique 3e dition.
Dunod, Paris, 2010.
F ROIDEVAUX C., G AUDEL M.-C. et S ORIA M. Types de donnes et algorithmes. Ediscience
International, Paris, 1994.
G UYOT J. et V IAL C. Arbres, tables et algorithmes. Eyrolles, Paris, 1992.
L UC B OUGE L., K ENYON C., M ULLER J.-M. et ROBERT Y. Algorithmique. Exercices corrigs.
Ellipses, Paris, 1993.
215
INDEX
A
accepteurs 169
adresse 18
affectation 7
afficher 8, 45
algorithme 1
allocation dynamique 23
alphabet 170, 171, 174, 176, 177, 182
arbre 127129, 131, 137139, 143
binaire 127129, 131, 142, 145
de Syracuse 145
automate
fini 169, 170, 172, 173, 176, 178
E
mond 176, 188, 195, 196, 198
quifinalit 1
ET 11
tat 169172, 174, 178, 182, 204
accepteur 171
initial 170, 174
terminal 170, 174, 177, 178, 182
valuation 13
expression 8
F
B
boucle 12
C
calcul 1
champ 29
complexit algorithmique 1
concatnation 37, 45, 51, 52
condition 9
constante 7
FIFO 90
file 90
fonction 23
appel 25
appelante 25
appele 25
argument 26
corps 24
en-tte 24
H
D
dcidabilit 1
algorithmique 1
logique 1
pratique 1
thorique 1
dterminisation 173177, 195198, 200, 201,
208210, 214
Heqat 99
I
insertion 35, 37, 43, 99, 112
point d 112
itratif 12, 45, 49, 50, 54, 63, 142, 178
217
L
LIFO 87
liste
doublement chane 44, 99, 112
linaire 35
simplement chane 38, 46, 112
R
racine 127
raisonnement 1
rcursif 45, 5052, 61, 65, 136, 142
rcursivit 127, 136
M
machine de Turing 169
minimisation 178, 182, 201
mot
vide 170
saisir 9, 40
solution 1
sommet de pile 87
sous-programme 23
structures de contrle 9
successeur 56, 112
suppression 37, 41, 43, 46, 61, 99, 112, 138
NON 11
NULL 21
T
O
OU 11
P
pile 8789
pile 89
pointeur 18, 19
prdcesseur 60, 61, 112, 145, 166
problme 1
problme de Joseph 99
pseudo code IX
218
tableaux 14
tables de vrit 11
transition 33, 169171, 174, 175, 214
table de 171
tri 2
type 5
point 20
V
variable 6
locale 24