Vous êtes sur la page 1sur 228

EXERCICES ET PROBLMES

DALGORITHMIQUE
X
Rappels de cours
X
Exercices et problmes
avec corrigs dtaills
X
Solutions en pseudo code
et en langage C
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
Dunod, Paris, 2010
Illustration de couverture : digitalvision

ISBN 978-2-10-055072-2
TABLE DES MATIRES
AVANT-PROPOS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . IX
INTRODUCTION. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
CHAPITRE 1 LES BASES DE LA PROGRAMMATION. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1 Les types de donnes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Les variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Quelques lments de syntaxe pour le langage algorithmique . . . . . . . . . . . . . . . . . 6
1.4 Oprations et oprateurs de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4.1 Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4.2 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4.3 Oprateurs arithmtiques et expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4.4 Oprateurs dentre/sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.5 Structure de contrle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5.1 Conditions et tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5.2 Excution conditionnelle dinstructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5.3 Itrations et boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.6 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.6.1 Dnition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.6.2 Reprsentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6.3 Relation entre tableaux et boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.6.4 Les tableaux plusieurs dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.7 Pointeurs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.7.1 Notion dadresse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.7.2 Dnition et contenu. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.7.3 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.8 Les sous-programmes ou fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.8.1 Dnition dune fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
V
Exercices et problmes dalgorithmique
1.8.2 Appel des fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.8.3 Les fonctions et les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.8.4 Les fonctions et les pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.9 Cration de types par le programmeur : les types composs ou structures . . . . . . 29
1.9.1 Accs aux champs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.9.2 Oprateur daffectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.9.3 Structures contenant des tableaux et des pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.9.4 Structures dnies laide de structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.9.5 Pointeurs vers les structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.9.6 Types pointeurs et raccourcis de notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.9.7 Structures et fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
CHAPITRE 2 STRUCTURES SQUENTIELLES SIMPLES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.1 Listes linaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.1.1 Dnition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.1.2 Reprsentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.1.3 Variables dynamiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.1.4 Variantes dimplantation des listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
noncs des exercices et des problmes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Corrigs des exercices et des problmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
CHAPITRE 3 STRUCTURES SQUENTIELLES COMPLEXES. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.1 Piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.1.1 Reprsentation contigu des piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.1.2 Reprsentation chane des piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.1.3 Manipulation dune pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.2 Les les . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.2.1 Reprsentation contigu des les . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.2.2 Reprsentation chane des les . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.2.3 Manipulation dune le (mthode avec deux pointeurs) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
noncs des exercices et des problmes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Corrigs des exercices et des problmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
VI
Table des matires
CHAPITRE 4 STRUCTURES ARBORESCENTES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
4.1 Arbres binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
4.1.1 Dnition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
4.1.2 Reprsentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
4.1.3 Algorithmes de parcours dun arbre binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
4.1.4 Arbres binaires de recherche (ABOH = Arbres Binaires Ordonns Horizontalement) . . . . . 132
noncs des exercices et des problmes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Corrigs des exercices et des problmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
CHAPITRE 5 AUTOMATES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
5.1 Historique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
5.2 Quelques dnitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
5.3 Linterprtation intuitive. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
5.3.1 Automates dterministes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
5.3.2 Automate asynchrone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
noncs des exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Corrigs des exercices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 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
Un contenu construit pour aller directement lessentiel
Cet ouvrage de travaux dirigs dalgorithmique est construit pour aller directement lessentiel
sans faire dimpasse sur ce qui est important, ni se disperser dans ce qui viendra point nomm
dans les tapes de votre apprentissage.
Simple daccs, il contient les chapitres classiques dune introduction lalgorithmique, avec
notamment les structures squentielles, arborescentes, et les automates.
Chaque chapitre dbute avec un rappel de cours dune vingtaine de pages suivi des noncs et
corrigs des exercices et problmes.
Pour complter cette structure classique, un chapitre introductif rsume les bases minimales de
la programmation informatique.
Les corrigs sont donns sous la forme suivante :
une ventuelle tude des stratgies de rsolution du problme pos (si celui-ci est complexe),
accompagne de schmas descriptifs de principe ;
une spcication en langage algorithmique (pseudo code) de la ou des solutions envisages ;
une ventuelle proposition de ralisation en C99 des solutions proposes.
Des schmas intuitifs
Les schmas descriptifs de principe facilitent la comprhension des principes de fonctionnement
des algorithmes proposs.
La liste suivante vous sera utile notamment pour interprter les schmas du second chapitre.
Une place quelconque Un pointeur sur une
place non vide (et donc
le dbut dune liste de
places)
Une place pointant
sur la suivante
(place
intermdiaire)
Une place
intermdiaire
contenant llment 6

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
IX
Exercices et problmes dalgorithmique
La liste vide ( un
pointeur ne pointant
sur rien)
Une place terminale
(par composition)
Un singleton (liste
un seul lment)
Une liste lments
multiples
Le cas particulier du
couple (liste deux
lments)
Reprsentation des
modications effectues
(pointills (aprs) vs.
traits pleins (avant))
Un plan de travail qui peut tre adapt
Si vous dbutez et navez jamais crit le moindre programme informatique de votre vie, la lecture
du premier chapitre vous sera ncessaire. Sinon, elle nest pas indispensable, sauf ventuellement
comme rfrence pour le langage algorithmique utilis dans les corrigs.
Si vous dmarrez avec quelques notions de programmation, les deux chapitres sur les structures
squentielles et arborescentes vous donneront les bases ncessaires pour raisonner en termes
algorithmiques et aborder par la suite des structures et algorithmes plus complexes, btis sur ces
lments de bases.
Enn, quel que soit votre niveau, le dernier chapitre sur les automates vous sensibilisera sur les
fondements mathmatiques de lalgorithmique, notamment des logiques dexcution.
Avec les structures squentielles et les approches itratives, les structures arborescentes et les
approches rcursives, et enn, avec les automates et les logiques gnrales dexcution, vous
munirez votre arc de trois cordes essentielles pour aborder la suite de votre apprentissage.
PROPOS DES AUTEURS
Nicolas Flasque
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).
X
Avant-propos
Helen Kassel
De double formation en mathmatiques (DEA obtenu en Russie) et en informatique (DEA
obtenu en France), elle a enseign linformatique en Russie, aux tats-Unis et en France. Elle
possde galement une exprience du travail en entreprise en tant quingnieur en informatique.
Enseignant en informatique et en mathmatiques lEFREI depuis plus de dix ans, elle est
actuellement le chef du dpartement mathmatiques/informatique.
Franck Lepoivre
Diplm ingnieur de lISEP en 1995, il volue dans les entreprises de nouvelles technologies en
tant que consultant IT (coauteur de XML & Java, Eyrolles 2000) puis directeur marketing produit
(prix technologia ANVAR et 01 Informatique pour Kelua Kawana en 2002). En 2004, il lance
reciproCity pour porter lanalyse sociologique dans le domaine de lintelligence conomique.
En 2007, il lance Pepper Labs pour porter les mathmatiques appliques et algorithmique vers
les entreprises et leur problmatiques mtier (modlisation et prototypage doutils danalyse
complexe, notamment dans les domaines du marketing et des neurosciences appliques). Il
intervient lEFREI en algorithmique et structures de donnes, thorie des langages et techniques
de compilation, thorie des graphes, aide la dcision et algorithmique numrique.
Boris Velikson
Diplm de Ph.D. en Physique thorique aux tats-Unis aprs un Bac+5 en Russie, il a travaill
comme chercheur en thorie des champs quantiques et puis en biophysique, dans le domaine
de modlisation de grosses molcules biologiques sur ordinateur. Depuis plusieurs annes, il
travaille comme enseignant en mathmatiques, en statistique et en informatique, dans quelques
tablissements de la rgion parisienne, des niveaux trs diffrents, en franais et en anglais.
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
QUEST-CE QUE LALGORITHMIQUE ?
Un problme est un questionnement qui appelle une solution. Mais existe-t-il seulement une
solution ?
Tout problme en induit deux autres, deux questions pralables toute tentative de rsolution, et
dont les rponses ne vont pas toujours delles-mmes, et ne sont pas ncessairement afrmatives.
Ce sont les deux questions de dcidabilit :
La premire est celle de la dcidabilit logique ou thorique : ce problme, est-il soluble ?
Construire la rponse relve des mathmatiques pures et non pas de lart algorithmique
proprement parler. Rpondre cette question par la ngative peut viter la vaine recherche dune
rponse la seconde.
La certitude dune possibilit de rsolution acquise, se pose la seconde question de la dcidabilit
algorithmique ou pratique : comment trouver la solution ?
Rsoudre en pratique un problme thoriquement soluble, cest concevoir et oprer une mthode
de raisonnement qui, partant dun nonc qualitatif et quantitatif, permet de construire en un
nombre ni dtapes, lnonc de sa solution.
Un algorithme est la description dune telle mthode de raisonnement comme succession
dtapes lmentaires et intermdiaires de rsolution, ce quon appelle communment un calcul.
Ainsi un algorithme se conoit-il naturellement comme une dcomposition dun problme en sous-
problmes plus simples, individuellement faciles rsoudre et dont la composition donne la
solution, plus complexe, du problme principal.
Mais est-ce la meilleure faon de procder ?
Si dcrire un algorithme, signie dcrire une mthode de raisonnement (un programme) qui
dtermine la solution dun problme en un nombre ni dtapes de calcul, il se peut que le temps
ncessaire ce calcul place le rsultat nal hors de porte.
Cest ici quinterviennent les notions dquinalit
1
, notion prleve sur le vocabulaire strat-
gique, et de complexit algorithmique.
Une mthode de rsolution nest jamais unique, et les stratgies alternatives, cest--dire les
diffrentes faons daboutir au mme rsultat ne sont pas tactiquement gales. Certaines sont plus
1. Notion prleve sur le vocabulaire cyberntique et stratgique, lquinalit 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.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
1
Exercices et problmes dalgorithmique
coteuses que dautres, en termes de ressources temps, en termes de ressources mnmoniques
mobilises.
Savoir valuer, avant de lexcuter, lefcacit dun algorithme, chercher systmatiquement
minimiser ce cot au moment de le concevoir, cest assurment ce qui pose lalgorithmique comme
un art.
COMMENT DEVENIR ALGORITHMICIEN ?
Lapprentissage traditionnel de lalgorithmique lude les aspects les plus formels et sophistiqus
de la dcidabilit, de la calculabilit et de la complexit, qui sils sont fondamentaux, ne sont pas
ncessairement faciles daccs.
On commence gnralement lapprentissage par la pratique de la programmation laide dun
langage simple, puis dans un second temps, on prend du recul par rapport cette premire approche,
pour dcouvrir les aspects les plus gnraux des structures de donnes et des algorithmes standards.
Enn, on aborde les lments plus mathmatiques de la complexit aprs en avoir ressenti
la ralit par lexprience programmatique.
Une tape majeure, qui fera la diffrence entre programmeur et algorithmicien, consistera
prendre de la distance avec la programmation, et se reprsenter dans toute leur gnralit,
les schmas algorithmiques, indpendamment de tout langage dimplantation. Linuence du
paradigme de programmation spcique du premier langage appris est souvent le frein qui empche
daborder lalgorithmique selon la bonne perspective.
lautre extrmit du spectre de progression, destin lingnieur en informatique accompli,
un ouvrage tel que le TAOCP
1
de Donald E. Knuth qui reprsente la quintessence de lart
algorithmique, est un ouvrage radicalement indigeste pour qui fait ses premiers pas en informatique.
QUEST-CE QUUN ALGORITHME ?
Selon lEncyclopedia Universalis un algorithme est la spcication dun schma de calcul, sous
forme dune suite [nie] doprations lmentaires obissant un enchanement dtermin .
On connat depuis lantiquit des algorithmes sur les nombres, comme par exemple lalgorithme
dEuclide qui permet de calculer le p.g.c.d. de deux nombres entiers.
Pour le traitement de linformation, on a dvelopp des algorithmes oprant sur des donnes non
numriques : les algorithmes de tri, qui permettent par exemple de ranger par ordre alphabtique
une suite de noms, les algorithmes de recherche dune chane de caractres dans un texte, ou les
algorithmes dordonnancement, qui permettent de dcrire la coordination entre diffrentes tches,
ncessaire pour mener bien un projet.
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.
Dnissons plus formellement le concept :
Un algorithme dcrit un traitement sur un certain nombre, ni, de donnes.
Un algorithme est la composition dun ensemble ni dtapes, chaque tape tant forme dun
nombre ni doprations dont chacune est :
dnie 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 ni ; par exemple
la division entire est une opration effective, mais pas la division avec un nombre inni de
dcimales.
Quelle que soit la donne sur laquelle on travaille, un algorithme doit toujours se terminer aprs
un nombre ni doprations, et fournir un rsultat.
CONCEPTION DUN ALGORITHME
La conception dun algorithme un peu compliqu se fait toujours en plusieurs tapes qui corres-
pondent des rafnements successifs. La premire version de lalgorithme est autant que possible
indpendante dune implmentation particulire.
En particulier, la reprsentation des donnes nest pas xe.
ce premier niveau, les donnes sont considres de manire abstraite : on se donne une notation
pour les dcrire ainsi que lensemble des oprations quon peut leur appliquer et les proprits de
ces oprations. On parle alors de type abstrait de donnes. La conception de lalgorithme se fait
en utilisant les oprations du type abstrait.
Pour rsoudre des problmes nous allons appliquer une dmarche descendante : on se donne la
dnition des types de donnes (on dit encore leur spcication), et on conoit lalgorithme ce
niveau.
On donne ensuite une reprsentation concrte des types et des oprations, qui peut tre encore
un type abstrait, et ceci jusqu obtenir un programme excutable.
NOTION DE STRUCTURE DE DONNES
Une structure de donnes est un ensemble organis dinformations relies logiquement, ces infor-
mations pouvant tre traites collectivement ou individuellement.
Lexemple le plus simple : le tableau monodimensionnel (un vecteur) est constitu dun certain
nombre de composantes de mme type.
On peut effectuer des oprations sur chaque composante prise individuellement mais on dispose
aussi doprations globales portant sur le vecteur considr comme un seul objet.

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
3
Exercices et problmes dalgorithmique
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.
4
1
LES BASES
DE LA PROGRAMMATION
1.1 LES TYPES DE DONNES
Un type en algorithmique est une information permettant de traduire les valeurs depuis une reprsen-
tation binaire (celle de lordinateur) vers une autre reprsentation plus adapte leur programmation
dans un langage volu. Cette notion est tellement importante que toute valeur a forcment un type.
Le rle du type est dassurer cette traduction en indiquant quelle place en mmoire occupe la valeur
et quelle est la technique de codage utilise.
Nous distinguons quatre types lmentaires en algorithmique :
Le type entier sera utilis pour stocker des valeurs entires, positives ou ngatives. Un entier
occupe quatre octets (32 bits) en mmoire.
Le type rel sera utilis pour stocker les nombres virgule. Un rel occupe huit octets (64 bits)
en mmoire.
Le type caractre sera utilis pour stocker les caractres. Un caractre occupe un octet (8 bits)
en mmoire.
Le type boolen sera utilis pour stocker les valeurs de type vrai/faux. Un boolen occupe un
octet (8 bits) en mmoire.
Attention au type dit rel . En eet, un ordinateur ne stocke ses valeurs que sur une place
limite, il ne stocke donc quun nombre limit de dcimales aprs la virgule. Les valeurs de type
rel en algorithmique ne sont donc que des valeurs approches de leur version mathmatique !
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 lacher
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
Chapitre 1

Les bases de la programmation
1.2 LES VARIABLES
Une variable est une donne quun programme peut manipuler. Tout variable possde :
Un type (entier, rel, caractre ou boolen).
Un nom ou identicateur que lutilisateur choisit ; il permet au programme de reconnatre quelle
donne il doit manipuler.
Une valeur qui peut voluer au cours du programme, mais qui doit respecter le type.
Une variable dont le type est entier ne pourra donc jamais contenir de valeur virgule.
Lidenticateur ou nom de la variable peut tre quelconque, mais doit respecter les critres
suivants :
un identicateur 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 identicateurs toto et Toto
sont diffrents ;
le nom de variable doit avoir une relation avec le rle de cette variable et tre comprhensible.
1.3 QUELQUES LMENTS DE SYNTAXE POUR LE LANGAGE
ALGORITHMIQUE
Pour crire correctement un programme en langage algorithmique, il faut fournir certaines informa-
tions lordinateur : le mot programme suivi du nom du programme, indique le nom du programme
ainsi que son point de dpart.
Avant dutiliser une variable dans un programme, il faut la dnir, cest--dire indiquer le mot
VAR, puis le nom de la variable et enn son type prcd de :.
Une variable sappelant taux, et dont le type est rel, doit tre dnie de la manire suivante :
VAR taux : rel
Cette dnition cre une variable nomme taux dans laquelle peuvent tre stocks des nombres
virgule.
Quelques exemples de dnition de variables
VAR solution_equation : rel dnit une variable nomme solution_equation dont le type
est rel ;
VAR val_1 : entier dnit une variable nomme val_1 dont le type est entier ;
VAR lettre : caractre dnit une variable nomme lettre dont le type est caractre.
6
1.4. Oprations et oprateurs de base
Il est possible de dnir plusieurs variables dun mme type en spciant le nom du type,
puis la liste des noms de variables spars par des virgules ,. Ainsi, les dnitions 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 OPRATIONS ET OPRATEURS DE BASE
1.4.1 Affectation
Lopration daffectation permet de donner (ou daffecter, do son nom) une valeur une variable.
Sa syntaxe est la suivante :
nom_de_variable valeur__affecter
Le symbole indiquant le sens de laffectation.
La valeur dune variable qui na pas subi daectation est alatoire. Elle est reprsente par un
point dinterrogation.
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 inuence sur la syntaxe :
Constantes de type entier : il suft 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 scientique 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 gurer le point
dcimal.
Constantes de type caractre Il est possible daecter 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
7
Chapitre 1

Les bases de la programmation
x 6.022E+23
ma_var Y // quivaut ma_var 101
// car le code ASCII de Y vaut 101
1.4.3 Oprateurs arithmtiques et expressions
Il est galement intressant de pouvoir effectuer des calculs et den affecter le rsultat une variable.
Nous retrouvons sans surprise les oprateurs arithmtiques les plus classiques :
Addition : +
Soustraction : -
Multiplication : *
Division : /
Modulo : %
Ces oprateurs agissent avec des constantes et/ou des variables dont la valeur est utilise pour le
calcul effectuer.
Avec ces oprateurs, les variables et les constantes, il est possible dcrire ce que lon appelle
des expressions : une expression est une suite doprateurs et de termes qui est comprhensible et
que lon peut calculer.
(x+3)/2(4x)*7 est une expression, car on peut appliquer les oprations, mais 2)+)*5 8/(9
nest pas une expression, bien que tout ce qui la compose soit des oprations, des termes, et
des parenthses !
Lors du traitement de laffectation gnrique : variable expression, lordinateur calcule dans
un premier temps la valeur numrique de lexpression fournie droite de loprateur daffectation
puis range dans un second temps cette valeur calcule dans la variable qui se trouve gauche de
loprateur daffectation.
1.4.4 Oprateurs dentre/sortie
Les oprations que nous venons daborder permettent juste de faire des calculs, mais ne permettent
pas encore de visualiser les rsultats ou dafcher du texte, ou encore de faire des saisies au clavier.
Pour cela, nous utiliserons les commandes AFFICHER et SAISIR :
AFFICHER sert, comme son nom lindique, afcher du texte ou les valeurs des variables. On
utilise afcher, suivi entre parenthses des diffrents lments faire apparatre lcran. Ces
lments sont soit du texte brut crit entre doubles guillemets, soit une expression. Dans le cas
de texte brut, ce dernier apparat tel quel lcran. Dans le cas dune expression, cest la valeur
numrique du calcul de cette expression qui est afche. Les lments successifs afcher sont
spars par une virgule.
Exemple
VAR t : rel // dfinition de la variable entire t
t 2.421
AFFICHER ("t vaut : ", t, " !") // cette instruction fera apparatre
// lcran le message suivant : t vaut 2.421 !
8
1.5. Structure de contrle
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 safche pour indiquer lutilisateur ce quil
doit faire ; cest donc au programmeur de penser afcher 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.
1.5 STRUCTURE DE CONTRLE
Les structures de contrle (branchements conditionnels et boucles) permettent un programme
de ne pas tre purement squentiel (chane linaire dinstructions).
Lexemple le plus simple traiter est celui de la rsolution dune quation du second degr dans
R (qui a deux, une ou aucune solution) en fonction de la valeur des coefcients. Le programme qui
doit rsoudre ce problme devra donc adapter son comportement en fonction des valeurs prises par
certaines variables (notamment le discriminant de lquation).
1.5.1 Conditions et tests
Une condition est une expression dont le rsultat nest pas une valeur numrique, mais VRAI
ou FAUX, qui sont les deux lments de lalgbre dite boolenne ou encore logique. Le calcul
boolen respecte un certain nombre de rgles, qui sont trs simples : cela revient rsoudre des
petits problmes de logiques. Les oprateurs boolens sont galement trs simples comprendre et
manipuler.
Les oprateurs boolens de comparaison sont prsents dans le tableau 1.1 (page suivante).
Grce ces oprateurs, il est possible dcrire des conditions lmentaires pour raliser des tests
simples.
1.5.2 Excution conditionnelle dinstructions
Structure SI...ALORS
La structure de contrle SI...ALORS permet dexcuter des instructions en fonction de la valeur
dune condition (qui nest autre que le rsultat dun test).
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
9
Chapitre 1

Les bases de la programmation
Tableau 1.1
Nom Utilisation Rle Rsultat
= valeur1 = valeur2 galit VRAI si les deux valeurs testes
sont gales
= valeur1= valeur2 Ingalit VRAI si les deux valeurs testes
sont diffrentes
> valeur1 > valeur2 Suprieur strictement VRAI si valeur1 strictement
suprieure valeur2
< valeur1 < valeur2 Infrieur strictement VRAI si valeur1 strictement
infrieure valeur2
valeur1 valeur2 Suprieur ou gal VRAI si valeur1 suprieure ou
gale valeur2
valeur1 valeur2 Infrieur ou gal VRAI si valeur1 infrieure ou
gale valeur2
La syntaxe est la suivante :
SI (condition) ALORS
instruction(s)
FINSI
Implantation C
if (condition)
{
instruction(s);
}
Cette structure fonctionne de la manire suivante :
si la condition est vraie, alors les instructions crites entre les accolades sont excutes ;
si la condition est fausse alors, les instructions ne sont pas excutes.
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
1.5. Structure de contrle
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 vrier 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. An 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 ET Oprateur OU Oprateur NON
c1 c2 c1 ET c2 c1 c2 c1 OU c2 c1 NON c1
FAUX FAUX FAUX FAUX FAUX FAUX FAUX VRAI
FAUX VRAI FAUX FAUX VRAI VRAI
Implantation C
ET se note &&
OU se note ||
se note !

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
11
Chapitre 1

Les bases de la programmation
1.5.3 Itrations et boucles
Certains algorithmes ncessitent de rpter des instructions un certain nombre de fois avant dob-
tenir le rsultat voulu. Cette rptition est ralise en utilisant une structure de contrle de type
itratif, nomme boucle. Il existe trois types de boucles.
La boucle TANT...QUE
Sa syntaxe est la suivante :
Langage algorithmique
TANTQUE condition FAIRE
instruction 1
...
instruction n
FAIT
instructions suivantes
Implantation C
while (condition)
{
instruction 1;
...
instruction n;
}
instructions suivantes;
Lorsque lordinateur rencontre cette structure, il procde systmatiquement de la manire sui-
vante :
La condition est teste (on dit aussi value).
Si la condition est fausse, linstruction ou les instructions du bloc ne sont pas excutes et on
passe aux instructions suivantes (aprs la structure de contrle).
Si la condition est vraie, linstruction ou les instructions du bloc sont excutes, et on recom-
mence ltape 1) : test de la condition.
La boucle FAIRE...TANT QUE
Cette structure de contrle est trs proche syntaxiquement de la boucle ou rptition TANTQUE. La
seule diffrence rside dans lordre dans lequel sont faits les tests et les instructions. Cette structure
sutilise de la manire suivante :
Langage algorithmique
FAIRE
instruction 1
...
12
1.5. Structure de contrle
instruction n
TANTQUE condition
instructions suivantes
Implantation C
do
{
instruction 1;
...
instruction n;
}
while (condition);
instructions suivantes;
Lorsque lordinateur rencontre cette structure, il procde systmatiquement de la manire sui-
vante :
Excution de linstruction ou du bloc dinstruction concerne.
Test de la condition (on dit aussi valuation).
Si la condition est vraie, linstruction ou les instructions du bloc sont excutes, et on recom-
mence ltape 1 soit excution de linstruction ou des instructions du bloc.
Si la condition est fausse, le programme passe aux instructions suivantes.
La boucle POUR
Lorsque le nombre de fois o un bloc dinstructions doit tre excut est connu lavance, la boucle
POUR est prfrable aux boucles prcdentes. Lusage principal de la boucle POUR est de faire la
gestion dun compteur (de type entier) qui volue dune valeur une autre.
Langage algorithmique
POUR variable DE valeur1 A valeur2 FAIRE
instruction 1
...
instruction n
FAIT
instructions suivantes
Implantation C
for (variable = valeur1; variable <= valeur2; variable++)
{
bloc dinstructions;
}
instructions suivantes;

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
13
Chapitre 1

Les bases de la programmation
Lorsque lordinateur rencontre cette structure, il procde systmatiquement de la manire sui-
vante :
La variable, jouant le rle de compteur, est initialise la valeur1.
Lordinateur teste si la variable est infrieure ou gale la valeur2 :
si cest le cas, linstruction ou le bloc dinstruction est effectu, la variable jouant le rle de
compteur est augmente de 1, et retour ltape 2, et non ltape 1 qui initialise la variable ;
si ce nest pas le cas, linstruction ou le bloc dinstruction nest pas effectue, et lordinateur
passe aux instructions suivantes.
En ralit, la boucle POUR est quivalente la boucle TANTQUE, mais les deux sont utilisables
dans des cas distincts. Dans le cas o le nombre ditrations nest pas connu lavance, la boucle
TANTQUE sera utilise.
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 difcilement envisageable de dnir 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 Dnition
Un tableau se dnit 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 dnition dun tableau nomm tab qui peut stocker 100 valeurs
de type entier au maximum.
La taille maximale dun tableau doit tre une constante numrique.
Ce que nest pas un tableau
Un tableau est en ralit une variable comme les autres, mais son type est dune nature radicalement
diffrent des types prsents plus haut. En particulier, ce nest pas le type des lments stocks.
Ainsi :
un tableau stockant des entier nest pas un entier ;
un tableau stockant des reel nest pas un reel ;
un tableau stockant des caractere nest pas un caractere.
14
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
dnition suivante :
VAR vals : rel[15]
Cette dnition 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
tab[0] tab[2] tab[6] 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 dnition 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 dnitions 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
Chapitre 1

Les bases de la programmation
Les instructions suivantes sont alors valables :
x 1.205E-17
z[2] x // car z[2] et x sont tous deux des rels
idx 4
z[idx] x + z[idx-2] // soit z[4] x + z[2]
Taille utile
Le fait de donner la taille maximale dun tableau sous la forme dune constante est une contrainte
lourde, puisque cette valeur est xe 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 dnition 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 dnition dun tableau est associe la dnition 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 vrier quil reste au moins une case disponible :
la taille utile doit tre infrieure strictement la taille maximale du tableau. Cette vrication
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 vrier que la taille utile est strictement positive,
cest--dire quil y a au moins un lment dans le tableau.
Enn, cette variable stockant la taille utile dun tableau na pas de nom qui lui est spciquement
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 den-
tiers. 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
1.6.4 Les tableaux plusieurs dimensions
Il est possible de dnir des tableaux plusieurs dimensions en les indiquant dans des crochets
successifs lors de la dnition du tableau. Pour des propos dillustration lexemple se limitera
deux dimensions, la gnralisation N dimensions est immdiate.
Certains problmes (notamment les jeux de plateau) ont une reprsentation naturelle en deux
dimensions avec un reprage en lignes/colonnes ou abscisse/ordonne.
Exemple
Un damier se reprsente comme un plateau de 100 cases constitu de 10 lignes et 10 colonnes.
Une programmation dun jeu de dames utilisera donc de manire naturelle un tableau deux
dimensions, une pour les lignes, lautre pour les colonnes. Ltat de chacune des cases du damier
sera stock sous la forme dun entier (1 pour vide, 2 pour pion blanc, etc.). Ainsi la dnition

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
17
Chapitre 1

Les bases de la programmation
dun tableau dans ce cadre sera la suivante :
// 10 lignes, chaque ligne ayant 10 colonnes, soit 100 cases entier damier[10][10]
Cette dnition permet de simplier la reprsentation et donc la rsolution du problme !
Utilisation dindices dans les tableaux deux dimensions : chaque lment du tableau est repr
par un numro de ligne et un numro de colonne. Ainsi, si lig et col sont deux indices (donc des
entiers) valides (compris entre 0 et 9 pour lexemple du damier), damier[lig][col] est lentier
situ la ligne lig et la colonne col du tableau deux dimensions pris en exemple.
Attention tout de mme cet exemple : les notions de ligne et colonne ne sont pas connues par
lordinateur, qui ignore ce qui est fait des valeurs quil stocke.
1.7 POINTEURS
Abordons maintenant un point souvent redout tort : les pointeurs. Un peu de logique et de
rigueur sufsent 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 modis au
cours du programme, car xs au moment de la dnition 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, an 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 sufra 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 dnition de variable suivante : VAR x : rel
Cette simple dnition 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 signie 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.
Toute variable possde une et une seule adresse.
1.7.2 Dnition et contenu
Un pointeur est une variable un peu particulire, car elle ne stocke ni un entier, ni un rel, ni un
caractre, mais une adresse. Il est tentant de penser quune adresse tant un numro de cellule dans
la mmoire, elle est comparable un entier. Cependant, une adresse ne sutilise pas comme un
entier, puisquune adresse ne peut pas tre ngative, et ne sert pas faire des calculs. Une adresse
est donc en ce sens un nouveau type.
Notion de contenu
Soit p un pointeur (il sera temps de passer la syntaxe exacte de dnition 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
signication 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 dnir 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
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
19
Chapitre 1

Les bases de la programmation
est ncessaire dajouter des informations supplmentaires an 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 sufsante 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 dnition 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 dnit par le type de son contenu. Pour la cration dun pointeur nomm ptr qui
pointe sur des entiers, sa dnition scrit : le contenu de ptr est de type entier . En langage
informatique, cest cette notation qui sera traduite en dnition 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 dnition 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 ). Enn, la dnition 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 dnition 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 dnition 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 n 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.
An 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 dni : 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 = NULL)
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
21
Chapitre 1

Les bases de la programmation
// instructions dans lesquelles on peut accder *p_val
SINON
AFFICHER("attention, p_val vaut NULL, on ne peut accder *p_val")
FINSI
Dans ce cas, le programme ache 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 adresses de variables existantes
Les variables que lutilisateur dnit au sein de son propre programme ont forcment des adresses
valides. Pour rappel, ladresse dune variable est accessible au moyen de loprateur &.
Exemple
Voici donc un exemple dinitialisation de pointeur tout fait valide :
VAR *ptr_c : caractre
VAR lettre : caractre
ptr_c &lettre
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
Explication avec des valeurs numriques
Lors de la dnition de la variable lettre, une adresse lui est automatiquement attribue par
lordinateur. La valeur choisie est par exemple, 102628 (ce nombre na aucune importance, mais
aide illustrer lexplication). Ladresse de lettre (&lettre) vaut donc 102628, et lettre vaut
Y suite son initialisation par linstruction lettre Y.
Suite lexcution de linstruction ptr_c &lettre, le pointeur ptr_c reoit 102628 : la valeur
de ptr_c est donc 102628.
Que vaut le contenu de ptr_c (*ptr_c) ? Le contenu du pointeur ptr_c est la valeur stocke en
mmoire ladresse 102628 (car ptr_c vaut 102628). Il se trouve que cette adresse est celle de
la variable lettre. Ainsi * ptr_c vaut Y. Il nest donc pas tonnant que (*ptr_c)+1 soit gal
Z.
Allocation dynamique de mmoire et commande RESERVER()
Le dernier type (et le plus intressant) dinitialisation de pointeur consiste effectuer cette initiali-
sation laide dune nouvelle adresse autorise. Cest rendu possible par lemploi dune nouvelle
commande dont le but est dobtenir une zone de stockage mmoire dynamiquement, cest--dire
lors de lexcution du programme.
Cette commande se nomme RESERVER() et sa syntaxe dutilisation est la suivante :
RESERVER(pointeur)
22
1.8. Les sous-programmes ou fonctions
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 dni de la manire suivante :
VAR *ptr : rel
A priori, ptr nest pas initialis et il est donc hasardeux daccder son contenu. An dinitialiser
ptr correctement, il est possible de reserver de lespace mmoire pour son contenu.
RESERVER(ptr) aura cet eet.
Une rservation de mmoire se nomme galement une allocation dynamique de mmoire.
Lorsque la place en mmoire nest plus utile, il suft de la librer en utilisant la commande suivante :
LIBERER(pointeur)
1.8 LES SOUS-PROGRAMMES OU FONCTIONS
Le rle dune fonction est de regrouper des instructions ou traitements qui doivent tre faits de
manire rptitive au sein dun programme. Par exemple, dans un programme traitant des tableaux,
on voudrait afcher plusieurs fois des tableaux dont les variables stockent des valeurs diffrentes.
Cependant, mme si les valeurs sont diffrentes, lafchage dun tableau se rsume toujours
un parcours de toutes les variables utilises avec une boucle POUR (de lindice 0 jusqu lindice
taille_utile-1), avec un afchage de chaque valeur grce linstruction AFFICHER().
Il serait utile de regrouper ces instructions dafchage, pour nen avoir quun seul exemplaire,
et de pouvoir sen servir lorsquon en a besoin, sans avoir saisir les lignes dans le programme.
Cest cela que sert une fonction : elle regroupe des instructions auxquelles on peut faire appel,
cest--dire utiliser en cas de besoin. Il sagit en ralit dun petit programme qui sera utilis par le
programme : on parle aussi de sous-programme pour une fonction. Ce sous-programme fonctionne
en bote noire vis--vis des autres sous-programmes, cest--dire quils nen connaissent que
les entres (valeurs qui sont fournies la fonction et sur lesquelles son traitement va porter) et
la sortie (valeur fournie par la fonction, qui peut tre rcupre par les autres fonctions ou par le
programme).
Une analogie avec une fonction mathmatique permet dillustrer clairement les notions dentres
et de sorties. Soit la fonction suivante dnie avec le formalisme des mathmatiques :
F : R R R
x, y x
2
+ xy + y
2
Les entres sont les donnes que lon fournit la fonction pour quelle puisse les traiter et fournir
un rsultat : il sagit des valeurs x et y. La sortie est le rsultat que donne la fonction, il sagit de la
valeur calcule x
2
+ xy + y
2
.
Types des entres et sorties Daprs la dnition (mathmatique) de la fonction, le type
informatique des entres et de la sortie peut tre dtermin : ce sont des rels dans cet exemple.
23
Chapitre 1

Les bases de la programmation
La fonction propose en exemple a comme entres deux valeurs de type rel, eectue un calcul,
et a comme sortie une valeur de type rel.
1.8.1 Dnition dune fonction
Comme de nombreuses entits en informatique, une fonction doit tre dnie avant dtre utilise,
cest--dire que lon doit indiquer quelles sont les instructions qui la composent : il sagit de la
dnition de la fonction, o lon associe les instructions lidentication de la fonction.
On doit donc trouver une dnition de la fonction, qui comporte :
Une identication ou en-tte de la fonction, suivie des instructions de la fonction, ou corps de
la fonction.
La fonction doit tre dnie et comporter : un en-tte, pour lidentier et un corps contenant ses
instructions, pour la dnir.
Comment crire une dnition de fonction
En-tte
Len-tte dune fonction contient les informations ncessaires pour identier 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 dnitions 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 dnition 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 dnitions de
variables qui peuvent tre utiles pour faire des calculs intermdiaires lorsque lon utilise la fonction.
Les dnitions des variables se trouvent avant le mot DEBUT.
Les variables locales
Ces variables, dnies 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 dnit (en quelque
24
1.8. Les sous-programmes ou fonctions
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 dnition de variable.
Enn, les dnitions des variables locales la fonction suivent exactement les mmes rgles que
celles que nous avons dj rencontres pour les dnitions 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
Eet : transmet la valeur de lexpression et met n 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.
1.8.2 Appel des fonctions
Lappel dune fonction correspond une demande de son utilisation, ceci est fait dans une autre
fonction, dont par exemple le programme principal. An dappeler une fonction, on doit prciser :
son nom, ainsi que les valeurs que lon fournit pour les entres. On ne prcise pas la valeur de la
sortie, car cest la fonction appele qui est en charge de la fournir !
La fonction (ou programme principal) qui appelle (ou utilise) une fonction est dite : fonction
appelante; la fonction qui est utilise est dite fonction appele.
Exemple
Soit une fonction nomme fonc, et possdant une liste dentres : type1 entree1, type2
entree2,... ,typen entreen. Son en-tte est donc :
FONCTION fonc(entree1 : type1, entree2 : type2,..., entreen : typen):
type_sortie)

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
25
Chapitre 1

Les bases de la programmation
Pour appeler cette fonction, la syntaxe est la suivante :
fonc(expr1, expr2,...,exprn)
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.
Gestion des entres : les arguments
Lors de lappel de la fonction, une expression donnant une valeur une entre de la fonction est
appele argument de la fonction.
Retenez bien quun argument nest pas forcment une variable, mais peut tre une expression.
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 : aectation
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 commu-
nication 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
1.8. Les sous-programmes ou fonctions
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 modies 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 modie 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 dni 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 dnie 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.
Appel dune fonction avec un argument de type tableau
Lorsquun tableau est utilis comme un argument, il est inutile dutiliser la notation avec les
crochets. En effet, ces crochets sont utiliss pour dnir le tableau, ou pour accder un lment
27
Chapitre 1

Les bases de la programmation
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 dnition 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.
1.8.4 Les fonctions et les pointeurs
Le passage de tableau en paramtre est en fait une trs bonne illustration des effets de la transmission
dune adresse une fonction : le mme phnomne entre en jeu lorsque ce sont des pointeurs qui
sont utiliss pour ce passage de paramtres, puisquun tableau est un pointeur.
Nous avons ainsi remarqu que le passage dune adresse, dans le cas dun tableau, permet
dobtenir un accs une variable du programme principal partir dune fonction recevant ladresse
de cette variable. Nous allons donc utiliser explicitement cette transmission dadresse de variable
pour avoir un accs son contenu, et ce grce la notation * dj traite dans la section
concernant les pointeurs.
Paramtre de type adresse de
La syntaxe utilise pour un paramtre de fonction lors de la dnition de celle-ci, lorsque le
paramtre est un pointeur est la suivante :
*nom_du_paramtre : type_point
Cela revient donc utiliser la syntaxe de dnition 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 signie, 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 poin-
teur.
28
1.9. Cration de types par le programmeur : les types composs ou structures
Les donnes modies
Les paramtres dune fonction sont une copie des arguments. Ainsi, la modication de la valeur
dun paramtre naura aucune inuence sur la valeur de largument. Une fonction ne peut pas
modier la valeur des arguments qui lui sont transmis. Cependant, il est parfois intressant quune
fonction puisse modier une variable de la fonction qui lappelle. Pour ce faire, la technique consiste
transmettre la fonction ladresse de la variable modier. Ainsi, la fonction rcupre ladresse
de la variable et accde sa valeur grce loprateur * (contenu de). La notation donnes
modies rencontre dans la suite de cet ouvrage indique que cest une adresse qui est transmise.
Retour dune adresse
Une fonction peut retourner une adresse, car nimporte quel type peut tre fourni en sortie de
fonction. Les prcautions prendre lorsquune adresse est retourne par une fonction sont les
suivantes, ce sont des rgles de programmation respecter de faon trs stricte :
une fonction ne doit jamais retourner ladresse dun de ses paramtres ;
une fonction ne doit jamais retourner ladresse dune de ses variables locales ;
une fonction ne doit jamais retourner un tableau statique local (dni dans la fonction) ;
une fonction peut retourner une allocation obtenue par la fonction RESERVER().
1.9 CRATION DE TYPES PAR LE PROGRAMMEUR : LES TYPES
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 dnir 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 identi 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.

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
29
Chapitre 1

Les bases de la programmation
Par convention de nommage, un nom de type compos doit systmatiquement commencer par
t_.
Pour dnir un type compos, aussi appel structure, on utilise simplement la syntaxe sui-
vante :
STRUCTURE nom_du_type
nom_champ_1 : type_champ_1
...
nom_champ_n : type_champ_n
Le mot STRUCTURE permet de dnir un nouveau type, et non pas une variable.
Dnition du type complexe pour reprsenter un nombre complexe :
STRUCTURE t_complexe
// les dfinitions de champ sont comme
// les dfinitions de variables, il est possible
// de les regrouper.
re : reel
im : reel
Une fois ce type compos dni, 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
dnir des variables dont le type est un type compos ou structure, en utilisant la syntaxe classique
de dnition dune variable :
VAR nom_de_variable : type
VAR var_comp : t_complexe est une dnition de variable tout fait correcte, et dont la
signication est : la variable nomme var_comp est de type t_complexe.
1.9.1 Accs aux champs
Loprateur utilis pour accder un champ partir du nom dune variable est loprateur .,
dont la syntaxe dutilisation est la suivante :
nom_de_variable.nom_du_champ
Cela sinterprte comme : le champ nom_du_champ de la variable nom_de_variable. Cette
criture est une expression dont le type est celui du champ rfrenc.
Exemple
Soit la dnition de variable suivante : VAR z : t_complexe
An dinitialiser cette variable z, il est ncessaire daccder individuellement ses champs, car
lordinateur est incapable dinitialiser directement une variable de type t_complexe.
30
1.9. Cration de types par le programmeur : les types composs ou structures
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 dni laide de la
structure. Le . doit systmatiquement tre prcd dun nom de variable.
1.9.2 Oprateur daffectation
Loprateur daffectation est le seul oprateur que lordinateur peut appliquer aux structures,
car il sagit dans ce cas de recopier les champs dune variable de type compos dans les mmes
champs dune autre variable du mme type. Cest dailleurs tout ce que fait cette opration.
Une instruction daffectation entre deux variables dun type compos copie donc les champs
dune variable dans une autre.
1.9.3 Structures contenant des tableaux et des pointeurs
Tableaux statiques
Puisquun champ peut tre de nimporte quel type, il peut sagir entre autres dun tableau, par
exemple statique. Il est ncessaire, dans ce cas, de stocker sa taille utile dans un champ de la
structure.
Lorsque plusieurs variables dun tel type sont dnies, un tableau statique diffrent (dadresse
diffrente) est cr pour chacune de ces variables. En cas daffectation, ce sont les contenus des
cases du tableau qui sont recopis.
1.9.4 Structures dnies laide de structures
Un champ dune structure peut tre de nimporte quel type, donc il peut tre dun type compos,
cest--dire une structure. Cela aide bien hirarchiser les diffrents niveaux pour viter que les
structures ne comportent trop de champs.
Il est possible dans ce cas davoir un accs un champ en utilisant plusieurs fois de suite la
notation ..
Pour quun champ dune structure s1 soit lui-mme une structure s2, il faut cependant que cette
structure s2 soit dnie avant s1.
Exemple
STRUCTURE t_date
jj, mm, aa : entier // pour jour, mois, anne
STRUCTURE t_evenement
ladate : t_date // rutilisation du type t_date dfini prcdemment
*description : caractre // ou encore description : caractre[50]
le type t_evenement est dni laide du type t_date.

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
31
Chapitre 1

Les bases de la programmation
1.9.5 Pointeurs vers les structures
Est-il possible dutiliser des pointeurs pour stocker ladresse dune variable de type compos ? La
rponse cette question a des consquences assez importantes, dans la mesure o, comme pour les
exemples dj traits dans les chapitres prcdents, des applications (programmes professionnels
utiliss dans lentreprise) auront utiliser beaucoup de structures.
Or, la possibilit de stocker en mmoire un certain nombre de structures implique que lon puisse
utiliser des tableaux, et le plus souvent, des tableaux dynamiques. En ralit, les tableaux statiques
ne sont quasiment pas utiliss, seule la notation [ ] est vraiment confortable dun point de vue de
la programmation. Un petit retour en arrire vers les pointeurs est ici ncessaire.
An de pouvoir manipuler un contenu, lordinateur a besoin de deux informations :
une adresse lui permettant de dterminer lendroit de la mmoire consulter ;
un type.
Cette information de type, indispensable la dnition dun pointeur, est exploite pour savoir
combien doctets doit manipuler lordinateur lors de laccs la mmoire.
Les champs dune variable de type compos sont stocks les uns la suite des autres de manire
conscutive dans la mmoire, cette variable forme un bloc en mmoire. Elle a donc une adresse
(comme pour les tableaux, celle du dbut du bloc), ou plus prcisment, une seule adresse permet
de la reprer.
En ce qui concerne la taille de cette variable, le raisonnement est encore plus simple : la taille
dune variable de type structure est la somme de la taille de tous ses champs.
Ayant une taille xe (et calculable par la mthode expose ci-dessus) et une adresse unique,
une variable de type structure peut tre manipule comme un contenu et donc il est possible de
dnir un pointeur vers une variable de type structure
Aspects syntaxiques
Les notions dadresse, de valeur, de contenu restent dans ce cas tout fait valides, et les notations
usuelles sappliquent. Soit t_com un type compos (une structure). Dnir un pointeur ptr vers un
contenu de type t_com scrit donc naturellement :
VAR *ptr : t_com
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
1.9. Cration de types par le programmeur : les types composs ou structures
Exemple
Soit p_tot un pointeur dni 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 dnition 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 (che)
Dernier oprateur abord pour ce qui concerne les structures, loprateur nest pas propre-
ment 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
1.9.6 Types pointeurs et raccourcis de notation
Il est parfois pratique de disposer dun nom de type raccourci pour dsigner un pointeur vers une
structure. Ce nom raccourci de type se dnit avec la syntaxe suivante :
TYPE *type_pointeur : type
Exemple :
TYPE *ptr_comp : t_complexe
Le raccourci de type prcdent signie que le type ptr_comp dsigne un pointeur vers un
t_complexe. Ainsi, les deux dnitions 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.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
33
Chapitre 1

Les bases de la programmation
1.9.7 Structures et fonctions
Passage de paramtres de type compos
Les structures se comportent comme les autres types en ce qui concerne les fonctions : il est possible
de spcier des entres dont les types sont des structures, auquel cas le mcanisme de passage de
paramtre reste valable. La diffrence rside dans le fait que plusieurs valeurs numriques doivent
tre transmises entre largument et le paramtre. Pour raliser cette transmission, cest en ralit
loprateur daffectation qui sera employ pour recopier la valeur de largument (expression) dans
le paramtre correspondant.
Paramtres : adresses de structures
Les types composs ne diffrent gure des types de base, mis part limpossibilit demployer
les oprateurs arithmtiques et logiques avec les types composs. Il en est de mme avec leurs
adresses : une fonction pouvant accepter une adresse en entre (paramtres de type pointeur), elle
peut donc a fortiori accepter une adresse dont le contenu est dun type quelconque, et notamment
un type compos. Il suft dans ce cas dutiliser la notation lintrieur de la fonction pour
accder aux champs de la structure dont ladresse est fournie la fonction.
Retour de valeurs de type compos
Une fonction peut tout fait retourner une valeur de type compos, partir du moment o ce type
est dni : il peut alors tre utilis en type de sortie de fonction sans aucun problme, ce qui est
dailleurs souvent le cas en informatique.
Retour dadresses de structures
En synthse des deux derniers paragraphes, une fonction peut galement retourner ladresse dune
valeur de type quelconque, notamment un type compos.
34
2
STRUCTURES SQUENTIELLES
SIMPLES
RAPPELS DE COURS
2.1 LISTES LINAIRES
Exemple
Imaginons la gestion dun tableau contenant les rfrences des livres dune bibliothque. Ce
tableau est rang dans lordre alphabtique. Lorsquun nouveau livre est achet, son insertion
dans le tableau en respectant lordre requiert de dplacer toutes les rfrences qui suivent la
position dinsertion, pour dgager de la place. Le cot dune telle opration est lev et peut
devenir prohibitif dans certains cas. Pour viter ce type de problme, il faudrait que le passage
dune case dun tableau la suivante ne se fasse plus partir dun indice absolu quon incrmente,
mais en notant localement dans une case du tableau lindice de la case suivante.
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 Dnition
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
Chapitre 2

Structures squentielles simples
Tableau 2.1
A d b \0
0 1 2 3 4 5
Tableau 2.2
3 5 2
0 1 2 3 4 5
chaque algorithme utilisant la reprsentation en question. Dans la case 1 du deuxime tableau on
trouve 3, qui est lindice du deuxime lment du premier tableau, etc. Finalement, dans la case 2
du deuxime tableau on trouve 5, et dans la case 5 du premier tableau on a le marqueur de n.
Si on insre c dans le premier tableau, on peut linsrer dans la premire case libre (cest la
case 0), et il ny a que deux changements faire au niveau du deuxime tableau. On met 0 dans la
case 3, et on met 2 dans la case 0. Les tableaux 2.3 et 2.4 prsentent les rsultats.
Tableau 2.3
c a D b \0
0 1 2 3 4 5
Tableau 2.4
2 3 5 0
0 1 2 3 4 5
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 dnir des enregistrements (structures) dont un des
champs est de type pointeur vers une structure chane du mme type.
Exemple de dnition dune structure chane
Pseudo code formel
STRUCTURE nud
nom : caractre[20]
prenom : caractre[20]
*suiv : nud
TYPE *ptr_nud : nud
Implantation C
typedef struct nud
{
char nom[20];
36
2.1. Listes linaires
char prenom[20];
struct nud *suiv;
} nud;
typedef nud* ptr_nud;
La liste vide est reprsente par le pointeur NULL.
Figure 2.1 Liste (simplement) chane
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 suppres-
sion 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 k
ime
lment nest plus direct : on doit parcourir k1 pointeurs partir
de la tte pour trouver le k
ime
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 modies 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
Chapitre 2

Structures squentielles simples
Dnition dune structure de liste simplement chane
Pseudo code formel
STRUCTURE place
info : T
*suiv : place
TYPE *list : place
Implantation C
typedef struct place
{
<type> content;
struct place *succ;
} place;
typedef place *list;
Tester la prsence dun lment dans une liste chane
Spcication de lalgorithme recherche (a)
FONCTION recherche(l: liste<lment>, x: lment): boolen
VAR trouve: boolen ; p: place
DEBUT
trouve faux
SI estvide(l) ALORS
p tte(l)
TANTQUE dernier(p) trouve = faux FAIRE
SI contenu(p) = x ALORS
trouve vrai
SINON
p succ(p) // itration vers la cellule suivante
FINSI
FAIT
SI contenu(p) = x ET trouve = faux ALORS
trouve vrai
FINSI
FINSI
RETOURNER trouve
FIN
Ralisation en C
Donnes : list l : la liste de recherche, int k : llment recherch
Rsultat : type boolen (int en C)
int containsElement(list l, int k)
{
int found = 0;
if (l == NULL) return found;
while ((l->succ != NULL) & (! found))
{
38
2.1. Listes linaires
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 = k parce
que si l->succ pointe sur NULL, l->succ->content nexiste pas.
Une alternative sans variable boolenne
Spcication de lalgorithme recherche (b)
FONCTION recherche(l: liste<lment>, x: lment): boolen
VAR p : place
DEBUT
SI estvide(l) ALORS
p tte(l)
TANTQUE dernier(p) FAIRE
SI contenu(p) = x ALORS
RETOURNER vrai
SINON
p succ(p) // itration vers la cellule suivante
FINSI
FAIT
SI contenu(p) = x ALORS
RETOURNER vrai
FINSI
FINSI
RETOURNER faux
FIN
Ralisation en C (et par extension, fonction de dcompte doccurrences)
Donnes : list l : la liste de recherche, int k : llment recherch
Rsultat : type boolen (int en C)
/**
* Notez lutilisation de petites fonctions utilitaires :
* isEmptyList(l) l == NULL
* getHeadContent(l) l->content
* hasMoreElements(l) ! isEmptyList(nextElement(l))
* nextElement(l) l->succ
*/
int countElementOccurrences(list l, int k)
{
int count = 0;
if (isEmptyList(l)) return count;
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
39
Chapitre 2

Structures squentielles simples
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 n
Spcication de lalgorithme cration de liste en ligne de commande
FONCTION crer_par_saisie(): liste
VAR l: liste ; e: lment
DEBUT
l listevide()
SAISIR(e)
SI e = fin ALORS
cons(e, l)
FINSI
FIN
Alternative
DEBUT
l listevide()
FAIRE
SAISIR e
SI e = fin ALORS
cons(e, l)
FINSI
TANTQUE e = fin
FIN
Ralisation en C
Donne modifie : list *pl
/**
* Construction dune liste par saisie depuis lentre standard
* La saisie sachve quand lutilisateur crit fin
* La saisie dun terme qui nest pas un entier ni fin
* est interprte comme la saisie de 0
*/
int newListFromStandardInput(list *pl)
{
*pl = NULL;
int size = 1;
char input[10];
printf("Saisissez des entiers puis tapez fin :\n");
printf("%d> ", size++);
40
2.1. Listes linaires
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);
}
Supprimer un lment de la liste
Spcication de lalgorithme supprimer un lment dune liste
FONCTION supprimer(l: liste<lment>, x: lment): boolen
VAR p, prec, cour: place
VAR ok: boolen
DEBUT
ok faux
p tte(l)
SI contenu(p) = x ALORS
prec p
p succ(p)
ok vrai
{ suppression du premier lment }
LIBERER(prec)
SINON
prec p
cour succ(p)
TANTQUE ok = faux cour = listevide() FAIRE
{ cour pointe sur celui supprimer, }
{ prec pointe sur le prcdent }
SI contenu(cour) = x ALORS
ok vrai
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
41
Chapitre 2

Structures squentielles simples
{ cration du lien pour supprimer un lment }
{ situ entre les deux qui sont lis }
succ(prec) succ(cour)
LIBERER(cour)
SINON
{ mmorisation de la place dlment prcdent }
{ llment tester }
prec cour
cour succ(cour)
FINSI
FAIT
FINSI
RETOURNER ok
FIN
Ralisation en C (avec extensions pour les listes circulaires et doublement chanes)
Donne : int x : llment supprimer
Donne modifie : list* pl : la liste do supprimer llment
Rsultat : type boolen (int en C)
int removeFirst(list* pl, int x)
{
list prec, cour;
int ok = 0;
if (*pl == NULL) return ok;
int circular = isCircular(*pl); // vous de jouer
int perimeter = getCLength(*pl); // Idem
if ((*pl)->content == x) // si le x est le premier lment..
{
prec = *pl; // la tte est note prec
*pl = (*pl)->succ; // on dplace le dbut de liste au suivant
if (*pl != NULL) (*pl)->prev = NULL; // pour la doublement chane
// en + pour la circulaire qui pendant un instant ne lest plus :
if (circular)
{
if ((*pl)->succ == prec) (*pl)->succ = *pl;
else
{
list last = *pl;
while (last->succ != prec) last = last->succ;
last->succ = *pl;
}
}
ok = 1;
free(prec); // on libre la mmoire de lex tte
}
else
{
prec = *pl; // la tte est note prec
42
2.1. Listes linaires
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;
}
2.1.4 Variantes dimplantation des listes
Il existe dailleurs de nombreuses variantes de la reprsentation des listes laide de pointeurs.
Cellule racine
Il est parfois commode de dnir 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 (linaire simplement) chane avec cellule racine
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.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
43
Chapitre 2

Structures squentielles simples
Figure 2.3 Liste (simplement) chane circulaire
Liste doublement chane (ou chane bidirectionnelle)
Dans la reprsentation classique, le parcours des listes est orient dans un seul sens : du premier
lment vers le dernier lment. Cependant de nombreuses applications ncessitent de parcourir les
listes la fois vers lavant et vers larrire, et dans ce cas on peut faciliter le traitement en rajoutant
des pointeurs arrire , ce qui augmente videmment la place mmoire utilise. On obtient alors
une liste doublement chane : chaque place comporte un pointeur vers la place suivante et un
pointeur vers la place prcdente.
Figure 2.4 Liste (linaire) doublement chane
Dnition dune structure de liste doublement chane
Pseudo code formel
STRUCTURE node
content : T
*succ, *prev : node
TYPE *list : node
Implantation C
typedef struct node
{
<type> content;
struct node *succ, *prev;
} node;
typedef node *list;
44
noncs des exercices et des problmes
Afcher le contenu dune liste lenvers
Spcication abstraite
FONCTION afficher(l: liste)
DEBUT
l tte(l)
TANTQUE dernier(l) FAIRE
l succ(l)
FAIT
TANTQUE premier(l) FAIRE
AFFICHER contenu(l)
l prev(l)
FAIT
FIN
Ralisation en C
Donne : list l : la liste afficher
void reversePrintList(list l)
{
int k = 1;
while (l->succ != NULL)
{
l = l->succ;
k++;
}
while (l != NULL)
{
printf("\tList[%d] = %d\n", k--, l->content);
l = l->prev;
}
}
NONCS DES EXERCICES ET DES PROBLMES
EXERCICES
Exercice 2.1 Rechercher llment maximal dune liste
**
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 L
1
et L
2
dont les lments sont des entiers. crire un algorithme
qui rattache la liste L
2
la suite de la liste L
1
. Tous les cas particuliers doivent tre pris en compte.

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
45
Chapitre 2

Structures squentielles simples
Exercice 2.3 Extraire deux listes partir dune liste
***
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
**
Soit L une liste chane. crire trois algorithmes tels que :
dans le premier on supprime toutes les occurrences dun lment donn x ;
dans le deuxime, on ne laisse que les k premires occurrences de cet lment et on supprime les
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.
Exercice 2.6 Inverser une liste
**
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 k
ime
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
Exercice 2.11 Supprimer un sous-ensemble dune liste
*
Concevoir un algorithme qui supprime :
A un maillon sur deux, en commenant par la tte de liste,
B les maillons contenant des lments impairs,
C les maillons contenant des lments pairs,
D les maillons contenant des lments suprieurs un seuil donn,
E les maillons contenant des lments infrieurs un seuil donn,
dune liste simplement chane (dentiers).
PROBLME
Problme 2.1 Saisir, enregistrer puis valuer un polynme
***
Enregistrer les coefcients 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 coefcients et des exposants, qui sont saisis au clavier ;
et le second an dvaluer le polynme pour la valeur x, galement saisie au clavier.
Corrigs des exercices et des problmes
EN PRAMBULE
Pour la ralisation en C de tous les algorithmes spcis ci-dessous, on dnit la structure de liste
chane suivante dont on prcisera au cas pas cas, le type <element>. Les structures utilises pour
les spcications 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)
Les quelques implantations C de mthodes lmentaires de manipulation de la liste chane
conformes au contrat du type abstrait, pourront savrer utiles dans lcriture de vos programmes
dimplantation C de vos algorithmes, notamment en purant votre code dune trop grande quantit
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
47
Chapitre 2

Structures squentielles simples
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));}
pour les solutions algorithmiques, les fonctions sont les suivantes :
FONCTION content(l : list)
DEBUT
RETOURNER lcontent
FIN
FONCTION isempty(l : list)
VAR vide : boolen
DEBUT
SI(l=NULL)ALORS
vide vrai
SINON
vide faux
FINSI
RETOURNER vide
FIN
FONCTION succ(l : list) : list
DEBUT
RETOURNER lsucc
FIN
FONCTION islast(l : list)
DEBUT
RETOURNER isempty(succ(l))
FIN
48
Corrigs des exercices
Enn, le constructeur suivant pourra vous aider simplier vos critures pour lallocation dun
nouveau nud de liste :
list newSingletonList(int content)
{
list l = malloc(sizeof(listNode)); // allocation mmoire
l->content = content; // affectation du contenu
l->succ = NULL;
return l;
}
Et son quivalent en pseudo langage :
FONCTION newSingletonList(content : entier) : list
VAR nouveau : list
DEBUT
RESERVER(nouveau)
nouveaucontent content
nouveausucc NULL
RETOURNER nouveau
FIN
CORRIGS DES EXERCICES
Exercice 2.1 Rechercher llment maximal dune liste
Spcication de lalgorithme itratif
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> = ): 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

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
49
Chapitre 2

Structures squentielles simples
Ralisation en C de lalgorithme itratif
Ici, le champ content est de type unsigned.
int maxOf(list<unsigned> l)
{
if (l == NULL) return ERR;
int max = l->content; // valeur en premire place
while (l->succ != NULL)
{
l = l->succ; // itration de la liste
if (l->content > max) max = l->contenu;
}
return max;
}
Et rcrite avec les mthodes utilitaires donnes en introduction :
int maxOf(list<unsigned> l)
{
if (isempty(l)) return ERR;
int max = content(l); // valeur en premire place
while (! islast(l))
{
l = succ(l); // itration de la liste
if (content(l) > max) max = content(l);
}
return max;
}
Spcication de lalgorithme rcursif
Entre : liste de positifs (non vide)
Sortie : un positif
FONCTION maxOf(l: liste<positif> = ): positif
VAR max, contenu: positif
DEBUT
RETOURNER maxOfRec(tte(l))
FIN
FONCTION maxOfRec(p: place): positif
VAR max, contenu: positif
DEBUT
contenu contenu(p)
SI dernier(p) ALORS
RETOURNER contenu
50
Corrigs des exercices
FINSI
max maxOfRec(succ(p))
SI contenu > max ALORS
RETOURNER contenu
FINSI
RETOURNER max
FIN
Ralisation en C de lalgorithme rcursif
int maxOfRec(list<unsigned> l)
{
if (l == NULL) return ERR;
int content = l->content;
if (l->succ == NULL) return content;
int max = maxOfRec(l->succ);
if (content > max) return content;
return max;
}
Et rcrite avec les mthodes utilitaires donnes en introduction :
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;
}
Exercice 2.2 Concatner deux listes
tude
La contrainte dentiers positifs du premier exercice disparat, mais pas le cas des listes vides.
Nous avons quatre cas considrer :
l
1
et l
2
vides,
l
1
vide mais pas l
2
,
l
2
vide mais pas l
1
,
ni l
1
, ni l
2
vides.
Si l
2
est vide, on laisse l
1
inchange. Si l
1
est vide mais pas l
2
, on affecte directement l
2
l
1
. Si ni
l
1
ni l
2
ne sont vides, on parcourt l
1
jusqu sa dernire cellule et on y rattache l
2
.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
51
Chapitre 2

Structures squentielles simples
Figure 2.5 Concatnation standard
Enn, si on va un peu trop vite dans ltude, on peut oublier le cas l
1
= l
2
(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
Ce dernier cas dutilisation de la fonction de concatnation revient, si on lautorise, transformer
la liste chane linaire en liste chane circulaire.
Autorisons-le, en notant que nous avons alors un constructeur de liste circulaire par fermeture dune
liste chane linaire (nous utiliserons cette facilit dans le problme 3.1 (problme de Joseph)).
Pour la concatnation, une approche itrative simpose delle-mme, un procd rcursif nayant
pas grand intrt pour une simple jonction.
Nous travaillons par rfrence, cest--dire quil ny a pas de copie des deux listes (on rattache
les deux listes).
Spcication de lalgorithme
Entres modies : deux listes dentiers
FONCTION concat(l
1
, l
2
: liste<entier>)
VAR p
1
, p
2
: place
DEBUT
SI estvide(l
1
) ALORS
SI estvide(l
2
) ALORS
l
1
l
2
SINON
52
Corrigs des exercices
p
1
tte(l
1
)
TANTQUE dernier(p
1
) FAIRE
p
1
succ(p
1
)
FAIT
succ(p
1
) tte(l
2
)
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
}
Et rcrite avec les mthodes utilitaires donnes en introduction :
void concat(list *pl1, list *pl2)
{
if (isempty(*pl2)) return; // ras, l1 soit nulle ou non
if (isempty(*pl1)) // affectation directe de l2 l1
{
*pl1 = *pl2;
return;
}
// sinon :
list l = *pl1;
while ((! islast(l)) l = succ(l); // itration l1
l->succ = *pl2; // rattachement de l2
}

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
53
Chapitre 2

Structures squentielles simples
Exercice 2.3 Extraire deux listes partir dune liste
tude
Plusieurs approches sont possibles selon que :
On travaille partir de la liste de dpart en la prservant (copie des lments), ou bien en recyclant
ses cellules (rutilisation des lments).
On ralise une seule fonction complexe qui produit simultanment les deux listes, ou bien deux
fonctions simples spcialises appeles successivement.
On labore une structure complexe nouvelle pour combiner les deux listes produites dans lunique
sortie de la fonction, ou bien on passe deux pointeurs respectivement sur les deux listes (pointeurs
sur pointeurs) initialement non initialiss (paramtres inout).
Figure 2.7 Dconstruction dune liste avec reconstruction sous forme de deux listes
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.
Spcication de lalgorithme
Entres modies :
La liste dentier initiale
Les deux listes dentiers produites
FONCTION separer(l
0
, l
1
, l
2
: liste<entier>)
VAR p
0
, p
1
, p
2
: place
DEBUT
SI estvide(l
0
) ALORS
p
0
tte(l
0
)
SI contenu(p
0
) 0 ALORS
p
1
tte(l
1
) tte(l
0
)
SINON
p
2
tte(l
2
) tte(l
0
)
FINSI
TANTQUE dernier(p
0
) FAIRE
54
Corrigs des exercices
p
0
succ(p
0
)
SI contenu(p
0
) 0 ALORS
SI estvide(l
1
) ALORS
p
1
tte(l
1
) p
0
SINON
p
1
succ(p
1
) p
0
FINSI
SINON
SI estvide(l
2
) ALORS
p
2
tte(l
2
) p
0
SINON
p
2
succ(p
2
) p
0
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

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
55
Chapitre 2

Structures squentielles simples
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.)
}
Exercice 2.4 Permuter deux places dune liste
tude
Il convient de prvoir un code derreur pour les cas suivants :
Liste vide mais t ou v non null.
Liste non vide, mais t ou v pointant nul ou pointant un lment nappartenant pas la liste.
La fonction retournera 1 en cas derreur, 0 sinon.
Pour le reste, lide est didentier les deux lments, et dinterchanger (en utilisant une variable
temporaire) les pointeurs de leurs prdcesseurs respectifs et de leurs successeurs respectifs.
Figure 2.8 Permutation : le cas gnral...
Les cas suivants feront lobjet dun traitement spcial :
Les pointeurs t et v sont gaux : cest lopration identit, il ny a rien faire sinon ne rien faire.
Lun de deux pointeurs ou les deux sont respectivement tte ou queue (dbut ou n de liste).
Les deux pointeurs identient des places contiges (cf. gure 2.9 ce cas est dangereux si trait
comme prcdemment).
Figure 2.9 ... avec son exception dangereuse en labsence de traitement spcique
56
Corrigs des exercices
Spcication de lalgorithme
FONCTION swap(modif pl : list, modif t, v : place) : entier
VAR resultat : entier ; term_l, term_t, term_v, l : list
VAR prec_t, succ_t, prec_v, succ_v : list
DEBUT
SI (pl = NULL) ALORS
SI (t = NULL) ET (v = NULL) ALORS
RETOURNER 0
SINON
RETOURNER -1
FINSI
SINON
SI (t = NULL) OU (v = NULL) ALORS
RETOURNER -1
FINSI
term_l dernierElt(*pl)
term_t dernierElt(t)
term_v dernierElt(v)
SI ( ((term_t = term_l) ET (term_v = term_l))) ALORS
RETOURNER -1
FINSI
FINSI
SI (t = v) ALORS
RETOURNER 0
FINSI
l = pl;
trouverPrecEtSucc(l, t, &prec_t, &succ_t)
trouverPrecEtSucc(l, v, &prec_v, &succ_v)
SI ((prec_v = t) OU (prec_t = v)) ALORS
SI prec_v = t ALORS
SI prec_t = NULL ALORS
*pl v
SINON
prec_tsucc v
FINSI
vsucc t
tsucc succ_v
SINON
SI prec_v = NULL ALORS
*pl t
SINON
prec_vsucc t
FINSI

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
57
Chapitre 2

Structures squentielles simples
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
// Permutation de deux lments
int swap(list *pl, place *t, place *v)
{
if (*pl == NULL)
{
if ((t == NULL) && (v == NULL)) return 0; // tout va bien !!
else return -1;
}
else
{
if ((t == NULL) || (v == NULL)) return -1; // non consistant !!
// Maintenant test dappartenance
// on utilise un truc : mme terminal !
list term_l = lastElement(*pl);
list term_t = lastElement(t);
list term_v = lastElement(v);
if (!((term_t == term_l) && (term_v == term_l))) return -1;
}
// prconditions vrifies : on peut commencer
if (t == v) return 0; // identit -- tout va bien !!
58
Corrigs des exercices
// note : le cas du singleton est implicitement dj trait :
// on travaille maintenant sur une liste l multiple
// sinon, cas gnral :
list l = *pl;
list prec_t, succ_t;
getPrecAndSucc(l, t, &prec_t, &succ_t);
l = *pl;
list prec_v, succ_v;
getPrecAndSucc(l, v, &prec_v, &succ_v);
// cas de contigit ->[v]->[t]-> ou ->[t]->[v]->
if ((prec_v == t) || (prec_t == v))
{
// cas : ->[t]->[v]-> => ->[v]->[t]->
if (prec_v == t)
{
// cas .->[t] => .->[v] vs autres cas
if (prec_t == NULL)
{
printf("case : .->[t]->[v]-> => .->[v]->[t]->\n");
*pl = v;
}
else
{
printf("case : [..]->[t]->[v]-> => [..]->[v]->[t]->\n");
prec_t->succ = v;
}
v->succ = t;
t->succ = succ_v;
}
// cas : ->[v]->[t]-> => ->[t]->[v]->
else
{
// cas .->[v] => .->[t] vs autres cas
if (prec_v == NULL)
{
printf("case : .->[v]->[t]-> => .->[t]->[v]->\n");
*pl = t;
}
else
{
printf("case : [..]->[v]->[t]-> => [..]->[t]->[v]->\n");
prec_v->succ = t;
}

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
59
Chapitre 2

Structures squentielles simples
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
// (fonction scurise par lappelant :
// paramtres supposs cohrents entre eux)
int getPrecAndSucc(list l, list t, list* prec, list* succ)
{
if (l == t) // cas de t en tte
60
Corrigs des exercices
{
*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;
}
}
}
Exercice 2.5 Supprimer des lments
tude
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
Entre : x, lentier dont on supprime toutes les occurrences dans la liste.
Entre modi : la liste dont on supprime des occurrences de x.
Sortie : un code de statut dexcution
SUPPRESSION (1) : il y a eu au moins un retrait effectif.
IDENTITE (0) : il ny a eu aucun retrait car la liste ne comporte aucun x.
Cette fonction utilise un type numr (numration) qui est un type dni par les valeurs que
peuvent prendre les variables de ce type. Ainsi, dans la fonction suivante, les variables statut et
st2 ne peuvent prendre que les valeurs SUPPRESSION ou IDENTITE.
FONCTION supprimerOccurrences(*l: liste<entier>, x: entier)
VAR statut, st2: numration {SUPPRESSION, IDENTITE}; e: entier
DEBUT
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
61
Chapitre 2

Structures squentielles simples
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
Retrait de toutes les occurrences dun lment aprs la k
ime
Trs semblable au prcdent.
Entres : x, lentier dont on supprime toutes les occurrences sauf les k premires dans la liste, k le
nombre doccurrences quon laisse sauves.
Entres modies : la liste dont on supprime certaines occurrences de x.
Sortie : un code de statut dexcution
SUPPRESSION (1) : il y a eu au moins un retrait effectif.
IDENTITE (0) : il ny a eu aucun retrait car la liste ne comporte aucun x.
FONCTION supprOccurAprsKime(*l: liste<entier>, x: entier, k: entier)
VAR statut, st2: numration {SUPPRESSION, IDENTITE}; e: entier;
DEBUT
statut IDENTITE
SI estvide(l) ALORS
SI premier(l) = x ALORS
SI k < 1 ALORS
l fin(l)
statut SUPPRESSION
SINON
e premier(l)
l fin(l)
62
Corrigs des exercices
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
Exercice 2.6 Inverser une liste
tude
Figure 2.10 Inversion dune liste
Deux approches, lune itrative, lautre rcursive.
Algorithme itratif
FONCTION reverse(*pl : list)
VAR prev, curr, succ : 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
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
63
Chapitre 2

Structures squentielles simples
RETOURNER
FINSI
prev *pl
curr prevsucc
SI currsucc = NULL ALORS
currsucc prev
prevsucc NULL
*pl curr
RETOURNER
SINON
prevsucc NULL
FINSI
TANTQUE currsucc = 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 1
er
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 1
er
->NULL (queue)
}
list succ;
while (curr->succ != NULL) // Expl. sur 1re itration
{
succ = curr->succ; // sauv. du succ (le 3me lt)
64
Corrigs des exercices
curr->succ = prev; // pointage du 2d elt sur le 1
er
prev = curr; // le nouveau prev cest le 2d
curr = succ; // le nouveau curr, cest le 3
me
}
// 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 3
me
(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;
}

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
65
Chapitre 2

Structures squentielles simples
Exercice 2.7 Construire une liste partir dune source de donnes
Cration depuis un tableau dentiers
Spcication de lalgorithme
FONCTION newListFromArray(content : entier[], longueur : entier) : list
{
VAR listenouv, courant : list ; i : entier
DEBUT
SI (longueur < 1) ALORS // la liste sera vide
RETOURNER NULL
FINSI
listenouv newSingletonList(content[0]) // construction de la tte
courant listenouv
// ajout des autres places la suite
POUR i DE 1 A longueur-1 FAIRE
courantsucc newSingletonList(content[i])
courant courantsucc // passage au suivant
FAIT
RETOURNER listenouv // retour de la tte, qui reprsente la liste
FIN
Implantation C
list newListFromArray(int* content, int length)
{
if (length < 1) return emptyList(); // cas de la liste vide
// sinon : initialisation de la chane avec la cration de la tte
list l = newSingletonList(content[0]);
// dclaration et initialisation dun variable ditration de liste
list pp = l;
// itration de la 1
re
place la dernire ([longueur-1] intervalles)
int i;
for (i = 1; i < length; i++)
{
pp->succ = newSingletonList(content[i]);
//pp->succ->prev = pp; // chanage arrire (opt.)
pp = pp->succ; // itration de la liste
}
return l;// retour du pointeur sur la tte, lequel reprsente la liste
}
Il est possible mais risqu, par lastuce suivante, dviter le passage du paramtre qui indique la
longueur du tableau source : size_t longueur = sizeof(contenu) / sizeof(int);
66
Corrigs des exercices
Duplication dune liste existante
Spcication de lalgorithme
FONCTION duplicate(l : list) : list
VAR tte, copie, copie_tte : list
DEBUT
SI (l = NULL) ALORS
RETOURNER NULL
FINSI
tte l
copie newSingletonList(lcontent)
copie_tte copie
TANTQUE (lsucc = NULL) ET (lsucc = tte) FAIRE
l lsucc; // parcours de la liste dorigine
// cration dune place partir de la place dorigine
copiesucc newSingletonList(lcontent)
// insertion de la nouvelle place dans la copie
SI (lprev = NULL) ALORS
copiesuccprev copie
FINSI
copie copiesucc
FAIT
SI (lsucc = NULL) ALORS
copiesucc copie_tte
FINSI
RETOURNER copie_tte
FIN
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
}

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
67
Chapitre 2

Structures squentielles simples
if (l->succ != NULL) dupl->succ = dupl_head;
return dupl_head;
}
Exercice 2.8 Refermer une liste sur elle-mme
Spcication
Donne modifie : la liste transforme
FONCTION refermer_sur_elle_meme(*l: liste)
VAR p: place
DEBUT
SI estvide(*l) ALORS
RETOURNER
FINSI
p tte(*l)
TANTQUE dernier(p) FAIRE
p succ(p)
FAIT
succ(p) tte(*l)
FIN
Ralisation en C
Donne modifie : la liste transforme
void lin2circ(plist pl)
{
if (pl == NULL) return;
list l = *pl;
if (l == NULL) return;
while (l->succ != NULL) l = l->succ; // itration de la liste
l->succ = *pl;
}
Exercice 2.9 Effectuer et retourner deux calculs sur une liste
tude
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
Corrigs des exercices
La difcult 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 modies.
On utilisera un code de statut pour rendre compte des rsultats spciaux vs. standard dont les
valeurs signient les congurations 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.
Spcication abstraite
Entre : la liste dentiers.
Entre modifie : le produit des positifs et le produit des ngatifs.
Sortie : code de statut qui peut prendre lun des 4 tats TOUT (4),
UNIQUEMENT_POSITIFS (2), UNIQUEMENT_NEGATIFS (1), RIEN (0).
Algorithme :
FONCTION produits(l: liste<entier>, pos, neg: entier): statut
VAR pos_ok, neg_ok: boolen; p: place;
DEBUT
pos 1
pos_ok faux
neg 1
neg_ok faux
SI estvide(l) ALORS
p tte(l)
SI contenu(p) > 0 ALORS
pos contenu(p)
pos_ok vrai
FINSI
SI contenu(p) < 0 ALORS
neg contenu(p)
neg_ok vrai
FINSI
TANTQUE dernier(p) FAIRE
p succ(p)
SI contenu(p) > 0 ALORS
pos pos * contenu(p)
pos_ok vrai
FINSI
SI contenu(p) < 0 ALORS
neg neg * contenu(p)
neg_ok vrai
FINSI
FAIT
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
69
Chapitre 2

Structures squentielles simples
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
// status code : 4 : tout, 2 : positifs, 1 : ngatifs, 0 : rien
int produits(list l, int *pos, int *neg)
{
if (l == NULL) return 0;
int pos_prod = 1;
int pos_prod_ok = 0;
int neg_prod = 1;
int neg_prod_ok = 0;
int content = l->content; // pas indispensable mais conomique
if (content > 0)
{
pos_prod = content;
pos_prod_ok = 1;
}
if (content < 0)
{
neg_prod = content;
neg_prod_ok = 1;
}
while (l->succ != NULL)
{
l = l->succ;
content = l->content;
if (contenu > 0)
{
pos_prod *= content;
pos_prod_ok = 1;
}
if (contenu < 0)
{
70
Corrigs des exercices
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;
}
Exercice 2.10 Couper une liste en deux
tude
Dans les cinq cas de gure, il sagit de vrier 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 vrier que le pointeur dsigne bien un maillon de la liste ;
B vrier que la liste contient bien la donne x ;
C vrier que la liste contient au moins k lments ;
D identier 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 lidentication 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 modier 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
Donnes modifies : l
1
, la liste dentre coupe en deux qui devient la liste
amont, et l
2
la liste aval ventuellement produite en sortie.
Rsulat : un entier comme statut dexcution
-1 = ERR :ERREUR,
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
71
Chapitre 2

Structures squentielles simples
0 : opration blanche,
1 : une coupe a t effectue,
2 : statut 1 + l
2
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
Donne : cutPoint, le pointeur sur la cellule suivant le point de coupe
int cutA(plist pl1, plist pl2, list cutPoint)
{
/// CODE GENERIQUE COMMUN DINITIALISATION ///
// si cutPoint est NULL, autant arrter ici
if (cutPoint == NULL) return 0;
// cas de la coupure en tte de liste :
if (cutPoint == l1)
{
*pl1 = NULL;
*pl2 = l1;
return status;
}
// sinon, on parcourt la liste jusquau prdcesseur du point de coupe
// ou jusqu la queue de liste (cutPoint pointe sur une autre liste)
while (l1->succ != NULL && l1->succ != cutPoint) l1 = l1->succ;
// si nous avons atteint la fin de liste, cest un cas derreur
if (l1->succ == NULL) return ERR;
/// CODE GENERIQUE COMMUN DE FINALISATION ///
}
Cas du slecteur lment
Donne : x, llment de la cellule suivant le point de coupe
int cutB(plist pl1, plist pl2, int x)
{
72
Corrigs des exercices
/// CODE GENERIQUE COMMUN DINITIALISATION ///
// cas de la coupure en tte de liste :
if (x == l1->content)
{
*pl1 = NULL;
*pl2 = l1;
return status;
}
// sinon, on parcourt la liste jusquau prdcesseur du point de coupe
// ou jusqu la queue de liste (pas dlment x dans la liste)
while (l1->succ != NULL && l1->succ->content != x) l1 = l1->succ;
// si nous avons atteint la fin de liste, cest une opration blanche
if (l1->succ == NULL) return 0;
/// CODE GENERIQUE COMMUN DE FINALISATION ///
}
Cas du slecteur position
Donne : k, la distance de la tte de liste au point de coupe
int cutC(plist pl1, plist pl2, unsigned k)
{
/// CODE GENERIQUE COMMUN DINITIALISATION ///
// cas de la coupure en tte de liste :
if (k == 0)
{
*pl1 = NULL;
*pl2 = l1;
return status;
}
// sinon, on parcourt la liste jusquau prdcesseur du point de coupe
// ou jusqu la queue de liste (cutPoint pointe sur une autre liste)
while (l1->succ != NULL && --k > 0) l1 = l1->succ;
// si nous avons atteint la fin de liste, cest une erreur
if (l1->succ == NULL) return ERR;
/// CODE GENERIQUE COMMUN DE FINALISATION ///
}
Cas du slecteur lment minimal
int cutD(plist pl1, plist pl2)
{
/// CODE GENERIQUE COMMUN DINITIALISATION ///
// on rutilise la fonction minOf de mesure de longueur de liste
int min = minOf(l1); // sur vos copies, il faut la rcrire
// cas de la coupure en tte de liste :
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
73
Chapitre 2

Structures squentielles simples
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
int cutE(plist pl1, plist pl2)
{
/// CODE GENERIQUE COMMUN DINITIALISATION ///
// on rutilise la fonction len de mesure de longueur de liste
int n = len(l1); // sur vos copies, il faut la rcrire
// on calcule la distance au point de coupe (note: distance >= 1)
int cutPoint = (n % 2 == 0) ? n / 2 : n / 2 + 1;
int status = 1;
// on modifie le statut si *pl2 nest pas NULL
if (*pl2 != NULL) status = 2;
// on effectue le parcours jusquau prdcesseur du point de coupe
while (--cutPoint > 0) l1 = l1->succ;
// avoir atteint la queue de liste ne peut logiquement pas arriver..
/// CODE GENERIQUE COMMUN DE FINALISATION ///
}
Spcication 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 : l
1
, la liste dentre coupe en deux qui devient la liste
amont, et l
2
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 l
2
Rsultat : un statut dexcution
-1 = ERR :ERREUR,
0 = ID : opration blanche,
1 = OK : une coupe a t effectue,
FONCTION scinder(modif l
1
, l
2
= : liste; c: critre): statut
VAR p: place
74
Corrigs des exercices
DEBUT
c mettre__jour(c, l
1
)
SI estvalide(c) ALORS
RETOURNER ERR
FINSI
SI estvide(l
1
) ALORS
RETOURNER ID
FINSI
p tte(l
1
)
SI est_successeur_point_de_coupe(p) ALORS
l
2
l
1
l
1

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(l
2
) succ(p)
succ(p)
RETOURNER OK
FINSI
FIN
Exercice 2.11 Supprimer un sous-ensemble dune liste
tude
Cet exercice avec ses cinq cas de gure est une extension du principe algorithmique vu dans
lexercice 2.5 (supprimer des lments). La faon de dnir 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 prxe (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 simplie de lalgorithme gnral, laquelle prote 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 Spcication 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
Chapitre 2

Structures squentielles simples
rpond au critre de suppression. Cette fonction critre peut avoir dautres paramtres, notamment
un entier indiquant le seuil partir duquel eectuer la suppression pour les cas D et E.
Spcication de lalgorithme
FONCTION removeAll(*pl:list, critre(entier,...) : boolen ): entier
VAR l, suppr : list
DEBUT
SI (pl = NULL) ALORS
RETOURNER -1 // -1 signale une erreur
FINSI
l *pl
SI (l = NULL) ALORS
RETOURNER 0 // 0 indique quaucune suppression nest faite
FINSI
// suppression partir de la tte de liste
TANTQUE (l = NULL) ET (critre(lcontent,...)=vrai) FAIRE
suppr l
l supprsucc
LIBERER(suppr)
FAIT
*pl l
// suite de la liste
TANTQUE (l = NULL) FAIRE
TANTQUE (lsucc = NULL) ET (critre(lsucccontent,...)=vrai) FAIRE
suppr lsucc
lsucc suppr succ
LIBERER(suppr)
FAIT
l lsucc
FAIRE
RETOURNER 1 // indique que tout sest bien pass
FIN
Implantation C
Donnes modifies : la liste dont on supprime des lments.
Rsultat : un entier comme statut dexcution
-1 = ERR : erreur,
0 = ID : opration blanche,
1 = OK : au moins une suppression a t effectue.
int removeAll<V>(plist pl[, param<V>])
{
if (pl == NULL) return ERR; /// cas derreur
list l = *pl;
76
Corrigs des exercices
if (l == NULL) return ID; /// cas dopration blanche
list killed;
/// cas de suppression de toute la chane prfixe dlments impairs
while (l != NULL && tosuppr<V>(l->content, param<V>))
{
killed = l;
l = killed->succ;
free(killed);
}
*pl = l;
/// partir de ce point : chane vide, soit tte non supprime
/// supression des lments cf. critre et au-del de la tte
while (l != NULL)
{
while (l->succ != NULL && tosuppr<V>(l->succ->content, param<V>))
{
killed = l->succ;
l->succ = killed->succ;
free(killed);
}
/// on passe soit au conserv suivant, soit NULL (fin de chane)
l = l->succ;
}
return OK;
}
Adaptations pour les cas B E
Les correspondances suivantes permettent de spcialiser le schma gnral pour rpondre au quatre
cas de gure B, C, D, E :
Spcication de lalgorithme
FONCTION critre_estPair(n : entier) : boolen // casB
VAR pair : boolen
DEBUT
SI n % 2 = 0 ALORS
pair vrai
SINON
pair faux
FINSI
RETOURNER pair
FIN
FONCTION critre_estImpair(n : entier) : boolen // casC
DEBUT
RETOURNER critre_estPair(n)
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
77
Chapitre 2

Structures squentielles simples
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
int removeAllB(plist pl)
param<B>
tosuppr<B>(element) element % 2 == 0
int removeAllC(plist pl)
param<C>
tosuppr<C>(element) element % 2 == 1
int removeAllD(plist pl, int threshold)
param<D> int threshold
tosuppr<D>(element, threshold) element > threshold
int removeAllE(plist pl, int threshold)
param<E> int threshold
tosuppr<E>(element, threshold) element < threshold
Adaptations pour le cas A
Le cas de gure A, du fait de la rgularit des intervalles de suppression conduit une version
simplie 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
Corrigs des exercices
Spcication de lalgorithme
FONCTION removeAllA(*pl : list) : entier
VAR l, suppr : list
DEBUT
SI (pl = NULL) ALORS
RETOURNER -1
FINSI
l *pl
SI (l = NULL) ALORS
RETOURNER 0
FINSI
*pl lsucc
LIBERER(l)
l *pl
TANTQUE (l = NULL) ET (lsucc = NULL) FAIRE
suppr lsucc
lsucc suppr succ
LIBERER(suppr)
l lsucc
FAIT
RETOURNER 1
FIN
Implantation C
int removeAllA(plist pl)
{
if (pl == NULL) return ERR; /// cas derreur
list l = *pl;
if (l == NULL) return ID; /// cas dopration blanche
/// suppression de la tte
*pl = l->succ;
free(l);
l = *pl;
/// supression raison dun nud sur deux
list killed;
while (l != NULL && l->succ != NULL)
{
killed = l->succ;
l->succ = killed->succ;
free(killed);
l = l->succ;

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
79
Chapitre 2

Structures squentielles simples
}
return OK;
}
CORRIG DU PROBLME
Problme 2.1 Saisir, enregistrer puis valuer un polynme
tude
Pour le module de saisie, on sinspirera de la fonctionnalit de construction de liste partir de la
saisie sur lentre standard, dont on fera une adaptation.
Pour lvaluation, il sagit dun calcul itratif.
Deux fonctionnalits utilitaires pourront constituer une amlioration de la version de base :
Une fonction dafchage de la formule polynme.
Une fonction (laisse en exercice non corrig) de normalisation du polynme aprs sa saisie et
avant son emploi pour des valuations (optimisation).
La structure de donne
Pseudo code formel
STRUCTURE terme
coeff : rel
exposant : entier
*suivant : terme
TYPE *polynome : terme
Implantation C
typedef struct term
{
long int coeff; // intervalle : -2 147 483 648 2 147 483 647
long int expon;
struct term* nextTerm;
} term;
typedef term *polynomial;
Saisie du polynme
Implantation C
// Fonction drive de la fonction
// constr::newListFromStandardInput(list* pl) : int
void recordPolynomial(polynomial* pp)
{
*pp = NULL;
int size = 1;
char coeffStr[11];
80
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;
}
Normalisation du polynme saisi
Spcication de lalgorithme
FONCTION normalizePolynomial(*pp : polynome)
VAR ???
DEBUT
???
FIN

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
81
Chapitre 2

Structures squentielles simples
Implantation C
void normalizePolynomial(polynomial* pp)
{
A VOUS DE JOUER !!
}
valuation du polynme (instanciation)
Spcication de lalgorithme
FONCTION evalPolynomial(p : polynome, x : rel) : rel
VAR rsultat, valeur : rel
DEBUT
rsultat 0
SI (p = NULL) ALORS
RETOURNER rsultat
FINSI
rsultat rsultat + (pcoeff) * (puissance(x, pexposant))
TANTQUE (psuivant = NULL) FAIRE
p psuivant
valeur (pcoeff) * (puissance(x, pexposant))
rsultat rsultat+valeur
FAIT
RETOURNER rsultat
FIN
Implantation C
long evalPolynomial(polynomial p, long x)
{
long res = 0;
if (p == NULL) return res;
res += (p->coeff) * (powl(x, p->expon));
printf("first term eval : %ld\n", res);
while (p->nextTerm != NULL)
{
p = p->nextTerm;
long termVal = (p->coeff) * (powl(x, p->expon));
printf("next term eval : %ld\n", termVal);
res += termVal;
}
return res;
}
82
Corrig du problme
Achage du polynme
Spcication de lalgorithme
FONCTION printPolynomial(p : polynome)
DEBUT
SI (p = NULL) ALORS
RETOURNER
FINSI
printTerm(p)
TANTQUE (psuivant = NULL) FAIRE
p psuivant
printTerm(p)
FAIT
FIN
Implantation C
void printPolynomial(polynomial p)
{
if (p == NULL) return;
printf("[");
printTerm(p);
while (p->nextTerm != NULL)
{
p = p->nextTerm;
printTerm(p);
}
printf("]");
}
Achage dun terme du polynme
Spcication de lalgorithme
FONCTION printTerm(p : polynome)
DEBUT
SI (p = NULL) ALORS
RETOURNER
FINSI
SI (pcoeff = 0) ALORS
RETOURNER
FINSI
SI (pcoeff = 1) ALORS
SI (pexposant = 0) ALORS
AFFICHER("+1")
SINON
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
83
Chapitre 2

Structures squentielles simples
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);
}
}
Fonction principale (saisie et valuations de polynmes)
void testPolynomial()
{
printf("\n\t\t>> TD 1.10 Saisie et evaluation de polynomes <<\n\n");
int i = 1;
char input[10];
polynomial newPolynomial;
printf("\nSouhaitez-vous creer un nouveau polynome depuis
la ligne de commande (OUI/*) ?\n\n");
printf("newPolynomial %d> ", i);
scanf("%s", input);
printf("\n");

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
85
Chapitre 2

Structures squentielles simples
while (strcmp("OUI", input) == 0)
{
recordPolynomial(&newPolynomial);
printf("Le polynome saisi est : ");
printPolynomial(newPolynomial);
printf("\n");
testEvalPolynomial(newPolynomial);
printf("\nSouhaitez-vous creer un nouveau polynome depuis
la ligne de commande (OUI/*) ?\n\n");
printf("newPolynomial %d> ", i);
scanf("%s", input);
printf("\n");
}
}
Fonction principale dlgue (valuations dun polynme)
void testEvalPolynomial(polynomial p)
{
int i = 1;
char input[10];
long x;
printf("\nSouhaitez-vous evaluer le polynome pour une nouvelle
valeur de x (OUI/*) ?\n\n");
printf("eval %d> ", i);
scanf("%s", input);
printf("\n");
while (strcmp("OUI", input) == 0)
{
printf("x %d> ", i);
scanf("%s", input);
printf("\n");
x = atol(input);
long result = evalPolynomial(p, x);
printPolynomial(p);
printf("(x = %ld) -> [%ld]\n", x, result);
i++;
printf("\nSouhaitez-vous evaluer le polynome pour une nouvelle
valeur de x (OUI/*) ?\n\n");
printf("eval %d> ", i);
scanf("%s", input);
printf("\n");
}
}
86
3
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.
Dnition dune structure de pile ralise avec un tableau
Pseudo code formel
STRUCTURE tStack
content : T[n]
top : entier
Implantation C
typedef struct tStack
{
<type> content[n];
int top;
} tStack;
87
Chapitre 3

Structures squentielles complexes
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 1 8
3.1.2 Reprsentation chane des piles
Les lments de la pile sont chans entre eux, et le sommet dune pile non vide est le pointeur vers
le premier lment de la liste.
Exemple de dnition dune structure de pile chane
Pseudo code formel
STRUCTURE pile
sommet : liste
nb_elt : entier
Implantation C
typedef struct pile
{
liste sommet;
int nb_elt;
} pile;
3.1.3 Manipulation dune pile
Initialiser une pile juste dclare
Dclarations
Rsultat de type pile
En-tte : pile init()
Algorithme initialiser
FONCTION init() : pile
VAR p : pile
DEBUT
p.sommet NULL
p.nb_elt 0
RETOURNER p
FIN
Tester si la pile est vide
Dclarations
Donne : pile p
Rsultat : de type boolen
En-tte en C : int pile_vide (pile p)
88
3.1. Piles
Algorithme pile vide
FONCTION pilevide(p : pile) : boolen
VAR vide : boolen
DEBUT
SI p.nb_elt=0 ALORS
vide vrai
SINON
vide faux
RETOURNER vide
FIN
Placer un lment au sommet de la pile (empiler)
Dclarations
Donne : T elt__emp
Donne modie : pile *pp
En-tte en C : void empiler(T elt_a_emp, pile *pp);
Algorithme empiler
FONCTION empiler(elt_a_emp : T ,*pp : pile)
VAR courant : liste
DEBUT
RESERVER(courant)
courantinfo elt__emp
// si la pile tait vide alors sommet tait NULL
// et on cre la pile :
courantsuivant ppsommet
ppsommet courant // rattache ancien sommet
ppnb_elt ppnb_elt + 1
FIN
Retirer un lment du sommet de la pile (dpiler)
Dclarations
Donne modie : pile *pp, T *elt_dep
Rsultat : de type boolen
En-tte en C : int depiler(pile *pp, T *elt_dep);
Algorithme dpiler
FONCTION depiler(*pp :pile, *elt_dep : T) : boolen
VAR courant : ptr_poste; ok : boolen;
DEBUT
SI non pile_vide(*pp) ALORS
ok vrai
*elt_dep ppsommetinfo
ppnb_elt ppnb_elt -- 1
// libration de lespace mmoire :
courant ppsommet
// si la pile contenait un seul lment,
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
89
Chapitre 3

Structures squentielles complexes
// elle devient vide, et *pp.sommet devient NULL
ppsommet ppsommetsuivant
LIBERER (courant)
SINON
ok faux
RETOURNER ok
FIN
3.2 LES FILES
Dans le cas dune le on fait les adjonctions une extrmit, les accs et les suppressions lautre
extrmit. Par analogie avec les les dattente on dit que llment prsent depuis le plus longtemps
est le premier, on dit aussi quil est en tte.
Les les sont aussi appeles FIFO pour First-In-First-Out, cest--dire premier entr, premier
sorti.
Les oprations sur les les sont :
tester si la le est vide ;
accder au premier lment de la le ;
ajouter un lment dans la le ;
retirer le premier lment de la le.
3.2.1 Reprsentation contigu des les
Dans ce cas on doit conserver lindice i du premier lment et lindice j de la premire case libre
aprs la le. On fait progresser ces indices modulo la taille lmax du tableau. Le seul point dlicat
est la dtection des dbordements.
Dnition dune structure de le ralise avec un tableau
Pseudo code formel
STRUCTURE file
donne : T[n]
tte : entier
fin : entier
Implantation en C
typedef struct file
{
T donne[n];
int tte;
int fin;
} file;
90
3.2. Les les
3.2.2 Reprsentation chane des les
Dans le cas dune reprsentation chane, soit on a deux pointeurs, tte et dernier, vers le premier
et le dernier lment de la le, soit on utilise le pointeur qui suit le dernier lment pour reprer le
premier lment. On a donc une reprsentation circulaire.
3.2.3 Manipulation dune le (mthode avec deux pointeurs)
Dnition dune structure de le chane
Pseudo code formel
STRUCTURE poste
info : T
*suivant : poste
TYPE *ptr_poste : poste
STRUCTURE file
tte : ptr_poste
fin : ptr_poste
nb_elt : entier
Implantation en C
typedef struct poste
{
T info;
struct poste *suivant;
} poste;
typedef poste* ptr_poste;
typedef struct file
{
ptr_poste tte;
ptr_poste fin;
int nb_elt;
} file;
Initialiser une le juste dclare
Dclarations
Rsultat de type file
En-tte en C : file init()
Algorithme initialiser
FONCTION init()
VAR f : file
DEBUT
f.tete NULL
f.fin NULL
f.nb_elt 0
RETOURNER f
FIN

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
91
Chapitre 3

Structures squentielles complexes
Tester si la le est vide
Dclarations
Donne : file f
Rsultat : de type boolen
En-tte en C : int file_vide (file f)
Algorithme le vide
FONCTION file_vide(f :file) : boolen
VAR vide: boolen
DEBUT
SI f.nb_elt = 0 ALORS
vide vrai
SINON
vide faux
FINSI
RETOURNER vide
FIN
Placer un lment la n de la le (enler)
Dclarations
Donne : T elt__enf
Donne modie : file *ff
En-tte C : void enfiler(T elt_a_enf, file *ff);
Algorithme enler
FONCTION enfiler(x : T, *ff : file)
VAR courant : ptr_poste
DEBUT
RESERVER(courant)
courantinfo x
courantsuivant NULL
SI fftete = NULL ALORS
fftete courant
fffin courant
SINON
fffinsuivant courant
fffin courant
FINSI
ffnb_elt ffnb_elt + 1
FIN
Retirer un lment de la tte de la le (dler)
Dclarations
Donne modie : file *ff, T *elt_def
Rsultat : de type boolen
En-tte : int defiler(file* ff, T *elt_dep);
92
3.2. Les les
Algorithme dler
FONCTION defiler(ff : file, *elt_def : T) : boolen
VAR courant : ptr_poste , ok : boolen
DEBUT
SI non file_vide(*ff) ALORS
ok vrai
*elt_def ffteteinfo
ffnb_elt ffnb_elt -- 1
// opration pour librer lespace mmoire :
courant fftete
fftete fftetesuivant
LIBERER(courant)
SI ffnb_elt = 0 ALORS
// si la file contenait un seul lment,
// elle devient vide, et fffin devient NULL,
// ainsi que fftete
fffin NULL
FINSI
SINON
ok faux
FINSI
RETOURNER ok
FIN
Remarque
Une le se reprsente beaucoup mieux en dynamique (reprsentation chane).
Utilisation des les : 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 buer dimprimante est gr en le dattente.
Exemple de gestion dun buer 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.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
93
Chapitre 3

Structures squentielles complexes
crire : cest mettre une information rfrence par le pointeur Ecriture si cela est possible, et
dplacer le pointeur dcriture sur le poste suivant.
Figure 3.1 tat initial du buffer clavier
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
3.2. Les les
Figure 3.3 Lecture et criture simultanes
Modle statique
Pseudo code formel
STRUCTURE elt
info : T
lire : boolen
Implantation C
typedef struct elt {
T info;
boolen lire;
} elt;
lire est un ag (drapeau) :
lire = vrai : on peut lire, mais pas crire ;
lire = faux : on peut crire, mais pas lire.
Pseudo code formel
SRUCTURE liste_circ
donne : elt[n]
lecteur : entier
crivain : entier

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
95
Chapitre 3

Structures squentielles complexes
Implantation C
typedef struct liste_circ {
elt donne [n];
int lecteur;
int crivain;
} liste_circ
Initialisation
Dclarations
Donne : int n
Donne modie : liste_circ *l
En-tte : void init(liste_circ *l);
Algorithme
FONCTION init(*l : liste_circ)
VAR i : entier
DEBUT
POUR i DE 0 A n-1 FAIRE
ldonne[i].lire faux
llecteur 0
lecrivain 0
FAIT
FIN
Lecture
Donne : int n
Donne modie : 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
3.2. Les les
criture
Donne : int n, T elt_ecr
Donne modie : 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
typedef struct elt
{
T info;
boolen lire;
struct elt *suivant;
} elt;
typedef ptr_elt *elt;
typedef struct liste_circ {
ptr_elt lire;
ptr_elt ecrire;
} liste_circ;

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
97
Chapitre 3

Structures squentielles complexes
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
NONCS DES EXERCICES ET DES PROBLMES
EXERCICES
Exercice 3.1 Afcher une liste chane
****
crire un algorithme pour afcher une liste chane dentiers sous la forme suivante :
Si la liste est linaire :
Nom_liste: .->[elt
1
]->[elt
2
]->...->[elt
n
]->[elt
last
] |
Si la liste est circulaire :
Nom_liste: .->[elt
1
]->[elt
2
]->...->[elt
n
]->[elt
last
] ...
Si certains maillons contigus sont doublement chans, afcher ]<->[ au lieu de ]->[.
Apportez une attention particulire aux traitements spciques de dbut, milieu et n de liste ainsi
quaux cas spciaux comme la liste vide ou singleton.
Exercice 3.2 Construire une liste circulaire ordonne
***
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 le
**
Concevoir deux algorithmes, qui crent respectivement :
la le inverse dune le ;
la pile inverse dune pile.
Ces deux algorithmes doivent restituer leur entre inchange.
Exercice 3.5 Simuler la rcursivit laide dune pile
*
crire un algorithme pour calculer la somme de 1 n n N

en simulant la rcursivit laide


dune pile.
Les en-ttes des oprations utiliser pour cet exercice sont fournis :
FONCTION nouvellePile() : pile
FONCTION estPileVide(p : pile) : boolen
FONCTION empiler (val : T, *pp : pile)
FONCTION depiler (*pval : T, *pp : pile) : entier
98
Problmes
Exercice 3.6 Insrer et supprimer dans une liste doublement chane
**
crire un algorithme dinsertion dans une liste doublement chane.
crire un algorithme de suppression dans une liste doublement chane.
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 k
imes
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 k
ime
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 modication de la liste pendant le traitement (lecture seule) ;
pas de copie de linformation des nuds traverss.
Corrigs des exercices et des problmes
PRAMBULE
Reprsentation dune structure de le laide dune liste
Pseudo code formel
STRUCTURE lQueue
head : list
queue : list
length : entier
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
99
Chapitre 3

Structures squentielles complexes
// quelques fonctions utiles, on fournit leurs en-ttes
FONCTION newEmptyQueue() : lQueue*
FONCTION isEmptyQueue(*p_q : lQueue) : boolen
FONCTION qPush(e : entier, *p_q : lQueue)
FONCTION qPop(*pe : entier, *p_q : lQueue) : entier
Implantation en C
/** structure de file ralise avec une liste simplement chane
dont la tte est lentre, la queue la sortie **/
typedef struct lQueue
{
list head;
list queue;
int length;
} lQueue;
/** Oprations **/
lQueue *newEmptyQueue();
int isEmptyQueue(lQueue *p_q);
void qPush(int e, lQueue *p_q);
int qPop(int* pe, lQueue* p_q);
Reprsentation dune structure de pile laide dune liste
Pseudo code formel
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
/** structure de pile ralise avec une liste simplement chane
dont la tte est le sommet, la queue la base **/
typedef struct lStack
{
list top;
int depth;
} lStack;
/** Oprations **/
100
Corrigs des exercices
lStack *newEmptyStack();
int isEmptyStack(lStack *p_s);
void sPush(int e, lStack *p_s);
int sPop(int* pe, lStack* p_s);
CORRIGS DES EXERCICES
Exercice 3.1 Afcher une liste chane
Spcication de lalgorithme
FONCTION printListGraph(l : list, nom : caractere[])
VAR tte : list
DEBUT
SI (l = NULL) ALORS
AFFICHER(nom, : .| la liste est vide)
SINON
tte l
AFFICHER(nom," : .")
SI (tteprev = NULL) ALORS // dbut de liste ?
AFFICHER("->",ttecontent)
SINON
AFFICHER("<->",ttecontent)
FINSI
TANTQUE (lsucc = NULL) ET (lsucc = tte) FAIRE
l lsucc
SI (lprev = NULL) ALORS
AFFICHER("->", lcontent)
SINON
AFFICHER("<->", lcontent)
FINSI
FAIT
SI (lsucc = NULL) ALORS
AFFICHER(" |")
SINON
AFFICHER(" ...")
FINSI
FINSI
FIN
Ralisation en C
void printListGraph(list l, char * nom)
{
if (l == NULL) printf("%s : . | (empty list !!)", nom);
else
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
101
Chapitre 3

Structures squentielles complexes
{
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");
}
Exercice 3.2 Construire une liste circulaire ordonne
Analyse
Dans cet exercice, la circularit de la liste produire est accessoire. Il est naturel de penser
travailler sur une liste linaire standard puis de naliser 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 ux 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 difcult (mais quelle lgance) supplmentaire si on choisit dinsrer dans une liste
circulaire ds son initialisation.
Le mode de tri ntait pas impos : vous pouvez laisser libre cours votre savoir-faire et vos
connaissances en ce domaine.
Ralisation
Ci-aprs deux solutions (mthode en ux 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
Corrigs des exercices
Mthode itrative classique
Spcication de lalgorithme
/** construit une liste circulaire ordonne dentiers
partir dun tableau non ordonn dentiers **/
FONCTION newOrdoredCircList(donnes : entier[], size : entier) : list
VAR i, valeur: entier ; trouv : boolen; tete, sl_val, l : list
DEBUT
SI size=0 ALORS // pas de donnes -> liste vide
RETOURNER NULL
FINSI
// construction de la tte partir du tableau de donnes
tete newSingletonList(donnes[0])
l tete
POUR i DE 1 A size-1 FAIRE
valeur donnes[i]
trouve faux
sl_val newSingletonList(valeur)
// insertion en tte de liste
SI valeur < lcontent ALORS
sl_valsucc l
l sl_val
tete l
SINON // insertion faire en milieu de liste
TANTQUE (lsucc = NULL) ET (trouv = faux) FAIRE
SI val < lsucccontent ALORS // il faut insrer ici
sl_valsucc lsucc
lsucc sl_val
trouv vrai
SINON // parcours de la liste : passage au suivant
l lsucc
FINSI
FAIT
SI lsucc = NULL ALORS // cas o on a atteint la fin de liste
lsucc sl_val
FINSI
FINSI
FAIT
concat(&tete,&tete) // pour rendre la liste circulaire
RETOURNER tete
FIN

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
103
Chapitre 3

Structures squentielles complexes
Implantation C
/** construit une liste circulaire ordonne dentiers
partir dun tableau non ordonn dentiers **/
list newOrdoredCircList(int *data, unsigned size)
{
if (data == NULL) return NULL;
int i;
int val;
int found;
list head = newSingletonList(data[0]);
list sl_val, l = head;
for (i = 1; i < size; i++)
{
val = data[i];
found = 0;
sl_val = newSingletonList(val);
/// insertion en tte de liste
if (val < l->content)
{
sl_val->succ = l;
l = sl_val;
head = l;
}
else
{
/// insertion en milieu de liste
while (l->succ != NULL && !found)
{
if (val < l->succ->content)
{
sl_val->succ = l->succ;
l->succ = sl_val;
found = 1;
}
else l = l->succ;
}
/// insertion en fin de liste
if (l->succ == NULL) l->succ = sl_val;
}
}
concat(&head, &head);
return head;
}
104
Corrigs des exercices
Mthode rcursive
Spcication de lalgorithme
/** Construit une liste circulaire ordonne dentiers
partir dun tableau non ordonn dentiers **/
FONCTION newOrdoredCircListRec(donnes : entier[], size : entier) : list
VAR valeur : entier ; trouv : boolen ; l, tte, l_succ, liste0 : list
DEBUT
SI size = 0 ALORS
RETOURNER NULL
FINSI
SI size = 1 ALORS
liste0 newSingletonList(donnes[0])
liste0succ list0
RETOURNER liste0
FINSI
// appel rcursif, o donnes+1 reprsente le sous-tableau
// du tableau donnes commenant lindice suivant
// (on a supprim la premire case du tableau)
l newOrdoredCircListRec(donnes+1,size-1)
tte l
valeur donnes[0]
trouv faux
TANTQUE (lsucc = tte) ET (trouv = faux) FAIRE
SI lcontent > lsucccontent ALORS
SI (val > lcontent) OU (val > lsucccontent) ALORS
trouv vrai
SINON
l lsucc
FINSI
SINON
SI (val > lcontent) ET (val > lsucccontent) ALORS
trouv vrai
SINON
l lsucc
FINSI
FINSI
FAIT
l_succ lsucc
lsucc newSingletonList(donnes[0])
lsuccsucc l_succ
l lsucc
RETOURNER l
FIN

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
105
Chapitre 3

Structures squentielles complexes
Implantation C
/** Construit une liste circulaire ordonne dentiers
partir dun tableau non ordonn dentiers **/
list newOrdoredCircListRec(int *data, unsigned size)
{
if (size == 0) return NULL;
if (size == 1)
{
list l0 = newSingletonList(data[0]);
l0->succ = l0;
return l0;
}
list l = newOrdoredCircListRec(data + 1, size - 1);
list l_head = l;
int val = data[0];
int found = 0;
while (l->succ != l_head && !found)
{
if (l->content > l->succ->content)
{
if (val > l->content || !(val > l->succ->content)) found = 1;
else l = l->succ;
}
else
{
if (val > l->content && !(val > l->succ->content)) found = 1;
else l = l->succ;
}
}
list l_succ = l->succ;
l->succ = newSingletonList(data[0]);
l->succ->succ = l_succ;
l = l->succ;
return l;
}
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
Corrigs des exercices
(
!(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;
Pour eectuer des tests
void testOrderedCirc()
{
printf(">> ordered circular lists\n");
int data[12] = {29, 23, 17, 11, 5, 2, 3, 7, 13, 19, 25, 31};
printListGraph(newOrdoredCircList[Rec](data, 12), "OCL ({29 ... 31})");
printListGraph(newOrdoredCircList[Rec](NULL, 0), "OCL (NULL)");
printListGraph(newOrdoredCircList[Rec]((int[]) {1}, 1), "OCL ({1})");
}
Exercice 3.3 Raliser le chanage arrire dune liste doublement chane
Spcication de lalgorithme
FONCTION doubleChain(l : list)
VAR tte : list
DEBUT
SI (l = NULL) ALORS
RETOURNER // ceci termine la fonction
FINSI
tte l
TANTQUE (lsucc = NULL) ET (lsucc = tte) FAIRE
SI (lsuccprev = NULL) ALORS
lsuccprev l
FINSI
l lsucc
FAIT
SI (lsucc = NULL) ALORS
tteprev l
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
107
Chapitre 3

Structures squentielles complexes
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;
}
Exercice 3.4 Inverser pile et le
Inversion de pile
Spcication de lalgorithme
FONCTION reverseQueue(*p_q : Queue) : lQueue*
VAR valeur, longueur : entier; *inverse : lQueue; *temp : lStack
DEBUT
SI (p_q = NULL) ALORS
RETOURNER NULL
FINSI
longueur p_qlength
inverse newEmptyQueue() // rsultat de linversion
temp newEmptyStack() // pile pour stockage des valeurs
// principe : empiler tous les lments de la file
// puis les dpiler dans la file inverse
TANTQUE (longueur > 0) FAIRE
qPop(&valeur, p_q)
sPush(valeur, temp)
qPush(valeur, p_q)
longueur longueur-1
FAIT
/// construction de la file inverse :
longueur p_qlength
TANTQUE (longueur > 0) FAIRE
sPop(&val, temp)
qPush(val, inverse)
longueur longueur-1
108
Corrigs des exercices
FAIT
RETOURNER inverse
FIN
Implantation C
Entre : la file source dont il sagit de construire linverse.
Sortie : la file inverse.
/** Cre la file inverse de la file *p_q **/
lQueue *reverseQueue(/*const*/lQueue *p_q)
{
if (p_q == NULL)
{
fprintf(stderr, "reverseQueue error: null queue !!");
return NULL;
}
int val;
int length = p_q->length;
lQueue *p_rev_q = newEmptyQueue(); /// la file inverse retourne
lStack *p_tmp_s = newEmptyStack(); /// la pile pour linversion
while (length-- > 0)
{
qPop(&val, p_q); /// dfilement depuis la file dentre *p_q
sPush(val, p_tmp_s); /// empilement dans la pile dinversion
qPush(val, p_q); /// renfile val : rotation de *p_q
}
/// construction de la file inverse :
length = p_q->length;
while (length-- > 0)
{
sPop(&val, p_tmp_s);
qPush(val, p_rev_q);
}
free(p_tmp_s); /// Soyons diligents avec la mmoire !!
return p_rev_q;
}
/** Exemple dinversion de file **/
void testQueueReversing()
{
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:");
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
109
Chapitre 3

Structures squentielles complexes
lQueue *p_rev_q = reverseQueue(p_q);
printListGraph(p_rev_q->head, "reversed queue:");
printListGraph(p_q->head, "unchanged input queue:");
}
Inversion de pile
Spcication de lalgorithme
FONCTION reverseStack(*p_s : lStack) : lStack *
VAR valeur : entier ; *inverse, *temp : lStack
DEBUT
SI (p_s = NULL) ALORS
RETOURNER NULL
FINSI
inverse newEmptyStack()
temp newEmptyStack()
FAIRE
sPop(&valeur, p_s)
sPush(valeur, inverse)
sPush(valeur, temp)
TANTQUE ( isEmptyStack(p_s))
FAIRE
sPop(&valeur, temp)
sPush(valeur, p_s)
TANTQUE ( isEmptyStack(temp))
RETOURNER p_rev_s
FIN
Implantation C
Entre : la pile source dont il sagit de construire linverse.
Sortie : la pile inverse.
/** Cre la pile inverse de la pile *p_s **/
lStack *reverseStack(lStack *p_s)
{
if (p_s == NULL)
{
fprintf(stderr, "reverseStack error: null stack !!");
return NULL;
}
int val;
lStack *p_rev_s = newEmptyStack(); /// la pile inverse retourne
lStack *p_tmp_s = newEmptyStack(); /// une pile pour restituer *p_s
do
110
Corrigs des exercices
{
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:");
}
Exercice 3.5 Simuler la rcursivit laide dune pile
Spcication de lalgorithme
FONCTION triangularSum(n : entier) : entier
VAR valeur, rsultat : entier ; p : pile
DEBUT
SI (n = 0) ALORS
RETOURNER 0
FINSI
p initPile()
valeur n
rsultat 0
FAIRE // les valeurs sont mises sur la pile (empiles)
empiler(valeur, &p)
valeur valeur-1
TANTQUE (valeur > 0)
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
111
Chapitre 3

Structures squentielles complexes
FAIRE // puis les valeurs sont sorties de la pile (dpiles)
depiler(&val, &p)
rsultat rsultat+valeur
TANTQUE ( estPileVide(p))
RETOURNER res
FIN
Implantation C
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;
}
Exercice 3.6 Insrer et supprimer dans une liste doublement chane
tude
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 difcult.
Pour le reste, il sagit de mettre jour les prdcesseurs en sus des successeurs.
Les problmes dinsertion/suppression en dbut ou n de listes, vs. en milieu de liste restent peu
prs les mmes quavec une liste simplement chane.
Insertion
Spcication de lalgorithme
// Insertion dans une liste doublement chane
// pl : liste o seffectue linsertion
// place : place dans la liste (on insre juste devant)
112
Corrigs des exercices
// 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
// Insertion dans une liste doublement chane
// pl : liste o seffectue linsertion
// place : place dans la liste (on insre juste devant)
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
113
Chapitre 3

Structures squentielles complexes
// 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
Corrigs des exercices
Suppression
Spcication de lalgorithme
// Suppression dans une liste doublement chane
// Type abstrait : list supprimer(list l, int k);
// status code : 0 : ok, -1 : ko
FONCTION removeElement(*pl :list, place : list) : boolen
VAR prec : list
DEBUT
SI appartient(place, *pl) ALORS
RETOURNER faux
FINSI
// cas de la liste vide
SI isempty(*pl) ALORS // autre moyen de tester la liste vide
RETOURNER vrai
FINSI
// cas de la place en tte de liste
SI place = *pl ALORS
*pl (*pl)succ
SI placesucc = NULL ALORS
(*pl)succprev NULL
FINSI
LIBERER(place)
RETOURNER vrai
FINSI
prec placeprev
precsucc placesucc
SI placesucc = NULL ALORS
placesuccprev prec
FINSI
LIBERER(place)
RETOURNER vrai
FIN
Implantation C
// Suppression dans une liste doublement chane
// Type abstrait : list supprimer(list l, int k);
// status code : 0 : ok, -1 : ko
int removeElement(list* pl, list place)
{
// vrifier que la place est bien dans la liste concerne
// (mme mthode que pour lexercice 2.5)
if (!(areConvergent(place, *pl))) return -1;
// cas de la liste vide
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
115
Chapitre 3

Structures squentielles complexes
if (*pl == NULL) return 0;
// cas tte de liste :
if (place == *pl)
{
*pl = (*pl)->succ;
if (place->succ != NULL) (*pl)->succ->prev = NULL;
free(place);
return 0;
}
list prec = place->prev;
prec->succ = place->succ;
if (place->succ != NULL) place->succ->prev = prec;
free(place);
return 0;
}
CORRIGS DES PROBLMES
Problme 3.1 Problme de Joseph
tude
Phase de construction
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 Liste Joseph
116
Corrigs des problmes
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 nalisation dune liste circulaire doublement chane (reste deux, puis un lment, il
convient dtre prudent) :
Figure 3.6 Finalisation dune LDCC (Liste doublement chane circulaire)

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
117
Chapitre 3

Structures squentielles complexes
Architecture fonctionnelle
Figure 3.7 Architecture fonctionnelle de PlayJoseph
Fonction principale
Spcication de lalgorithme
FONCTION playJoseph(n : entier, k : entier)
VAR joseph : list, i : entier
DEBUT
AFFICHER("Joseph pour n=", n, " et k=", k)
joseph newJosephCList(n)
printListGraph(joseph, "1")
i 2
TANTQUE (joseph = NULL) FAIRE
joseph removeKst(&joseph, k)
AFFICHER(i)
i i+1
printListGraph(joseph, "")
FAIT
FIN
Implantation C
// Excution du procd dlimination de Joseph
void playJoseph(int n, int k)
{
printf("\nPlay Joseph for n=%d and k=%d\n", n, k);
list joseph = newJosephCList(n);
printListGraph(joseph, "1");
118
Corrigs des problmes
int i = 2;
while (joseph != NULL)
{
joseph = removeKst(&joseph, k);
printf("%d", i++); printListGraph(joseph, "");
}
}
Construction de la liste circulaire
Spcication de lalgorithme
FONCTION newJosephCList(longueur : entier) : list
VAR joseph : list
DEBUT
SI (longueur < 1) ALORS
RETOURNER NULL
FINSI
joseph newJosephList(longueur, 1) // appel de la fonction qui suit
concat(&joseph, &joseph)
RETOURNER joseph
FIN
FONCTION newJosephList(longueur : entier, premier : entier) : list
VAR tte : list
DEBUT
SI (longueur < 1) ALORS
RETOURNER NULL
FINSI
RESERVER(tte)
ttecontenu premier
ttesucc newJosephList(longueur - 1, premier + 1)
RETOURNER tte
FIN
Implantation C
// Cration dune liste circulaire de 1 n (version rcursive)
list newJosephCList(int longueur)
{
if (longueur < 1) return NULL;
list joseph = newJosephList(longueur, 1);
concat(&joseph, &joseph); // voir exercice 2.2
return joseph;
}
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
119
Chapitre 3

Structures squentielles complexes
list newJosephList(int longueur, int first)
{
if (longueur < 1) return NULL; // cas de la liste vide
// initialisation de la chane avec la cration de la tte
list head = malloc(sizeof(place)); // allocation mmoire
head->contenu = first; // affectation du contenu
head->succ = newJosephList(longueur - 1, first + 1);
return head; // retour du pointeur sur la tte
}
Retrait du k
ime
lment
Spcication de lalgorithme
FONCTION removeKst(*pl : list, k :entier) : list
VAR tte, courant, prev : list
VAR circulaire : boolen ; primtre : entier
DEBUT
tte *pl
courant *pl
SI (courant = NULL) ALORS
RETOURNER NULL
FINSI
circulaire isCircular(courant)
primtre getCLength(courant)
SI (circulaire = vrai) ET (primtre = 1) ALORS
*pl NULL
RETOURNER *pl
FINSI
SI (circulaire = vrai) ALORS
prev getKstElement(*pl, primtre-1)
SINON
prev NULL
FINSI
TANTQUE (courantsucc = NULL) ET (k > 1) FAIRE
k k-1
prev courant
courant courantsucc
FAIT
SI (courantsucc = NULL) ET (k > 0) ALORS
RETOURNER *pl
FINSI
SI (circulaire = vrai) ALORS
prevsucc courantsucc
120
Corrigs des problmes
SI (courant = tte) ALORS
*pl courantsucc
FINSI
LIBERER(courant)
RETOURNER prevsucc
SINON
SI (prev = NULL) ALORS
*pl courantsucc
LIBERER(courant)
RETOURNER *pl
SINON
SI (courantsucc = NULL) ALORS
prevsucc NULL
LIBERER(courant)
RETOURNER NULL
SINON
prevsucc courantsucc
LIBERER(courant)
RETOURNER prevsucc
FINSI
FINSI
FINSI
FIN
Implantation C
// Supprime le k
ime
et retourne le suivant
// Si le 1
er
lment est supprim retour du suivant sinon *pl inchang
// note : la fonction fonctionne pour les listes
// circulaires et linaires simplement chanes
list removeKst(list* pl, int k)
{
list head = *pl;
list curr = *pl;
if (curr == NULL) return NULL;
// pour moduler les traitements : liste circulaire ou linaire ?
int circular = isCircular(curr);
int perimeter = getCLength(curr);
if (circular & perimeter == 1) // finalisation
{
*pl = NULL;
return NULL;
}
// mmorisation du prev : soit le prev de curr, soit NULL (linaire)
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
121
Chapitre 3

Structures squentielles complexes
list prev = (circular?getKstElement(*pl, perimeter - 1):NULL);
// on commence par itrer jusqu la bonne position.
// note : & != && : le k est dcrment ssi curr->succ != NULL
while (curr->succ != NULL & --k > 0)
{
prev = curr;
curr = curr->succ;
}
// si la liste nest pas circulaire
// et quon a atteint la queue alors que k > 0, on arrte
if (curr->succ == NULL && k > 0) return *pl;
// sinon : on supprime en tenant compte de tous les cas possibles
// cas de la liste circulaire
if (circular)
{
prev->succ = curr->succ;
// danger : si on retire le 1er : *pl pointe alors dans le vide
if (curr == head) *pl = curr->succ;
free(curr);
return prev->succ;
}
// liste linaire ---* la suite nest pas indispensable pour Joseph
else
{
// tete dune linaire
if (prev == NULL)
{
*pl = curr->succ;
free(curr);
return *pl;
}
else
{
if (curr->succ == NULL) // queue dune linaire
{
prev->succ = NULL;
free(curr);
return NULL;
}
else // milieu dune linaire : comme pour une circulaire
{
prev->succ = curr->succ;
free(curr);
122
Corrigs des problmes
return prev->succ;
}
}
}
}
Problme 3.2 Mesurer la longueur dune liste de type Heqat
Spcication de lalgorithme
FONCTION getHLength(l :list l, *longueurLinaire : entier,
*longueurCirculaire : entier): entier
VAR longLigne, longueur : entier ; tte, t2 : list
DEBUT
SI (l = NULL) ALORS
*longueurLinaire 0
*longueurCirculaire 0
RETOURNER 0
FINSI
SI (lsucc = l) ALORS
*longueurLinaire 0
*longueurCirculaire 1
RETOURNER 1
FINSI
longLigne 0
longueur 1
tte l
TANTQUE (lsucc = NULL) ET (lsucc = tte) FAIRE
l lsucc
longueur longueur+1
SI (lsucc = NULL) ET (lsucc = tte) ALORS
*longueurLinaire 0
*longueurCirculaire longueur
RETOURNER longueur
FINSI
t2 tte
longLigne 0
TANTQUE (t2 = l) ET (t2succ = NULL) FAIRE
t2 t2succ
longLigne longLigne+1
SI (lsucc = NULL) ET (lsucc = t2) ALORS
*longueurLinaire longLigne
*longueurCirculaire longueur-longLigne
RETOURNER longueur
FINSI
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
123
Chapitre 3

Structures squentielles complexes
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
Corrigs des problmes
while (l->succ != NULL && l->succ != head)
{
/// iteration : 1er passage, l pointe sur le second lment
l = l->succ;
length++;
/// sil existe au moins un suivant,
/// et si alors, ce troisime lment est la tte
/// cest quon a une circulaire (en loccurrence, le triangle)
if (l->succ != NULL) if (l->succ == head)
{
*lineLength = 0;
*loopLength = length;
return length;
}
/// sinon on remet le pointeur h zro,
/// i.e., on le refait pointer sur la tte
list h = head;
lineL = 0; /// on reprend le dcompte
/// tant que h na pas rattrap l et quil reste itrable..
while ((h != l) && h->succ != NULL)
{
h = h->succ; // .. itrons le donc
lineL++;
/// si l est itrable et si alors son suivant EST h :
/// il y a boucle !!
if (l->succ != NULL) if (l->succ == h)
{
*lineLength = lineL;
*loopLength = length - lineL;
return length;
}
}
}
/// si on arrive l, cest une linaire classique terminant en NULL
*lineLength = length;
*loopLength = 0;
return length;
}

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
125
4
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 spciques
de linformatique : par exemple, cest sous forme darbre que sont organiss les chiers 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 dnitions des carac-
tristiques des arbres, aussi bien que les algorithmes qui manipulent des arbres scrivent trs
naturellement de manire rcursive.
4.1 ARBRES BINAIRES
Examinons tout dabord quelques exemples simples reprsents par des arbres binaires :
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 nale 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
Chapitre 4

Structures arborescentes
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 Dnition
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 ls gauche (respectivement ls 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 ls gauche (respectivement ls droit).
Si un nud n
i
a pour ls gauche (respectivement droit) un nud n
j
, on dit que n
i
est le pre de
n
j
(chaque nud na quun seul pre).
Deux nuds qui ont le mme pre sont dits frres.
Le nud n
i
est un ascendant ou un anctre du nud n
j
si, et seulement si, n
i
est le pre de n
j
,
ou un ascendant du pre de n
j
; n
i
est un descendant de n
j
si, et seulement si n
i
est ls de n
j
, ou
n
i
est un descendant dun ls de n
j
.
Tous les nuds dun arbre binaire ont au plus deux ls :
un nud qui a deux ls est appel nud interne ou point double
un nud sans ls 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 dnition rcursive des arbres binaires. Elle peut
tre ralise en allouant la mmoire soit de faon chane soit de faon contigu.
128
4.1. Arbres binaires
La reprsentation classique dun arbre est dynamique.
chaque nud on associe deux pointeurs, lun vers le sous-arbre gauche, lautre vers le sous-
arbre 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
typedef struct arbre {
T info;
struct arbre *sag, *sad;
} arbre;
typedef arbre *ptr_arbre;
Si a est un pointeur sur la racine de larbre, alors a = NULL correspond un arbre vide ; a->sag
pointe sur le ls gauche de a ; a->sad pointe sur le ls 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 Parcours en profondeur ( laide dune pile)
Parcours en largeur dabord Examiner tout un niveau (profondeur hirarchique) passant au
niveau du dessous tant quil en reste.
Problme : pas de lien entre ls. Cela doit tre trait itrativement.
129
Chapitre 4

Structures arborescentes
Figure 4.5 Parcours en largeur ( laide dune le)
Parcours en profondeur dabord
Pr-ordre
Principe
Si arbre non vide alors :
traiter la racine
parcourir en Pr-ordre le sag
parcourir en Pr-ordre le sad
Algorithme en pseudo C
Donne : ptr_arbre p
FONCTION prordre(a : ptr_arbre)
DEBUT
SI a = NULL ALORS
afficher(ainfo)
prordre(asag)
prordre(asad)
FINSI
FIN
Exemple
Figure 4.6 Parcours en prordre
On ache : a c m u t b y o
130
4.1. Arbres binaires
Il ny a pas de priorit sur le parcours des sous-arbres. On pourrait commencer par traiter le sad
avant le sag.
Ordre
Principe
SI arbre est non vide alors :
parcourir le sag
traiter la racine
parcourir le sad
Algorithme
Donne : ptr_arbre a
FONCTION ordre(a : ptr_arbre)
DEBUT
SI a = NULL ALORS
ordre(asag)
afficher(ainfo)
ordre(asad)
FINSI
FIN
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 ache : u m c t a y o b
131
Chapitre 4

Structures arborescentes
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
FONCTION postordre(a : ptr_arbre)
DEBUT
SI a = NULL ALORS
postordre(asag)
postordre(asad)
afficher(ainfo)
FINSI
FIN
Figure 4.8 Parcours en postordre
On ache : u m t c o y b a
4.1.4 Arbres binaires de recherche (ABOH = Arbres Binaires Ordonns
Horizontalement)
Dnitions et algorithmes de manipulation
ABOH = Arbre Binaire Ordonn Horizontalement Il est tel que pour tout nud de larbre, les
lments de son sag lui sont infrieurs, de son sad lui sont suprieurs ou gaux.
Exemple
On a bien larbre ordonn horizontalement.
Pour un parcours en ordre on obtient 1 5 6 10 15 30 35 50 60
La relation dordre est donc totale.
Mais cela dpend de la dnition, on peut mettre les plus grands dans le sad ou sag.
132
4.1. Arbres binaires
Ordre :
croissant : sag < racine <= sad
dcroissant : sad >= racine > sag
Figure 4.9 Arbre Binaire Ordonn Horizontalement (ABOH)
Algorithmes de manipulation de lABOH
Ordre Donne les lments en ordre total croissant ou dcroissant selon la dnition choisie pour
ABOH et la priorit de traitement du sag par rapport au sad.
Recherche dun lment dans un ABOH
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.
Principe
SI larbre est vide ALORS
fin et chec
SINON
SI llment cherch = lment point ALORS
fin et russite
SINON
SI llment cherch < lment point ALORS
rechercher llment dans le sag
SINON
rechercher llment dans le sad

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
133
Chapitre 4

Structures arborescentes
Algorithme
Donne : ptr_arbre a, T x
Rsultat de type boolen
FONCTION recherche(x : T, a : ptr_arbre) : boolen
VAR ok : boolen
DEBUT
SI a = NULL ALORS
ok faux
SINON
SI ainfo = x ALORS
ok vrai
FINSI
SINON
SI ainfo > x ALORS
recherche(x, asag, ok)
SINON
recherche(x, asad, ok)
FINSI
FINSI
RETOURNER ok
FIN
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 n
ime
occurrence de llment dans larbre, il faut utiliser un compteur.
Ajout dun lment dans un ABOH
Principe
Placer llment dans larbre en conservant lordre
et faisant le moins de rorganisation possible.
Un ajout dlment dans un ABOH se fait systmatiquement aux feuilles :
SI arbre est vide ALORS cration et ajout
SI non vide ALORS trouver la feuille
Trouver la feuille : parcourir larbre et rechercher la position de llment cest--dire comparer
llment ajouter la racine.
SI racine > lment ajouter ALORS
ajout dans le sag, donc retour en 1) avec le sag.
SI racine lment ajouter ALORS
ajout dans le sad, donc retour en 1) avec le sad
134
4.1. Arbres binaires
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

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
135
Chapitre 4

Structures arborescentes
Recherche dichotomique de la position.
Aucune rorganisation produire on se contente dajouter un lment.
En statique, ajouter un lment, cest ajouter un lment dans le tableau.
Algorithme (en rcursif)
Donne : T x
Donne modie : ptr_arbre *aa
FONCTION ajout(x : T, *aa : arbre)
DEBUT
SI *aa = NULL ALORS
reserver(*aa)
*aainfo x
*aasag NULL
*aasad NULL
SINON
SI *aainfo x ALORS
ajout(x, &(*aasad))
SINON
ajout(x, &(*aasag))
FINSI
FINSI
FIN
*aa est en donne modie, 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 ls
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 dun lment dans un ABOH
Llment est une feuille
Suppression simple :
libration mmoire
mise jour du pointeur concern dans le pre
Figure 4.14
136
4.1. Arbres binaires
Llment est le pre dun seul sous-arbre
Mise jour du pointeur concern dans le pre de llment supprim
avec ladresse du sous-arbre de llment supprim.
Figure 4.15
Llment deux sous-arbres
Figure 4.16 On veut supprimer le 8
Rechercher le plus grand du sag (ou le plus petit du sad)
Recopier sa valeur la place de llment supprimer
Supprimer le plus grand du sag (ou le plus petit du sad)
par la mthode 1 ou 2 (feuille ou un seul sous-arbre)
Principe
Si larbre est vide, retourner faux
Si linformation est plus petite llment, retour en 1) avec le sad
Si linformation est plus grande llment, retour en 1) avec le sag
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
137
Chapitre 4

Structures arborescentes
Si info = lment (on a trouv llment supprimer)
Ni sag, ni sad libration et retour avec vrai
sad et non sag ou sag et non sad mise jour du pre,
libration et retour avec vrai
sag et sad recherche du plus petit lment dans le sad
remplacement dlment par le plus petit
suppression du plus petit avec a) ou b)
retour avec vrai
Algorithmes sur lquilibre des arbres binaires
Dsquilibre possible dun ABOH.
Figure 4.17 Arbre totalement dsquilibr
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.
Dnition 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.
|n
g
n
d
| 1
138
4.1. Arbres binaires
Exemples
Figure 4.18 Arbres parfaitement quilibrs
Figure 4.19 Arbre imparfaitement quilibr
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.
|h
g
h
d
| 1
Les trois arbres de lexemple prcdent sont partiellement quilibrs.
Il existe des procds pour viter le dsquilibre darbres : par exemple, les arbres AVL.
Principe
SI larbre est vide retourner 0.
SINON compter le nombre de nuds du sag
compter le nombre de nuds du sad
retourner 1 + nsag + nsad
139
Chapitre 4

Structures arborescentes
Figure 4.20
Balance :
-1 : hsag = hsad + 1
0 : hsad=hsag
+1 : hasd = hsag + 1
Algorithme
Donne ptr_arbre a
Rsultat de type entier
FONCTION compter(a : ptr_arbre) : entier
VAR n :entier
DEBUT
SI a = NULL ALORS
n 0
SINON
n 1 + compter(asag) + compter(asad)
FINSI
RETOURNER n
FIN
Hauteur
SI larbre est vide retourner 0.
SINON calculer la hauteur du sag
calculer la hauteur du sad
SI hg > hd alors retourner 1 + hg
SINON retourner 1 + hd
Algorithme
Donne ptr_arbre a
Rsultat de type entier
FONCTION hauteur(a : ptr_arbre) : entier
VAR n :entier
DEBUT
SI a = NULL ALORS
n 0
140
4.1. Arbres binaires
SINON
n 1 + maximum(compter(asag), compter(asad))
FINSI
RETOURNER n
FIN
quilibre parfait
- SI larbre est vide, il est parfaitement quilibr
- SINON compter le nombre de nuds du sag
compter le nombre de nuds du sad
SI |ng -- nd| > 1 retourner faux
SINON vrifier lquilibre parfait du sag
SI oui vrifier lquilibre parfait du sad
SI oui retourner vrai
SINON retourner faux
SINON retourner faux
Algorithme
Donne ptr_arbre a
Rsultat de type entier
FONCTION quilibre_parfait(a : ptr_arbre) : boolen
VAR ok : boolen ; cptgauche, cptdroit : entier
DEBUT
SI a = NULL ALORS
ok vrai
SINON
cptgauche compter(asag)
cptdroit compter(asad)
SI |cptgauche -- cptdroit| > 1 ALORS
ok faux
SINON
SI quilibre_parfait(asag) ET quilibre_parfait(asad) ALORS
ok vrai
SINON
ok faux
FINSI
FINSI
FINSI
RETOURNER ok
FIN

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
141
Chapitre 4

Structures arborescentes
NONCS DES EXERCICES ET DES PROBLMES
EXERCICES
Exercice 4.1 Traiter un arbre en post-ordre
*
a) crire un algorithme rcursif de traitement en post-ordre dun arbre binaire.
b) Drouler lalgorithme sur lexemple suivant :
Figure 4.21
Exercice 4.2 Afcher le contenu des feuilles dun arbre
**
crire un algorithme permettant dafcher 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 itratif de recherche dun lment dans un ABOH.
Exercice 4.4 valuer le caractre ABOH dun AB
***
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 de calcul de la hauteur dun ABOH.
Exercice 4.6 valuer lquilibre dun AB
***
crire un algorithme permettant de savoir si un arbre binaire est partiellement quilibr.
Exercice 4.7 Parcourir un AB en largeur et en profondeur
***
crire deux algorithmes, respectivement de parcours en largeur dabord et en profondeur dabord,
pour afcher le contenu dun arbre binaire.
Exercice 4.8 Effectuer un calcul complexe sur un arbre
****
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
Exercice 4.9 Extraire une liste dlments dun arbre
****
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 ncessai-
rement 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 n
ime
dun binme :
(x + y)
n
=
n

k=0
C
k
n
x
nk
y
k
o C
k
n
, le coefcient binomial , alternativement dnot par
_
n
k
_
, se calcule par la formule :
C
k
n
=
n!
k! (n k)!
= C
nk
n
En combinatoire, C
k
n
dcompte le nombre de combinaisons de k lments parmi n.
Le triangle de Pascal, ou triangle arithmtique Chinois date du XIII
e
sicle et na donc pas t
invent par notre Blaise national.
Vous avez certainement dj rencontr cet objet mathmatique qui exploite une mthode de construc-
tion par rcurrence pour produire un arrangement gomtrique arborescent des coefcients bino-
miaux.
En effet, il est trivial de constater que : C
k
n
= C
k
n1
+ C
k1
n1
avec pour chaque ligne :
C
0
n
=
n!
0!n!
= C
n
n
= 1
et en particulier pour la premire :
C
0
0
=
0!
0!0!
= C
0
0
= 1
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
C
0
0
, le ls gauche de la racine C
0
1
, le ls droit de la racine C
1
1
, puis, ds la profondeur n = 2, dont
les ls gauche et droit du ls le plus gauche de profondeur n 1 (contenant C
0
n1
) contiennent
respectivement les valeursC
0
n
et C
1
n
, et dont tous les nuds situs droite de ces deux ls (contenant
C
k>1
n
) soient les ls droits de leur pre de niveau n 1 (contenant C
k1>0
n1
).
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
143
Chapitre 4

Structures arborescentes
Figure 4.22 Triangle de Yang Hui Source :
http://en.wikipedia.org/wiki/File:Yanghui_triangle.gif
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);
Problme 4.2 Interlude Syracusien
*****
Une suite de Syracuse de terme initial un entier N est dnie par :
s
N
0
= N
n N :
_
_
_
pair
_
s
N
n

s
n+1
=
s
N
n
2
impair
_
s
N
n

s
n+1
= 3s
N
n
+ 1
144
Problmes
Il semblerait que quel que soit N, la suite s
N
converge vers 1, i.e. :
N N : q N/s
N
q
= 1
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 suft 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 ls droit 2e, et ventuellement un ls 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.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
145
Chapitre 4

Structures arborescentes
crire une fonction rcursive qui construit larbre de Syracuse partir dune feuille donne conte-
nant la valeur e et dvelopp jusqu la profondeur p. Si un ls gauche (e 1)/3 vaut 0 ou 1 une
tape quelconque de la rcursion (i.e. e vaut 1 ou 4), ce ls gauche nest pas cr.
Corrigs des exercices et des problmes
EN PRAMBULE
Quelques remarques utiles
Les structures arborescentes, bien que plus complexes que les structures squentielles, conduisent
pardoxalement des algorithmes naturellement rcursifs et donc plus compacts .
Implantation en C dune structure darbre binaire
Pour la ralisation en C de tous les algorithmes spcis ci-dessous, on dnit la structure darbre
binaire suivante dont on prcisera au cas pas cas, le type <element> (prototype) :
Pseudo code formel
STRUCTURE treeNode
content : <lment>
*sag, *sad : treeNode
TYPE *tree : treeNode
TYPE *ptree : tree
Implantation en C
typedef struct treeNode
{
<lment> content;
struct treeNode *sag, *sad;
} treeNode;
typedef treeNode *tree;
typedef tree *ptree;
Qelques fonctions utilitaires facultatives pour amliorer la lisibilit algorithmique de vos impl-
mentations :
tree newSingletonTree(unsigned content)
{
tree t = malloc(sizeof(treeNode)); // allocation mmoire
t->content = content; // affectation du contenu
146
Corrigs des exercices
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);
}
La structure de liste simplement chane du second chapitre, et en particulier la fonction concat
de lexercice 2.2 sont rgulirement rutilises.
CORRIGS DES EXERCICES
Exercice 4.1 traiter un arbre en post-ordre
tude
Il sagit dune application directe des rappels de cours.
Pour rester gnrique, nous interprtons lnonc en retournant une liste chane des lments
de larbre ordonns en post-ordre. Cette liste peut ensuite faire lobjet de tout traitement itratif
comme par exemple pour en afcher le contenu.
Le rsultat produit sur lexemple donn est : 1, 2, 8, 12, 10, 5.
Spcication de lalgorithme
FONCTION getPostorderRouteList(t : tree) : list
VAR thisNode, sagldpl, sadldpl : list
DEBUT
// prconditions et prtraitements standards des cas aux limites
SI (isEmptyTree(t)) ALORS
RETOURNER emptyList()
FINSI
thisNode newSingletonList(tcontent)
SI (isSingletonTree(t)) ALORS
RETOURNER thisNode
FINSI
sagldpl getPostorderRouteList(tsag)
sadldpl getPostorderRouteList(tsad)
RETOURNER *concat(&sagldpl, concat(&sadldpl, &thisNode))
FIN
Exercice 4.2 afcher le contenu des feuilles dun arbre
tude
dfaut de prescription contraire, nous laborons un algorithme rcursif.
Le critre caractristique dun nud feuille est quil na ni ls droit, ni ls gauche.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
147
Chapitre 4

Structures arborescentes
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 afche laide dune fonction dimpression de
liste.
Spcication de lalgorithme
FONCTION getLeaves(t : tree) : list
VAR sagldpl, sadldpl : list
DEBUT
// prconditions et prtraitements standards des cas aux limites
SI (isEmptyTree(t)) ALORS
RETOURNER emptyList()
FINSI
SI (isSingletonTree(t)) ALORS
RETOURNER newSingletonList(tcontent)
FINSI
// sinon :
sagldpl getLeaves(tsag)
sadldpl getLeaves(tsad)
RETOURNER *concat(&sagldpl, &sadldpl)
FIN
Exercice 4.3 rechercher itrativement un lment dans un ABOH
Spcication de lalgorithme
FONCTION containsElement(t : tree, k : entier) : boolen
DEBUT
TANTQUE (t = NULL) FAIRE
SI (tcontent = k) ALORS
RETOURNER vrai
FINSI
SI (tcontent > k) ALORS
t tsad
SINON
t tsag
FINSI
FAIT
RETOURNER faux
FIN
Exercice 4.4 valuer le caractre ABOH dun AB
tude
En terminologie courante, on appelle un ABOH un ABR (Arbre binaire de recherche), en anglais,
un BST (Binary Search Tree).
148
Corrigs des exercices
Lerreur frquente consiste ne vrier 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 ns utiles.
Spcication de lalgorithme
FONCTION isPseudoBinarySearchTree(t : tree ) : boolen
{
SI (isEmptyTree(t)) ALORS
RETOURNER vrai
FINSI
SI (isSingletonTree(t)) ALORS
RETOURNER vrai
FINSI
SI (isEmptyTree(tsag) ET isEmptyTree(tsad)) ALORS
RETOURNER vrai
FINSI
SI (isEmptyTree(tsad)) ALORS
RETOURNER (tsagcontent < tcontent) ET
isPseudoBinarySearchTree(tsag)
FINSI
SI (isEmptyTree(tsag)) ALORS
RETOURNER (tsadcontent > tcontent) ET
isPseudoBinarySearchTree(tsad)
FINSI
RETOURNER
(tsagcontent < tcontent) ET
isPseudoBinarySearchTree(tsag) ET
(tsadcontent > tcontent) ET
isPseudoBinarySearchTree(tsad)
}
FONCTION isBinarySearchTree(t : tree) : boolen
DEBUT
SI (isEmptyTree(t)) ALORS
RETOURNER vrai
FINSI
SI (isSingletonTree(t)) ALORS
RETOURNER vrai
FINSI
SI (isEmptyTree(tsag) ET isEmptyTree(tsad)) ALORS
RETOURNER vrai
FINSI
SI (isEmptyTree(tsad)) ALORS
RETOURNER (maxOfTree(tsag) < tcontent) ET

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
149
Chapitre 4

Structures arborescentes
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
Corrigs des exercices
SI (sadMax > max) ALORS
max sadMax
FINSI
FINSI
RETOURNER max
FIN
Exercice 4.5 mesurer la hauteur dun ABOH
Spcication de lalgorithme
FONCTION getDepth(t : tree) : entier
VAR sagDepth, sadDepth, maxDepth : entier
DEBUT
SI (isEmptyTree(t)) ALORS RETOURNER 0
SINON
sagDepth getDepth(tsag)
sadDepth getDepth(tsad)
SI sagDepth > sadDepth ALORS
maxDepth sagDpeth
SINON
maxDepth sadDepth
FINSI
RETOURNER 1 + maxDepth
FINSI
FIN
Exercice 4.6 valuer lquilibre dun AB
tude
Lquilibre partiel dun arbre binaire est dtermin sur la base du diffrentiel des mesures des
hauteurs des ls 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.
Spcication 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
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
151
Chapitre 4

Structures arborescentes
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
Corrigs des exercices
Exercice 4.7 parcourir un AB en largeur et en profondeur
tude
Deux utilisations classiques de la le et de la pile sont respectivement le parcours en largeur et le
parcours en pronfondeur dun graphe, dont larbre est un cas particulier.
Ces deux algorithmes vous prparent ceux peine plus compliqus, pour le parcours itratif des
graphes gnraux
Spcication de lalgorithme
FONCTION depthWalkerPrinter(t : tree)
VAR curr : tree ; *s : lStack
DEBUT
SI (t = NULL) ALORS
RETOURNER
FINSI
s newEmptyStack()
sPush(t, s)
AFFICHER("dedpthWalkerPrinter : ")
TANTQUE ( isEmptyStack(s)) FAIRE
sPop(&curr, s)
AFFICHER(currcontent)
SI (tsag = NULL) ALORS
sPush(tsag, s)
FINSI
SI (tsad = NULL) ALORS
sPush(tsag, s)
FINSI
FAIT
FIN
FONCTION widthWalkerPrinter(t : tree)
VAR curr : tree; *q : lQueue
DEBUT
SI (t = NULL) ALORS
RETOURNER
FINSI
q newEmptyQueue()
qPush(t, q)
AFFICHER("widthWalkerPrinter : ")
TANTQUE ( isEmptyQueue(q)) FAIRE
qPop(&curr, q)
AFFICHER(currcontent)
SI (tsag = NULL) ALORS
qPush(tsag, q)

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
153
Chapitre 4

Structures arborescentes
FINSI
SI (tsad = NULL) ALORS
qPush(tsag, q)
FINSI
FAIT
FIN
Exercice 4.8 effectuer un calcul complexe sur un arbre
tude
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.
Spcication 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
Corrigs des exercices
*/
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
void signedAveragesMain(tree t, double *ppo_av, double *pne_av)
{
int po_sum, po_count, ne_sum, ne_count;
signedAverages(t, &po_sum, &po_count, &ne_sum, &ne_count);
*ppo_av = po_count == 0 ? -1 : (double) po_sum / (double) po_count;
*pne_av = ne_count == 0 ? -1 : (double) ne_sum / (double) ne_count;
}

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
155
Chapitre 4

Structures arborescentes
Fonction secondaire rcursive
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
Corrigs des exercices
{
/// 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);
}
Exercice 4.9 extraire une liste dlments dun arbre
tude
Analyse
Il sagit dextraire dun arbre tous les ls 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 vrication
de la nullit du modulo de lun par lautre est sufsante pour dterminer cette proprit.
Peu importe le type de parcours choisi (pr, in, ou post xe), lalgorithme est naturellement
rcursif et bas sur la fusion de proche en proche des listes denfants premiers de chaque sous-
arbre : 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
nalement fusionnes en une seule et unique liste au moment du dpilement nal de lappel initial
de la fonction.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
157
Chapitre 4

Structures arborescentes
Mthodes de construction
Un arbre vident dont tous les ls 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 ls gauche, 25 celle de ls droit, puis 4 prend la place de ls gauche de
9 et de 25, et 25 celle de ls droit de 9 et 9 celle de ls 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.
Spcication 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 = NULL) ALORS
sagprimechildren getPrimeChildren(tsag)
SI (gcd(tcontent, tsagcontent) = 1) ALORS
primechild newSingletonList(tsagcontent)
SI (primechild = NULL) ALORS
primechildsucc sagprimechildren
sagprimechildren primechild
FINSI
FINSI
FINSI
SI (tsad = NULL) ALORS
sadprimechildren getPrimeChildren(tsad)
SI (gcd(tcontent, tsadcontent) = 1) ALORS
primechild newSingletonList(tsadcontent)
SI (primechild = NULL) ALORS
primechildsucc sadprimechildren
sadprimechildren primechild
FINSI
158
Corrigs des exercices
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;
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
159
Chapitre 4

Structures arborescentes
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
Corrigs des exercices
primechildren = getPrimeChildren(p);
printListGraph(primechildren, "primechildren (121)");
p = primeChildrenTestTree(3, 2, 2, 2);
simplePrintTreeGraph(p, "ptest (222)");
primechildren = getPrimeChildren(p);
printListGraph(primechildren, "primechildren (222)");
}
Exercice 4.10 produire des coupes transversales dun arbre
Spcication de lalgorithme
/**
* 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
Chapitre 4

Structures arborescentes
CORRIGS DES PROBLMES
Problme 4.1 le triangle chinois de Pascal
Analyse
Traduction du chinois vers larabe/latin
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 liations croises en liations simples en supprimant les relations parent-enfant
sur la gauche, sauf pour le ct droit de larbre.
Figure 4.25
162
Corrigs des problmes
Stratgie de rsolution
Utilisation de la formule de rcurrence de Pascal
Le procd de construction dun arbre est naturellement rcursif et en particulier celui-ci.
La formule de Pascal est une rcurrence quil convient dexploiter pour viter de recalculer pour
chaque nud de larbre, une expression qui comprend trois factorielles dont on sait le cot prohibi-
tif.
Ne pas exploiter cette rcurrence est une vritable erreur professionnelle pour un informaticien...
Cest ce qui a justi un bonus supplmentaire pour les quelques-unes et quelques-uns qui y ont
pens.
Approche rcursive ou itrative ?
Comme on supprime les liations gauches, il faut quun parent gauche puisse passer la valeur
du parent droit , non reli, tout nouveau ls 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 gure 4.26 (la mme sous une perspective peine diffrente) devrait vous aider vous le
reprsenter.
Figure 4.26

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
163
Chapitre 4

Structures arborescentes
Spcication de lalgorithme
FONCTION buildPascalTriangle(*pt : tree, n : entier)
VAR i, j : entier; root, curr, currline, prev, prevline : tree
DEBUT
*pt newSingletonTree(1)
root *pt
SI (n = 0) ALORS
RETOURNER
FINSI
j n + 1
currLine root
curr currLine
TANTQUE (j > 0) FAIRE
currsad newSingletonTree(1)
curr currsad
j j-1
FAIT
prevLine root
prev prevLine
i n
TANTQUE (i > 0) FAIRE
prevLinesag newSingletonTree(1)
currLine prevLinesag
curr currLine
j i
TANTQUE (j > 0) FAIRE
prev prevsad
currsad newSingletonTree(currcontent + prevcontent)
curr currsad
j j-1
FAIT
prevLine currLine
prev prevLine
i i-1
FAIT
FIN
Ralisation en C
void buildPascalTriangle(tree* pt, unsigned n)
{
tree root = *pt = newSingletonTree(1);
if (n == 0) return;
unsigned j = n + 1; /// itrateur de colonnes (indices de 0 n)
164
Corrigs des problmes
tree currLine = root; /// ligne de sad->sad->... courante
tree curr = currLine; /// fils sad en cration courant
/// on commence par crer toute la branche droite de larbre
while (j-- > 0)
{
curr->sad = newSingletonTree(1);
curr = curr->sad;
}
/// le travail seffectue ensuite ligne aprs ligne,
/// avec une longueur qui se rduit chaque tape
tree prevLine = root; /// ligne de sad->sad->... prcdente
tree prev = prevLine;
unsigned i = n;
while (i-- > 0)
{
/// passage la ligne suivante : dabord crer sa tte
currLine = prevLine->sag = newSingletonTree(1);
curr = currLine;
unsigned j = i;
/// on co-itre la ligne prcdente avec la ligne en cours
/// de cration pour profiter de la formule de Pascal
while (j-- > 0)
{
prev = prev->sad;
curr->sad = newSingletonTree(curr->content + prev->content);
/// -> cl de performance de lalgo
curr = curr->sad;
}
prevLine = currLine;
prev = prevLine;
}
}
Complexit algorithmique compare
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 :
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
165
Chapitre 4

Structures arborescentes
c
_
C
k
n
_
= c
_
n!
k! (n k)!
_
= c (n!) + c (k!) + c ((n k)!) + 4 = 2 (2n 1)
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

i =1
2 (2i 1) (i + 1)
avec la mauvaise mthode, et un cot de
n

i =1
2 (i + 1) = (n + 1)(n + 2), avec la bonne.
Problme 4.2 interlude Syracusien
Fonction de retour du prdcesseur
Le principe
Il sagit de vrier dans un premier temps que (n 1) est divisible par 3. Cela se vrie facilement
laide du test (n - 1) % 3 = 0, soit m % 3 = 0 avec m = n - 1.
Mais ce nest pas sufsant, 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 vrier laide de (m/3) % 2 = 1 ou = 0, mais si le rsultat invalide
cette imparit, une division aura t effectue pour rien.
Il suft 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 vrie par m % 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
Corrigs des problmes
SI ((m % 3) = 0) ALORS
RETOURNER 0
FINSI
SI ((m % 6) = 0) ALORS
RETOURNER 0
FINSI
RETOURNER m / 3
FIN
Optimisation
Une solution plus efcace peut tre trouve moyennant un petit raisonnement darithmtique
modulaire :
n N : p N

: n = 3p + 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
Cette ralisation quivalente conomise de nombreuses oprations lmentaires (soustractions
inutiles, modulos intermdiaires, ngations logiques) rapporter au fait que lappel de cette fonction
utilitaire est systmatique au fur et mesure de la rcursion.
Fonction rcursive de construction de larbre
/**
* Retourne larbre de Syracuse (tte valant 1)
* jusqu la profondeur maxDepth
*/
FONCTION syracuseTreeGrowth(pt : ptree, maxDepth : entier)
VAR n, prec_inf_n : entier; t : tree
DEBUT
SI (maxDepth = 0) ALORS
RETOURNER
FINSI
t *pt
SI (t = NULL) ALORS
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
167
Chapitre 4

Structures arborescentes
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
5
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) ni).
Un modle plus simple mais fort utile est celui dun automate ni. Un automate ni 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 ni car il possde un nombre ni 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 ni forme un graphe orient tiquet, dont les tats sont les sommets et les transi-
tions les arcs tiquets.
Les automates nis fournissent un outil de construction dalgorithmes particulirement simple.
Il existe plusieurs types de machines tats nis. 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 nis peuvent caractriser des langages (cest--dire des ensembles de mots) nis
(le cas standard), des langages de mots innis (automates de Rabin, automates de Bchi), ou encore
divers types darbres (automates darbres).
Les machines tats nis se rencontrent galement dans les circuits digitaux, o lentre, ltat
et le rsultat sont des vecteurs de bits de taille xe (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
5.2 QUELQUES DFINITIONS
Pour dnir un automate ni, on a besoin des lments suivants :
Un alphabet A qui est un ensemble ni dobjets quon appelle lettres ou caractres mais
qui peuvent, selon le cas, tre de nimporte quel genre (instructions machine, tat civil dune
personne, type dobjet gomtrique etc.)
Des mots sont des squences ordonnes de caractres de lalphabet. Dans nos applications, on
aura affaire qu des mots de longueur nie, mais il nest pas interdit dutiliser des mots de
longueur innie (automates de Rabin, automates de Bchi). La longueur dun mot est le nombre
de lettres le constituant.
Exemple : w = abacda mot de longueur six sur lalphabet latin (ou bien sur lalphabet
{a, b, c, d}).
Un mot vide est not par ou par 1. Cest le seul mot de longueur nulle.
On note A

lensemble de tous les mots sur lalphabet A, y compris le mot vide.


Un automate ni sur lalphabet A est donn par un quadruplet (Q, I , T, E) o :
Q est un ensemble ni dtats de lautomate ;
I Q est un ensemble dtats initiaux ;
T Qest un ensemble dtats terminaux ;
E Q
_
A {}
_
Q est un ensemble de triplets ( p.a.q) appels les ches ou les
transitions de lautomate.
Remarque
Nous allons temporairement oublier la notion du mot vide, jusqu lintroduction explicite dauto-
mates asynchrones. Dans tous les exemples que nous allons traiter jusque ce point-l, on pourra
donc dnir lensemble E comme faisant partie de Q A Q.
5.3 LINTERPRTATION INTUITIVE
chaque instant lautomate se trouve dans lun des tats p de lensemble dtats Q. Il lit alors
lune des lettres a de lalphabet A (la lettre suivant du mot qui vient lentre) et passe dans un
tat q tel que ( p.a.q) soit une che.
Exemple
Figure 5.1
A = {a, b}; Q = {0,1, 2,3, 4}; I = {0}; T = {4}
170
5.3. Linterprtation intuitive
La lecture du mot w se termine dans ltat 4 ssi w se termine par abaa.
Le mme automate peut tre reprsent par une table de transitions :
Tableau 5.1
tat
tat rsultant
en lisant a en lisant b
(entre) 0 1 0
1 1 2
2 3 0
3 4 2
(sortie) 4 1 2
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 suxe du mot dj lu qui est en
mme temps un prxe 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) q
i
tels que la transition
( p.a.q
i
) existe, il passe aux tats q
i
, et de suite. (Sil y a plus dun tat q
i
, 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 n du mot. Si, la n de la lecture, lautomate
est dans un tat nal (accepteur), on dit quil accepte lentre ou quil la reconnat. Autrement, on
dit quil la rejette.
Notation
Pour un mot w A

on note p
w
q sil existe un chemin de ltat p ltat q obtenu en lisant w.
Dnitions formelles
a A, on a p
a
q ssi il existe une che ( p.a.q) E.
Ensuite, pour u A

et a A on a p
ua
q ssi r Q tq p
u
ret r
a
q.
Quand on a p
w
q on va dire quil existe un chemin de p q dtiquette w.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
171
Chapitre 5

Automates
On peut facilement voir (par rcurrence) que pour u, v A

on a :
p
uv
q ssi r Q tq p
u
r et r
v
q.
Un mot w A

est donc reconnu par lautomate ni sil existe un chemin i


uv
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 n de la lecture.
Le langage L reconnu par lautomate ni est lensemble de tous les mots reconnus.
Exemple dexercice type
Construire un automate ni reconnaissant les entiers crits en base deux et divisibles par trois.
Solution
Ajout dun 0 la n dun nombre binaire le multiplie par 2.
Ajout dun 1 la n dun nombre binaire le multiplie par 2 et lui ajoute 1.
Marquant les tats par le reste de la division entire par 3 :
Tableau 5.2
N N mod3 ajout dun 0 la n reste ajout dun 1 la n reste
3n 0 6n 0 6n + 1 = 3 2n + 1 1
3n + 1 1 6n + 2 = 3 2n + 2 2 6n + 3 = 3 (2n + 1) 0
3n +2 2 6n + 4 = 3 (2n + 1 ) + 1 1 6n + 5 = 3 (2n + 1) + 2 2
Notons les tats par le reste de la division entire par 3. On obtient :
Tableau 5.3
tat
tat rsultant
0 1
0 0 1
1 2 0
2 1 2
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
5.3. Linterprtation intuitive
5.3.1 Automates dterministes
On distingue des automates nis dterministes et non dterministes. Lautomate que nous avons
montr prcdemment est dterministe.
Voici lexemple dun automate non dterministe :
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 ni
quelconque, un autre automate qui est dterministe et qui lui est quivalent (reconnat le mme
langage).
Automate dterministe, dnition
Lautomate C = (Q, I , T, E) est dterministe ssi pour p Q et a A il existe au plus un tat
q Q tq ( p.a.q), et quil y ait un seul tat initial : I = {i }.
On peut donc caractriser un automate dterministe C par un quadruplet (Q, i , T, E) o i est
ltat initial.
Si la transition ( p.a.q) existe, on notera p.a = q. Si elle nexiste pas, on conviendra que p.ana
pas de valeur.
On peut facilement vrier par rcurrence sur la longueur des mots que pour un automate
dterministe C, pour p Q et mot u A

, il existe au plus un tat q Q tq p


u
q.
On notera alors p.u = qen convenant que p.u nest pas dni quand il nexiste pas de tel tat q;
et on aura pour deux mots u, v A

:
( p.u).v = p.(u.v)
Dterminisation
Soit C = (Q, I , T, E) un automate ni.
Notons P lensemble des parties de Q. Cest un ensemble ni puisque si Q a n lments, P en
a 2
n
.
Un automate dterministe D correspondant C prend :
comme ensemble dtats un sous-ensemble P de lensemble P des parties de Q,
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
173
Chapitre 5

Automates
comme unique tat initial lensemble I des tats initiaux de C,
comme ensemble dtats terminaux lensemble = {u P|u T = } des parties de Q qui
contiennent au moins un tat terminal de C,
comme ches lensemble des (u.a.v) o u P et v est lensemble de tous les tats q Q tels
quil existe un tat p dans u avec ( p.a.q) E.
Cet algorithme formel sexplique le plus facilement sur un exemple.
Exemple de dterminisation
Prenons lautomate non dterministe prcdent :
Figure 5.4
Dterminisation
Tableau 5.4
tat
tats rsultant
en lisant a en lisant b
(entre) (0) (0,1) (0)
(0,1) (0,1) (0,2)
(0,2) (0,1,3) (0)
(0,1,3) (0,1,4) (0,2)
(sortie) (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 gure un
nouvel tat (0,2). On procde ainsi tant quil y reste des tats non traits.
Les tats naux sont ceux qui contiennent un tat nal de lautomate initial. Dans notre cas, il
ny en a quun : (0,1,4).
174
5.3. Linterprtation intuitive
Figure 5.5 Automate dterminis
Voici lautomate dterminis :
La dmonstration montrant que et C reconnaissent le mme langage se fait par rcurrence.
Un automate dterministe peut tre ou ne pas tre complet.
Automate dterministe complet (dnition)
Pour u P et a A , v P tel que u.a = v.
Cest--dire que de chaque tat sortent des ches avec toutes les tiquettes possibles.
Lautomate D de lexemple prcdent est un automate complet.
Nimporte quel automate non dterministe est quivalent un automate dterministe et complet :
il suft, dans lautomate dterminis, dintroduire un tat poubelle sil nest pas dj complet.
Exemple
Figure 5.6
Dterminisation
Tableau 5.5
tat
tats rsultant
en lisant a en lisant b en lisant c
(entre) (0) (0,1) (0)
(0,1) (0,1) (0) (2)
(sortie) (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 lui-
mme :
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
175
Chapitre 5

Automates
Tableau 5.6
tat
tats rsultant
en lisant a en lisant b en lisant c
(entre) (0) (0,1) (0) P
(0,1) (0,1) (0) (2)
(sortie) (2) P P P
P P P P
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 ni D qui le
reconnat.
Si X est un langage reconnaissable sur lalphabet A, on peut le reconnatre avec un automate
dterministe et complet, car lautomate D gurant dans la dnition du langage reconnaissable est
toujours quivalent un automate dterministe et complet F.
crivant F = (Q, i , T, E), on a alors w X ssi i .w T et donc w X ssi i .w T.
176
5.3. Linterprtation intuitive
Le complment dun langage reconnaissable
Le complment dun langage reconnaissable (lensemble de mots sur le mme alphabet napparte-
nant 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 suft 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
tats rsultant
en lisant a en lisant b
(initial) (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).

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
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 sut 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 aaire 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
Il est bas sur la notion de partitionnement de lensemble dtats de lautomate.
Principe Tout dabord, si lautomate minimiser nest pas complet, il faut lui ajouter ltat
poubelle pour quil le devienne. Sinon on risque de faire une erreur grave, qui se manifeste facile-
ment au cas dun automate dterministe non complet dont tous les tats sont des tats terminaux
(essayez le voir vous-mmes aprs avoir compris lalgorithme de minimisation).
On spare tous les tats de lautomate dterministe initial en deux groupes : terminaux et
non-terminaux. Puis, on analyse la table des transitions en marquant vers quel groupe va chaque
transition. On repartitionne les groupes selon les patterns des transitions en terme de groupes. On
rpte ce processus en utilisant cette nouvelle partition, et on ritre jusqu ce quon arrive ne
plus pouvoir partitionner. Les groupes restants forment lautomate minimal. On appelle souvent les
itrations les tapes.
Description du processus de partitionnement itratif Soit un automate ni dterministe
complet D = (Q, i , T, E).
Rsultat obtenir : Un automate ni dterministe complet D

qui reconnat le mme langage


que D et qui a aussi peu dtats que possible.
Mthode Construire une partition initiale Q
0
de lensemble des tats avec deux groupes : les
tats terminaux T et les tats non-terminaux Q T.
178
5.3. Linterprtation intuitive
Procdure applicable la partition courante Q
i
, commencer par Q
0
:
POUR chaque groupe G de Q
i
FAIRE
dbut
partitionner G en sous-groupes de manire que deux tats e et t de G
soient dans le mme sous-groupe ssi pour tout symbole a A, les tats e
et t ont des transitions sur a vers des tats du mme groupe de Q
i
;
/* au pire, un tat formera un sous-groupe par lui-mme */
remplacer G dans par tous les sous-groupes ainsi forms ;
on obtient la partition de ltape suivant, Q
i +1
;
fin
Si Q
i +1
= Q
i
alors Q
f i nal
= Q
i
et continuer ltape 4.
Sinon, rpter ltape (2) avec Q
i +1
comme partition courante.
Choisir un tat dans chaque groupe de la partition Q
f i nal
en tant que reprsentant de ce groupe.
Les reprsentants seront les tats de lautomate ni dterministe rduit D

.
Soit e un tat reprsentatif, et supposons que dans D il y a une transition e
a
t .
Soit r le reprsentant du groupe de t (r peut tre gal t ).
Alors D

a une transition de e vers r sur a (e


a
r).
Ltat initial de D

est le reprsentant du groupe qui contient ltat initial i de D ; les tats


terminaux de D

sont des reprsentants des tats de T.


Pour tout groupe G de Q
f i nal
, soit G est entirement compos dtats de T, soit G nen contient
aucun.
Remarque
En ralit, il est parfois plus facile, au lieu de choisir un reprsentant, marquer les groupes naux
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 :
Si e
a
r , e A, r B o les groupes A, B Q
f i nal
, alors dans lautomate minimis on a A
a
B o
maintenant on considre A et B comme tats de lautomate minimis. Cette remarque deviendra
tout fait claire la n de lexemple suivant.
Exemple 1
Minimisons lautomate dterministe complet :
Figure 5.11

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
179
Chapitre 5

Automates
dcrit par le tableau de transitions suivant :
Tableau 5.8
tat
tats rsultant
en lisant a en lisant b
(initial) 0 1 2
1 1 3
2 1 2
3 2 4
(terminal) 4 1 2
Lunique tat terminal est ltat 4.
Donc, la partition initiale : Q
0
= {(0,1, 2,3), (4)}.
Notons les groupes non terminal et terminal : I = {(0,1, 2,3)} et I I = {(4)}.
On ne peut pas, videmment, essayer de sparer le groupe II qui consiste dj en un seul tat.
On regarde donc dans quel groupe tombent les transitions partir des tats du groupe I :
Tableau 5.9
tat
Groupes rsultant
en lisant a en lisant b
0 I I
1 I I
2 I I
3 I II
Donc, le groupe I se spare en deux, et on a Q
1
= {(0,1, 2), (3), (4)}.
Notons les groupes (en recyclant la notation) : I = {(0,1, 2)}, I I = {(3)}, I I I = {(4)}.
Maintenant essayons de sparer le groupe I en regardant o tombent les transitions partir de
ses tats dans les termes de la sparation Q
1
= {(0,1, 2), (3), (4)}.
Tableau 5.10
tat
Groupes rsultant
en lisant a en lisant b
0 I I
1 I II
2 I I
Donc, le groupe I se spare en deux, et on a Q
2
= {(0,2), (1), (3), (4)}.
Notons les groupes (en recyclant la notation) :
I = {(0,2)}, I I = {(1)}, I I I = {(3)}, I V = {(4)}.
Essayons de sparer le groupe I en regardant o tombent les transitions partir de ses tats
dans les termes de la sparation Q
2
= {(0,2), (1), (3), (4)}.
180
5.3. Linterprtation intuitive
Tableau 5.11
tat
Groupes rsultant
en lisant a en lisant b
0 II I
2 II I
Le groupe I ne se spare pas, Q
3
reste identique la sparation de ltape prcdent :
Q
3
= Q
2
= {(0,2), (1), (3), (4)}.
Fin de la procdure itrative de sparation.
Voici les itrations quon a eectues :
tape 1 : Q
courant
= {(0,1, 2,3), (4)}
Q
new
= {(0,1, 2), (3), (4)}
tape 2 : Q
courant
= {(0,1, 2), (3), (4)}
Q
new
= {(0,2), (1), (3), (4)}
tape 3 : Q
courant
= {(0,2), (1), (3), (4)}
Q
new
= {(0,2), (1), (3), (4)} = Q
f i nal
Passons aux reprsentants :
Tableau 5.12
Groupe Reprsentant
I 0 ou 2
II 1
III 3
IV 4
Il devient clair maintenant quon peut marquer les tats de lautomate minimis soit par un
reprsentant, soit par les chires 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

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
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 litw
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 nal (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}, T = {1,2}
182
5.3. Linterprtation intuitive
Q
0
= {(1,2), (3,4, 5,6)} = (I , I I }
Q
1
= ({(1,2).(3,6), (4,5)} = {I , I I , I I I } (en recyclant les chires romains)
Q
2
= Q
1
Voici lautomate minimis :
Figure 5.15
5.3.2 Automate asynchrone
Souvenons-nous que jusqu ce point nous avons vit le mot vide.
Un automate ni dans lequel certaines ches sont tiquetes par le mot vide ( -transitions )
sappelle automate asynchrone. Lensemble des ches vrie donc E Q
_
A {}
_
Q.
Proposition : pour tout automate asynchrone, il existe un automate ni ordinaire (c..d. sans
-transitions) qui reconnat le mme langage.
Comme tout automate non dterministe est quivalent un automate dterministe, il en suit que
tout automate asynchrone est quivalent un automate dterministe.
Il existe un algorithme de suppressions de -transitions.
Pour un automate asynchrone C = (Q, I , T, E) avec E Q
_
A {}
_
Q nous allons
construire un automate B = (Q, I , T, F) qui ne diffre de C que par lensemble de ses ches et
ce, suivant une rgle bien dtermine :
par dnition on a ( p.a.q) F sil existe un chemin
c : p
0

p
1

. . .

p
n
a
q
0

q
1

. . .

q
m
et p
0
= p q
m
= q
Figure 5.16
Le nombre de -transitions droite ou gauche de la transition p
n
a
q
0
peut tre nul (dans ce
cas p
n
= p
0
ou p
n
= p
0
).
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
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)
c) Maintenant on peut le dterminiser :
Tableau 5.13
tat
tats rsultant
en lisant a en lisant b en lisant c
1 1,2 2 3
1,2 1,2 2 3
2 2 3
3
Figure 5.19 (A3)
184
5.3. Linterprtation intuitive
ou bien, en ajoutant ltat poubelle :
Tableau 5.14
tat
tats rsultant
en lisant a en lisant b en lisant c
1 1,2 2 3
1,2 1,2 2 3
2 P 2 3
3 P P P
P P P P
Figure 5.20 (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 che 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.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
185
Chapitre 5

Automates
Tableau 5.15
tat
tats rsultant
en lisant a en lisant b en lisant c
1,2 1,2 2 3
2 2 3
3
Figure 5.22 (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 1,2 2 3
1,2 1,2 2 3
2 P 2 3
3 P P P
P P P P
Ici , on voit immdiatement que les tats 1 et 1,2 ne se spareront jamais !
Q
0
= {(1, (1,2), 2, P), (3)} = {I , (3)} (Utilisons une notation un peu plus intelligente pour ne
pas modier le sens des chires romains sans cesse)
Tableau 5.17
tat
Groupes rsultant
en lisant a en lisant b en lisant c
1 I I 3
1,2 I I 3
2 I I 3
P I I I
(5) se spare en formant un groupe part (cest toujours le cas)
Q
1
= {(1, (1,2), 2), (P), (3)} = {I , (P), (3)}
186
noncs des exercices
Tableau 5.18
tat
Groupes rsultant
en lisant a en lisant b en lisant c
1 I I 3
1,2 I I 3
2 P I 3
(2) se spare.
Q
2
= {(1, (1,2)), (2), (P), (3)} = {I , (2), (P), (3)}
Tableau 5.19
tat
Groupes rsultant
en lisant a en lisant b en lisant c
1 I 2 3
1,2 I 2 3
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).
NONCS DES EXERCICES
Exercice 5.1 Construire un automate ni dont les tats correspondent aux situations de famille
possibles dune personne (clibataire, mari, divorc, veuf) et dont les ches correspondent
aux changements de situation possible. tiqueter ces ches par M (mariage), D (divorce) et V
(veuvage).
Exercice 5.2 Construire des automates nis qui reconnaissent les langages suivants :
(a) Lensemble des mots sur lalphabet {0, 1} dont lavant dernier symbole est 0.
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
187
Chapitre 5

Automates
(b) Lensemble des mots sur lalphabet {0, 1} qui commencent et qui nissent par 0. (On
considre que le mot consistant en un seul 0 vrie cette condition).
(c) Lensemble des mots sur lalphabet {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +, -, .} qui reprsentent en
langage C des constantes numriques.
(d) Lensemble des mots sur lalphabet {0, 1} qui comportent au moins une fois le motif 10 et au
moins une fois le motif 01 (ces deux motifs ne sont pas obligs dtre spars lun de lautre
par exemple, la squence 010 comporte les deux motifs).
(e) {a
n
b
m
|n + m pair}
Exercice 5.3 Construire un automate ni reconnaissant les entiers crits en base deux divisibles
par cinq.
Exercice 5.4 Construire un automate ni reconnaissant les lignes contenant des commentaires
dun programme crit en langage C. La sortie de lautomate correspond la n du commentaire.
On prendra comme alphabet {/, , , a} o a reprsentera tous les caractres autres que ", * et /.
Tout commentaire commence par /* et nit par */.
Le commentaire peut contenir des / et des *, mais il ne peut pas contenir le motif */, sauf lintrieur
dune chane qui commence par " et nit par " sans contenir dautres ". En fait, toute chane de
caractres place entre les guillemets doubles, est dspcialise ; donc le nombre de guillemets
doubles doit tre pair et avant le commentaire, et lintrieur du commentaire.
La squence /*/ nest pas considre comme comportant et le motif /*, et le motif */.
Exemple dun commentaire reconnu par lautomate : /*commentaire "/*" toto"*/" **/
Exercice 5.5 Quel est le langage reconnu par lautomate suivant ? Quels tats de cet automate
sont inutiles ?
Figure 5.24
Exercice 5.6 Voici cinq automates nots A
n
(ils nont rien voir les uns avec les autres, ce nest
pas une squence !). Lalphabet A = {a, b} sauf pour A
4
o il est {0,1}. Pour chacun de ces
automates :
Dcrire L(A
n
) en langage ordinaire (sauf pour A
5
).
Caractriser A
n
(dire sil est accessible, coaccessible, mond...)
Calculer lautomate dterministe quivalent A
n
.
188
noncs des exercices
Figure 5.25 A1
Figure 5.26 A2
Figure 5.27 A3
Figure 5.28 A4
Figure 5.29 A5
(Ici, ne pas dcrire L(A
5
) avant que lautomate ne soit dterminis)
189
Chapitre 5

Automates
Exercice 5.7 Dterminiser lautomate suivant :
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 ni reconnaissant lensemble des mots sur lalphabet A =
{a,b} qui ne se terminent pas par abaab. Le dterminiser et minimiser.
Exercice 5.10 Montrer que les automates dterministes calculs lexercice 6 sont tous minimaux
sauf un. Lequel ?
Exercice 5.11 Dterminiser et minimiser les automates suivants :
a)
Figure 5.31
b)
Figure 5.32
190
Corrigs des exercices
Exercice 5.12 On dnit la famille dautomates suivants :

A
n
= (Q
n
, I , T, E), n 1
avec :
A = {a, b}
Q
n
= {0,1, ..., n 1}
I = T = {0}
Comme ches tiquetes par a lensemble de (q.a.((q + 1)mod n)) pour q : 0 q n 1
Comme ches tiquetes par b lensemble de (q.b.0) et (q.b.q) pour q : 1 q n 1
(attention : la premire ingalit commence par 0, et la seconde, par 1)
Dessiner

A
3
et

A
4
.
Puis montrer que le dterminis complet de

A
n
a toujours 2
n
tats.
Corrigs des exercices
Exercice 5.1
Figure 5.33 Cet automate est dterministe.
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 :

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
191
Chapitre 5

Automates
Figure 5.34 Cet automate nest pas dterministe.
Solution (b)
Lautomate le plus simple qui reconnat lensemble des mots sur lalphabet {0, 1} qui commencent
et qui nissent par 0, est le suivant :
Figure 5.35 Cet automate nest pas dterministe.
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 sui-
vant :
Figure 5.36 Cet automate est dterministe.
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
Corrigs des exercices
Figure 5.37 Cet automate est dterministe.
Solution (e)
Un des nombreux automates quivalents qui reconnat le langage {a
n
b
m
|n + m pair}, est le suivant :
Figure 5.38 Cet automate est dterministe.
Exercice 5.3 Ajout dun 0 la n dun nombre binaire le multiplie par 2.
Ajout dun 0 la n dun nombre binaire le multiplie par 2 et lui ajoute 1.
Tableau 5.20
N N mod 5 Ajout dun 0
la n de
lcriture binaire
donne N :
N mod 5 Ajout dun 1
la n de
lcriture binaire
donne N :
N mod 5
5n 0 10n 0 10n + 1 1
5n + 1 1 10n + 2 2 10n + 3 3
5n + 2 2 10n + 4 4 10n + 5 0
5n + 3 3 10n + 6 1 10n + 7 2
5n + 4 4 10n + 8 3 10n + 9 4
Cela se traduit par lautomate suivant :
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
193
Chapitre 5

Automates
Tableau 5.21
tat En lisant 0 En lisant 1
0 0 1
1 2 3
2 4 0
3 1 2
4 3 4
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
cet automate est le mme que 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
Corrigs des exercices
Exercice 5.6
Solution A
1
Cet automate lit tous les mots se terminant par bbb.
Il est mond.
Dterminisation
Tableau 5.22
tat a b
0 0 01
01 0 012
012 0 0123
0123 0 0123
Figure 5.42 A
1
Solution A
2
Cet automate lit tous les mots avec un b en 3
ime
position de la n.
Il est mond.
Dterminisation
Tableau 5.23
tat A b
Initial 0 0 01
01 02 012
012 023 0123
02 03 013
Terminal 0123 023 0123
Terminal 023 03 013
Terminal 03 0 01
Terminal 013 02 012

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
195
Chapitre 5

Automates
Figure 5.43
Solution A
3
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 a b
0 01 01
01 01 012
012 0123 012
0123 0123 0123
Figure 5.44
Solution A
4
Cet automate lit tous les mots consistant en des 0 ou en des 0 suivis dun seul 1, ou le mot 1.
196
Corrigs des exercices
Il est accessible mais non pas coaccessible.
Dterminisation
Tableau 5.25
tat 0 1
A AB B
AB ABC BC
ABC ABC BC
B C C
C C C
BC C C
On voit que C est ltat poubelle.
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 quA
4
:
Figure 5.46
et le dterminiser. En ce faisant on arrive un automate dterministe nettement plus simple que
tout lheure :
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
197
Chapitre 5

Automates
Tableau 5.26
tat 0 1
A AB B
AB AB B
B P P
P P P
Figure 5.47
Dans lexercice 10 on va voir que cet automate est en eet minimal.
Solution A
5
On ne dcrit pas le langage (cest trop compliqu ce stade).
Il est mond.
Dterminisation :
Tableau 5.27
tat a b
0 12 P
12 12 0
Figure 5.48
198
Corrigs des exercices
(Maintenant il devient bien plus facile de dcrireL(A
5
) si lon veut : il consiste en tous les mots
commenant par a, nissant par a, et o tout b est entour par des a de faon ne jamais avoir bb).
Exercice 5.7
Tableau 5.28
tat a b
02 12 01
01 12 1
12 2 01
1 2 P
2 P 01
P P P
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

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
199
Chapitre 5

Automates
Do lautomate reconnaissant le complment de ce langage :
Figure 5.51
Ltat P nest plus un tat poubelle, car il est un tat terminal.
Solution (b)
Figure 5.52
Cet automate nest pas dterministe, il faut le dterminiser.
Dterminisation
Tableau 5.29
tat 0 1
0 0 01
01 01 01
Figure 5.53
200
Corrigs des exercices
(En fait, on aurait pu dessiner cet automate ds le dbut).
Cet automate dterministe est complet ; donc pour construire lautomate reconnaissant le compl-
ment du langage, il suft de modier la position de la sortie :
Figure 5.54 Il est facile de voir quici ltat 01 est la poubelle.
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 naux,
tats non naux et vice versa. La minimisation se fera ensuite.
Figure 5.55
Cet automate lit tous les mots se terminant en .
Dterminisation
Tableau 5.30
tat a b
0 01 0
01 01 02
02 013 0
013 014 02
014 01 025
025 013 0

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
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 :
Q
0
= {I , I I } avec I = {025}, I I = {0,01, 02,013, 014}
Tableau 5.31
tat a b a b
0 01 0 II II
01 01 02 II II
02 013 0 II II
013 014 02 II II
014 01 025 II I
025 013 0 II II
202
Corrigs des exercices
Le groupe non terminal consiste en un seul tat et na pas lieu se sparer.
Le groupe terminal se spare en deux :
Q
1
= {I , I I , I I I } avec I = {025}, I I = {0,01, 02,013}, I I I = {014}
Tableau 5.32
tat a b a b
0 01 0 II II
01 01 02 II II
02 013 0 II II
013 014 02 III II
Le groupe II se spare en deux :
Q
2
= {I , I I , I I I , I V} avec I = {025}, II = {0, 01, 02}, III={014}, IV = {013}
Tableau 5.33
tat a b a b
0 01 0 II II
01 01 02 II II
02 013 0 IV II
Le groupe II se spare en deux :
Q
3
= {I , I I , I I I , I V, V} avec I = {025}, II = {0, 01}, III = {014}, IV = {013}, V={02}
Tableau 5.34
tat a b a b
0 01 0 II II
01 01 02 II V
Q
4
= Q
3
; tout les tats se sont spars ; lautomate tait minimal.
Exercice 5.10
Solution A
1
Reprenons lautomate dterministe complet A
1
:

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
203
Chapitre 5

Automates
Tableau 5.35
tat a b
0 0 01
01 0 012
012 0 0123
0123 0 0123
Figure 5.58
Minimisation
Q
0
= {I, II} avec I = {0, 01, 012}, II = {0123}.
Tableau 5.36
tat a B A b
0 0 01 I I
01 0 012 I I
012 0 0123 I II
Le groupe I se spare en deux. Modiant la notation, on a :
Q
1
= {I, II, III} avec I = {0, 01}, II = {012}, III = {0123}.
Tableau 5.37
tat a B a b
0 0 01 I I
01 0 012 I II
Les deux tats du groupe I se sont spars, donc tous les tats se sont spars lautomate tait
minimal ds le dbut.
204
Corrigs des exercices
Solution A
2
Reprenons lautomate dterministe complet A
2
mais modions la notation des tats pour plus de
clart :
Tableau 5.38
En renommant
les tats
tat a b tat a b
Initial 0 0 01 A A B
01 02 012 B D C
012 023 0123 C F E
02 03 013 D G H
Terminal 0123 023 0123 E F E
Terminal 023 03 013 F G H
Terminal 03 0 01 G A B
Terminal 013 02 012 H D C
(Le dessin nest pas trop utile pour cet automate.)
Minimisation
Q
0
= {I, II} avec I = {A, B, C, D}, II = {E, F, G, H}.
Tableau 5.39
tat a b a b
G
r
o
u
p
e
n
o
n
t
e
r
m
i
n
a
l A A B I I
B D C I I
C F E II II
D G H II II
G
r
o
u
p
e
t
e
r
m
i
n
a
l
E F E II II
F G H II II
G A B I I
H D C I I
Q
1
= {I, II, III, IV} avec I = {A, B}, II={C, D}, III={E, F}, IV={G, H}.

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
205
Chapitre 5

Automates
Tableau 5.40
tat a b A b
A A B I I
B D C II II
C F E III III
D G H IV IV
E F E III III
F G H IV IV
G A B I I
H D C II II
Tous les tats se sont spars, lautomate tait minimal ds le dbut.
Solution A
3
Reprenons lautomate dterministe complet A
3
:
Tableau 5.41
tat a b
0 01 01
01 01 012
012 0123 012
0123 0123 0123
Figure 5.59
Minimisation
Q
0
= {I, II} avec I = {0, 01, 012}, II = {0123}
Tableau 5.42
tat a b a b
0 01 01 I I
01 01 012 I I
012 0123 012 II I
Ltat 012 est sorti du groupe I. Nouveaux groupes :
Q
1
= {I, II, III} avec I = {0, 01}, II = {012}, III = {0123}.
206
Corrigs des exercices
Tableau 5.43
tat a b a B
0 01 01 I I
01 01 012 I II
Tous les tats se sont spars, lautomate tait minimal ds le dbut.
Solution A
4
Reprenons lautomate dterministe complet A
4
:
Tableau 5.44
tat 0 1
A AB B
AB ABC BC
ABC ABC BC
B C C
C C C
BC C C
Figure 5.60
Minimisation
Q
0
= {I, II} avec I = {A, C}, II = {AB, ABC, BC, B}.

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
207
Chapitre 5

Automates
Tableau 5.45
tat 0 1 0 1
G
r
o
u
p
e
n
o
n
t
e
r
m
i
n
a
l
A AB B II II
C C C I I
G
r
o
u
p
e
t
e
r
m
i
n
a
l
AB ABC BC II II
ABC ABC BC II II
BC C C I I
B C C I I
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 A
4
ntait donc
pas minimal. On voit quen le minimisant on est arriv au mme automate quon a obtenu en
dterminisant lautomate A
4
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 simplier la dterminisation.
208
Corrigs des exercices
Solution A
5
Figure 5.62 A
5
Minimisation
Q
0
= {I, II} avec I = {0, P}, II = {12}.
Tableau 5.46
tat a b a b
0 12 P II I
P P P I I
12 12 0 II I
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
Solution (a)
Dterminisation
Tableau 5.47
tat a b
0 0 01
01 0 012
012 02 012
02 02 012

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
209
Chapitre 5

Automates
Figure 5.63
Minimisation
Q
0
= {I, II} avec I = {0, 01}, II = {012, 02}.
Tableau 5.48
tat a b a b
0 0 01 I I
01 0 012 I II
012 02 012 II II
02 02 012 II II
Le groupe II ne peut pas se sparer. Le groupe I se spare en 0 et 01.
Le rsultat :
Figure 5.64
Solution (b)
Dterminisation
Tableau 5.49
En modiant les
tiquettes des tats
tat a b tat a b
02 12 01 A B D
01 12 012 D B E
12 1 02 B C A
012 12 012 E B E
1 P 02 C P A
P P P P P P
Tous les tats sont des tats naux sauf ltat poubelle.
210
Corrigs des exercices
Tableau 5.50
tat a b a b
A B D II II
D B E II II
B C A II II
E B E II II
C P A I II
Minimisation
Q
0
= {I, II} avec I = {P}, II = {A, B, C, D, E}.
Nouveaux groupes dtats : I = {P}, II = {C}, III = {A, B, D, E}.
Tableau 5.51
tat a b a b
A B D III III
D B E III III
B C A II III
E B E III III
Nouveaux groupes dtats : I = {P}, II = {C}, III = {B}, IV = {A, D, E}.
Tableau 5.52
tat a b a b
A B D III IV
D B E III IV
E B E III IV
Le groupe IV ne se spare pas.
Le rsultat :
Figure 5.65

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
211
Chapitre 5

Automates
Exercice 5.12

3
tats 0, 1, 2;
ches 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 a b
0 1
1 2 0,1
2 0 0,2
Figure 5.66

4
tats 0, 1, 2, 3;
ches 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
tat a B
0 1
1 2 0,1
2 3 0,2
3 0 0,3
212
Corrigs des exercices
Figure 5.67

n
Tableau 5.55
tat a B
0 1
1 2 0,1
2 3 0,2
3 4 0,3
... ... ...
n 1 0 0, n 1
Figure 5.68

D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
213
Chapitre 5

Automates
Dterminisation
Tableau 5.56
tat a B
0 1
1 2 01
2 3 02
3 4 03
... ... ...
n 1 0 (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)
... ... ...
Ici gurent toutes les combinaisons possibles de m tats parmi n cest--dire : 1 m n.
Il y a
n

m=0
C
m
n
= 2
n
1 de tels tats. Si lon y ajoute ltat poubelle (o mne la che tiquete b
partant de ltat 0), pour que lautomate soit complet, on obtient 2
n
tats.
214
BIBLIOGRAPHIE
BAYNAT B., CHRTIENNE P., HANEN C., KEDAD-SIDHOUM S., MUNIER-KORDON A. et PICOU-
LEAU C. Exercices et problmes dalgorithmique 2
e
dition. Dunod, Paris, 2007.
CARREZ C. Des structures aux bases de donnes. Dunod, Paris, 1994.
CORMEN T., LEISERSON C., RIVEST R. et STEIN C. Introduction lalgorithmique 3
e
dition.
Dunod, Paris, 2010.
FROIDEVAUX C., GAUDEL M.-C. et SORIA M. Types de donnes et algorithmes. Ediscience
International, Paris, 1994.
GUYOT J. et VIAL C. Arbres, tables et algorithmes. Eyrolles, Paris, 1992.
LUC BOUGE L., KENYON C., MULLER J.-M. et ROBERT Y. Algorithmique. Exercices corrigs.
Ellipses, Paris, 1993.
215
INDEX
A
accepteurs 169
adresse 18
affectation 7
afcher 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
ni 169, 170, 172, 173, 176, 178
B
boucle 12
C
calcul 1
champ 29
complexit algorithmique 1
concatnation 37, 45, 51, 52
condition 9
constante 7
D
dcidabilit 1
algorithmique 1
logique 1
pratique 1
thorique 1
dterminisation 173177, 195198, 200, 201,
208210, 214
E
mond 176, 188, 195, 196, 198
quinalit 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
FIFO 90
le 90
fonction 23
appel 25
appelante 25
appele 25
argument 26
corps 24
en-tte 24
H
Heqat 99
I
insertion 35, 37, 43, 99, 112
point d 112
itratif 12, 45, 49, 50, 54, 63, 142, 178
D
u
n
o
d

L
a
p
h
o
t
o
c
o
p
i
e
n
o
n
a
u
t
o
r
i
s

e
e
s
t
u
n
d

l
i
t
217
Exercices et problmes dalgorithmique
L
LIFO 87
liste
doublement chane 44, 99, 112
linaire 35
simplement chane 38, 46, 112
M
machine de Turing 169
minimisation 178, 182, 201
mot
vide 170
N
NON 11
NULL 21
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
R
racine 127
raisonnement 1
rcursif 45, 5052, 61, 65, 136, 142
rcursivit 127, 136
S
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
T
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
218