You are on page 1of 228

EXERCICES ET PROBLMES

DALGORITHMIQUE
http://www.free-livres.com/

Rappels de cours
X Exercices et problmes
avec corrigs dtaills
X Solutions en pseudo code
et en langage C
X

Nicolas Flasque
Enseignant mathmatiques et informatique, EFREI

Helen Kassel
Enseignant mathmatiques et informatique, EFREI

Franck Lepoivre
Enseignant-chercheur

Boris Velikson
Enseignant mathmatiques et informatique, EFREI

Illustration de couverture : digitalvision

Dunod, Paris, 2010


ISBN 978-2-10-055072-2

TABLE

DES MATIRES

AVANT-PROPOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

IX

INTRODUCTION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CHAPITRE 1 LES BASES DE LA PROGRAMMATION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.1 Les types de donnes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.2 Les variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.3 Quelques lments de syntaxe pour le langage algorithmique . . . . . . . . . . . . . . . . .

1.4 Oprations et oprateurs de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


1.4.1 Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.3 Oprateurs arithmtiques et expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.4 Oprateurs dentre/sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7
7
7
8
8

1.5 Structure de contrle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


1.5.1 Conditions et tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.2 Excution conditionnelle dinstructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.3 Itrations et boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9
9
9
12

1.6 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.1 Dfinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.2 Reprsentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.3 Relation entre tableaux et boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6.4 Les tableaux plusieurs dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14
14
15
16
17

1.7 Pointeurs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7.1 Notion dadresse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7.2 Dfinition et contenu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7.3 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18
18
19
20

1.8 Les sous-programmes ou fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


1.8.1 Dfinition dune fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23
24
V

Exercices et problmes dalgorithmique

1.8.2 Appel des fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


1.8.3 Les fonctions et les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.4 Les fonctions et les pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25
27
28

1.9 Cration de types par le programmeur : les types composs ou structures . . . . . .


1.9.1 Accs aux champs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.2 Oprateur daffectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.3 Structures contenant des tableaux et des pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.4 Structures dfinies laide de structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.5 Pointeurs vers les structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.6 Types pointeurs et raccourcis de notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.7 Structures et fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29
30
31
31
31
32
33
34

CHAPITRE 2 STRUCTURES SQUENTIELLES SIMPLES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

Rappels de cours. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

2.1 Listes linaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


2.1.1 Dfinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.2 Reprsentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.3 Variables dynamiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.4 Variantes dimplantation des listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35
35
35
37
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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1 Reprsentation contigu des piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.2 Reprsentation chane des piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.3 Manipulation dune pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

87
87
88
88

3.2 Les files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


3.2.1 Reprsentation contigu des files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.2 Reprsentation chane des files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.3 Manipulation dune file (mthode avec deux pointeurs) . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90
90
91
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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


4.1.1 Dfinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.1.2 Reprsentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.1.3 Algorithmes de parcours dun arbre binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.1.4 Arbres binaires de recherche (ABOH = Arbres Binaires Ordonns Horizontalement) . . . . .

127
128
128
129
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 dfinitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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

Dunod La photocopie non autorise est un dlit

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 spcification 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

IX

Exercices et problmes dalgorithmique

La liste vide ( un
pointeur ne pointant
sur rien)

Le cas particulier du
couple (liste deux
lments)

Une place terminale


(par composition)

Un singleton (liste Une liste lments


un seul lment)
multiples

Reprsentation des
modifications 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.
Enfin, 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 enfin, 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).

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

Dunod La photocopie non autorise est un dlit

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 affirmatives.
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 fini 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 sousproblmes 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, signifie dcrire une mthode de raisonnement (un programme) qui
dtermine la solution dun problme en un nombre fini dtapes de calcul, il se peut que le temps
ncessaire ce calcul place le rsultat final hors de porte.
Cest ici quinterviennent les notions dquifinalit1 , notion prleve sur le vocabulaire stratgique, 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, lquifinalit traduit la possibilit pour un systme
datteindre un mme but par diffrents chemins, i.e. une seule stratgie (le but), mais plusieurs tactiques pour raliser la
stratgie.
1

Exercices et problmes dalgorithmique

coteuses que dautres, en termes de ressources temps, en termes de ressources mnmoniques


mobilises.
Savoir valuer, avant de lexcuter, lefficacit 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.
Enfin, 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. Linfluence du
paradigme de programmation spcifique 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 TAOCP1 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 spcification dun schma de calcul, sous
forme dune suite [finie] 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.
Dfinissons plus formellement le concept :
Un algorithme dcrit un traitement sur un certain nombre, fini, de donnes.
Un algorithme est la composition dun ensemble fini dtapes, chaque tape tant forme dun
nombre fini doprations dont chacune est :
dfinie de faon rigoureuse et non ambigu ;
effective, cest--dire pouvant tre effectivement ralise par une machine : cela correspond
une action qui peut tre ralise avec un papier et un crayon en un temps fini ; par exemple
la division entire est une opration effective, mais pas la division avec un nombre infini de
dcimales.
Quelle que soit la donne sur laquelle on travaille, un algorithme doit toujours se terminer aprs
un nombre fini doprations, et fournir un rsultat.

Dunod La photocopie non autorise est un dlit

CONCEPTION DUN ALGORITHME


La conception dun algorithme un peu compliqu se fait toujours en plusieurs tapes qui correspondent des raffinements successifs. La premire version de lalgorithme est autant que possible
indpendante dune implmentation particulire.
En particulier, la reprsentation des donnes nest pas fixe.
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
dfinition des types de donnes (on dit encore leur spcification), 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 informations 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.

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.

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 reprsentation 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 effet, 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 lafficher
lcran. Cette table de correspondance se nomme la table de symboles (table ASCII, Unicode).

Pour la table ASCII, un caractre est stock dans un octet (un groupement de 8 bits), la valeur
entire peut donc aller de 0 255. Dans le tableau ne sont prsentes que les valeurs de 32 127 :
en de de 32, il sagit de caractres non imprimables, au-del de 127, ce sont des caractres
optionnels, qui sont adapts un type de clavier ou de langue particulier, notamment les caractres
accentus (, , , , , etc.).
5

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 identificateur 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.

Lidentificateur ou nom de la variable peut tre quelconque, mais doit respecter les critres
suivants :
un identificateur commence toujours par une lettre minuscule ;
lexception du premier caractre, il peut contenir : des lettres, des chiffres, et le symbole _
(soulign ou underscore) ;
les majuscules et les minuscules sont des lettres diffrentes : les identificateurs toto et Toto
sont diffrents ;
le nom de variable doit avoir une relation avec le rle de cette variable et tre comprhensible.

1.3 QUELQUES LMENTS DE SYNTAXE POUR LE LANGAGE


ALGORITHMIQUE
Pour crire correctement un programme en langage algorithmique, il faut fournir certaines informations 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 dfinir, cest--dire indiquer le mot
VAR, puis le nom de la variable et enfin son type prcd de :.
Une variable sappelant taux, et dont le type est rel, doit tre dfinie de la manire suivante :
VAR taux : rel

Cette dfinition cre une variable nomme taux dans laquelle peuvent tre stocks des nombres
virgule.
Quelques exemples de dfinition de variables
VAR solution_equation : rel dfinit une variable nomme solution_equation dont le type
est rel ;
VAR val_1 : entier dfinit une variable nomme val_1 dont le type est entier ;
VAR lettre : caractre dfinit une variable nomme lettre dont le type est caractre.

1.4. Oprations et oprateurs de base

Il est possible de dfinir plusieurs variables dun mme type en spcifiant le nom du type,
puis la liste des noms de variables spars par des virgules ,. Ainsi, les dfinitions suivantes
sont strictement quivalentes : VAR val_a : entier, VAR val_b : entier, VAR val_c : entier
VAR val_a, val_b, val_c : entier

1.4 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 daffectation 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 influence sur la syntaxe :
Constantes de type entier : il suffit juste dcrire la valeur en base dix, cette valeur peut tre
positive ou ngative. La variable recevra alors la valeur choisie.
Constantes de type rel : elles sont crites sous la forme mantisse exposant, cest--dire
la notation scientifique avec les puissances de dix, utilise par les calculatrices. La virgule est
reprsente par un point (notation anglo-saxonne), et lexposant, qui est un nombre positif ou
ngatif, est prcd du symbole E. Il est possible de ne pas indiquer :
lexposant lorsque celui-ci est nul ;
le signe + devant lexposant si celui-ci est positif ;
la partie dcimale dun nombre si celle-ci est nulle, par contre on fera toujours figurer le point
dcimal.
Constantes de type caractre Il est possible daffecter un entier un caractre en utilisant
une constante entire ou en indiquant le caractre souhait entour de simples guillemets. Ces
simples guillemets se lisent alors : "code ASCII de".
Exemple de programme daffectation
programme affectations
VAR a : entier
VAR x : rel
VAR ma_var : caractre
a -6

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 dafficher 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, afficher du texte ou les valeurs des variables. On
utilise afficher, 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 affiche. Les lments successifs afficher 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 !

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 saffiche pour indiquer lutilisateur ce quil
doit faire ; cest donc au programmeur de penser afficher un message pour indiquer quune
saisie doit tre faite !
Exemple
VAR a : entier
// dfinition de la variable entire a
SAISIR(a)
// saisie de la variable
// lutilisateur doit entrer une valeur au clavier
// et la valider par la touche entre

Contrairement loprateur AFFICHER, on ne peut saisir que des variables avec SAISIR.

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 coefficients. 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).

Dunod La photocopie non autorise est un dlit

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).
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

6=

valeur16= 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 6 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;
}

Dunod La photocopie non autorise est un dlit

La partie SI...ALORS est identique la structure de contrle simple : si la condition est vraie,
alors les instructions 1 sont excutes, et pas les instructions 2. Les instructions 2, concernes par
le SINON, sont excutes si et seulement si la condition est fausse.
Dans un programme, lutilisation de tests est trs frquente. Quelle forme aurait un programme
dans lequel il serait ncessaire de vrifier deux, quatre, voire dix conditions avant de pouvoir
excuter une instruction ? En vertu des rgles dindentation et de prsentation, il deviendrait
rapidement illisible et moins comprhensible. Afin dviter cela, il est possible de regrouper
plusieurs conditions en une seule en utilisant dautres oprateurs logiques (boolens) encore appels
connecteurs logiques.
Ces oprateurs permettent deffectuer des calculs avec des valeurs de type VRAI ou FAUX et de
fournir un rsultat de type VRAI ou FAUX. Nous en prsentons trois, nomms ET, OU et NON en
indiquant simplement les rsultats quils produisent. Ces tableaux sont nomms tables de vrit :
ET : intuitivement, la condition (c1 ET c2) est VRAI si et seulement si la condition c1 est VRAI
ET si la condition c2 est VRAI.
OU : la condition (c1 OU c2) est VRAI si et seulement si la condition c1 est VRAI OU si la
condition c2 est VRAI.
NON : loprateur NON change quant lui la valeur de la condition quil prcde. Il se note
galement .
Tableau 1.2
Oprateur OU

Oprateur ET

Oprateur NON

c1

c2

c1 ET c2

c1

c2

c1 OU c2

c1

NON c1

FAUX
FAUX

FAUX
VRAI

FAUX
FAUX

FAUX
FAUX

FAUX
VRAI

FAUX
VRAI

FAUX

VRAI

Implantation C

ET
OU

se note &&
se note ||
se note !

11

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 dobtenir 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 suivante :


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 recommence 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 suivante :


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 recommence 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.

Dunod La photocopie non autorise est un dlit

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;
13

Chapitre 1 Les bases de la programmation

Lorsque lordinateur rencontre cette structure, il procde systmatiquement de la manire suivante :


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 difficilement envisageable de dfinir manuellement autant de variables que de valeurs
stocker. Les tableaux, en informatique, permettent de rsoudre ce problme en proposant la
cration de plusieurs variables de mme type, dune manire trs compacte.
1.6.1 Dfinition
Un tableau se dfinit en indiquant son nom, le type des lments stocks dans le tableau, ainsi que
leur nombre, crit entre crochets. Ce nombre se nomme galement la taille maximale du tableau.
Syntaxe : VAR nom_du_tableau : type_des_lments[taille_maximale]
Exemple
VAR tab : entier[100] est la dfinition dun tableau nomm tab qui peut stocker 100 valeurs
de type entier au maximum.

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
dfinition suivante :
VAR vals : rel[15]

Cette dfinition cre un tableau nomm vals qui stocke au maximum quinze rel dans quinze
cases diffrentes. Aucune de ces cases nest initialise, ce qui est indiqu par un ? .
Tableau 1.3
?

vals

Chacune de ces cases est repre par son numro ou indice lintrieur du tableau. Un tableau
de taille maximale N verra ses cases numrotes de 0 N1. En effet, pour un ordinateur, la valeur
0 est une valeur comme une autre.
Soit i un indice (i est donc de type entier puisquil sagit dun numro de case). La valeur stocke
dans la case dindice i dun tableau tab se nomme tab[i].
Tableau 1.4
0

10

11

12

13

tab[0]

tab[2]

tab[6]

14
?
tab[14]

Remarque
Cette schmatisation sera employe de nombreuses reprises dans cet ouvrage. Nhsitez pas
lutiliser lors de la rsolution des exercices, elle aide visualiser le droulement des algorithmes.

Il ne faut pas confondre la valeur note entre crochets lors de la dfinition du tableau (la taille
maximale) et la valeur note entre crochets lors des instructions (lindice).
Toute expression qui donne une valeur entire peut jouer le rle dindice lors de laccs aux
valeurs stockes dans un tableau.
La notation t[i], o t est un tableau et i un indice est quivalente une variable et peut tre
utilise comme telle dans un programme. Cette variable est considre comme ayant le type des
lments stocks dans le tableau.
Soient les dfinitions de variables suivantes :
VAR x : rel
VAR z : rel[20] // z est un tableau stockant au plus 20 rels
VAR idx : entier // variable utilise comme un indice
15

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 fixe lors de lcriture du programme. Or un tableau ne stockera
pas, en pratique, toujours autant dlments que sa taille maximale. Il est donc ncessaire de savoir
combien de variables sont rellement intressantes traiter. Ce nombre de variables doit tre stock
dans une variable de type entier nomm taille utile du tableau par opposition la taille maximale
fournie la dfinition du tableau. Dans un tableau dont la taille utile est P et dont la taille maximale
est N, les variables intressantes seront ranges aux indices compris entre 0 et P1. Les cases dont
les indices vont de P N1 stockent des valeurs (car une variable nest jamais vide) mais elles ne
seront pas concernes par les traitements ou instructions effectues.
chaque dfinition dun tableau est associe la dfinition de sa taille utile. Ceci doit tre
systmatique.
Cette taille utile peut voluer lorsque :
Un lment est ajout au tableau, dans ce cas la taille utile est augmente de 1 (ou encore
incrmente). Avant dajouter un lment, il faut vrifier quil reste au moins une case disponible :
la taille utile doit tre infrieure strictement la taille maximale du tableau. Cette vrification
est ralise par lemploi dun test (instruction si).
Un lment est retir du tableau, dans ce cas la taille utile est diminue de 1 (ou dcrmente).
Avant denlever un lment du tableau, il faut vrifier que la taille utile est strictement positive,
cest--dire quil y a au moins un lment dans le tableau.
Enfin, cette variable stockant la taille utile dun tableau na pas de nom qui lui est spcifiquement
ddi. Pour des raisons de lisibilit, cette variable sera nomme util ou tai_ut par exemple.
1.6.3 Relation entre tableaux et boucles
Les boucles sont extrmement utiles pour les algorithmes associs aux tableaux. En effet, de
nombreux algorithmes relatifs au tableau ncessitent de parcourir les lments du tableau dans
un certain ordre, le plus souvent dans le sens des indices croissant. Le traitement de chacun des
lments tant souvent le mme, seule la valeur de lindice est amene changer. Une boucle est
donc parfaitement adapte ce genre de traitements.
Illustration par un algorithme de recherche de la plus grande valeur stocke dans un tableau dentiers. Cet algorithme est abondamment comment car il est une synthse des notions rencontres
pour les tableaux.

16

1.6. Tableaux

Dunod La photocopie non autorise est un dlit

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 dfinir des tableaux plusieurs dimensions en les indiquant dans des crochets
successifs lors de la dfinition 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 dfinition

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 dfinition permet de simplifier 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 suffisent bien comprendre cette notion fondamentale. Une fois de plus, la notion de type
sera essentielle dans cette partie.
1.7.1 Notion dadresse
Toute variable possde trois caractristiques : un nom et un type, qui ne peuvent tre modifis au
cours du programme, car fixs au moment de la dfinition de la variable ; et une valeur qui au
contraire volue au cours du programme. En ralit, toute variable possde galement une autre
caractristique fondamentale, utilise en interne par la machine : une adresse. En effet, afin de
mmoriser la valeur dune variable, lordinateur doit la stocker au sein de sa mmoire, mmoire
dont les lments ou cellules sont reprs par des numros (de manire analogue ce qui se passe
dans un tableau en ralit). Le nom de la variable nest en fait pas utile la machine, qui se contente
de son adresse ; cest pour le confort du programmeur que le choix du nom est rendu possible.
Ainsi, il existe une dualit entre le nom de la variable (ct programmeur) et son adresse (ct
machine).
Ladresse dune variable nest autre que le numro de la case ou cellule mmoire que celle-ci
occupe au sein de la machine. Au mme titre que son nom, ladresse dune variable ne peut pas
varier au cours dun programme. Il nest mme pas possible de choisir ladresse dune variable,
cette adresse est attribue automatiquement par lordinateur.
Chose curieuse, ces adresses de variables seront manipules sans mme connatre leur valeur
exacte. Il suffira de savoir les nommer pour les utiliser.
Pour ce faire, un nouvel oprateur est ncessaire : loprateur &. Il se lit tout simplement adresse
de et prcde uniquement un nom de variable.
Exemple
Soit la dfinition de variable suivante : VAR x : rel
Cette simple dfinition attribue la variable : un nom (x), un type (rel), une valeur (inconnue),
et galement une adresse (de manire automatique).

18

1.7. Pointeurs

La notation &x signifie donc : adresse de x . La valeur prcise de cette adresse ne nous est en
ralit daucun intrt, mais il sera parfois ncessaire de la manipuler.

Toute variable possde une et une seule adresse.

1.7.2 Dfinition 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.

Dunod La photocopie non autorise est un dlit

Notion de contenu
Soit p un pointeur (il sera temps de passer la syntaxe exacte de dfinition de pointeur lorsque
toutes les notions ncessaires auront t abordes). p tant un pointeur, il est galement une variable,
et donc possde un nom, une valeur (qui est une adresse) et un type.
Un pointeur possde en plus une autre caractristique, qui est son contenu.
Une adresse est le numro dune cellule mmoire, et dans cette cellule, se trouve une valeur.
Cette valeur est appele le contenu du pointeur, et ne doit pas tre confondue avec sa valeur.
Le contenu dun pointeur est la valeur de la cellule mmoire dont ce pointeur stocke ladresse.
Puisque ces deux notions sont diffrentes, il existe une nouvelle notation pour indiquer laccs
un contenu : cette notion est loprateur * (lire toile). Cet oprateur est le mme que loprateur
de multiplication, mais le contexte dcriture permet lordinateur de reconnatre quelle est la
signification exacte de cet oprateur.
Voici une rgle trs simple et trs utile pour lutilisation de cet oprateur *: il se lit toujours
comme contenu de , et non pas pointeur . Cette rgle vitera de nombreuses confusions par
la suite.
Exemple
Soit ptr un pointeur. Supposons que la valeur de ce pointeur soit 25040 (rappelons que cette
valeur est tout fait arbitraire). Supposons galement que dans la mmoire, lemplacement
25040 se trouve la valeur 17.
Dans ce cas, la valeur de ptr, note ptr, est 25040.
Le contenu de ptr, not *ptr, est la valeur de la cellule mmoire numro 25040, cest--dire 17.
*ptr vaut donc 17

Type point
Quelle est la syntaxe permettant de dfinir un pointeur ? Cette syntaxe nest hlas pas immdiate
(sinon elle aurait dj t prsente), car le type pointeur nexiste pas en tant que tel. Il
19

Chapitre 1 Les bases de la programmation

est ncessaire dajouter des informations supplmentaires afin que lordinateur puisse exploiter
correctement ces pointeurs.
En ralit, un pointeur a besoin dinformations sur son contenu, sinon il ne peut rien en faire. La
valeur dun pointeur nest quune adresse, mais cette information nest pas suffisante pour grer les
contenus possibles.
Les diffrents types dj voqus auparavant, entier, reel et caractere, ont des tailles
diffrentes : un entier occupe quatre octets (ou cellules), un rel en occupe huit et un caractre un
seul. Lorsque lordinateur cherche accder au contenu dun pointeur en mmoire, il doit savoir
combien de cellules seront concernes par cette opration. Le type de contenu, encore appel type
point, est indispensable la dfinition dun pointeur. Il lui est mme tellement li quun pointeur
ne peut pointer quun seul type de contenu.
Il nexiste donc pas de pointeur gnrique vers nimporte quel type de contenu, mais des
pointeurs sur (ou vers) des entier, des pointeurs sur des reel, des pointeurs vers des caractere.
Un pointeur se dfinit par le type de son contenu. Pour la cration dun pointeur nomm ptr qui
pointe sur des entiers, sa dfinition scrit : le contenu de ptr est de type entier . En langage
informatique, cest cette notation qui sera traduite en dfinition de variable, de la manire suivante :
VAR *ptr : entier (se lisant le contenu de ptr est de type entier ). Puisque le contenu de
ptr est de type entier, il est ais den dduire que ptr est un pointeur vers un entier. Attention ce
point qui ne semble pas trs important, mais qui est fondamental.
Il en est de mme pour les autres types points : la dfinition dun pointeur nomm ptr_reel
vers un rel est la suivante : VAR *ptr_reel : rel (se lisant : le contenu de ptr_reel est de
type reel ). Enfin, la dfinition dun pointeur nomm p_cgs vers un caractre est la suivante :
VAR *p_cgs : caractere (se lisant : le contenu de p_cgs est de type caractere ).
Dans les exemples de dfinition de pointeurs prcdents, la convention de nommage dun
pointeur veut que son nom dbute par ptr ou p_. Cependant un pointeur tant une variable, le
choix de son nom nest contraint que par les rgles de nommage de variables exposes en dbut de
chapitre.
1.7.3 Initialisation
Les dangers de loprateur *
Comme pour toute variable, un pointeur doit tre initialis avant dtre manipul. Comme un
pointeur stocke une adresse, il ne peut tre initialis comme une simple variable de type entier,
reel ou caractere car les adresses sont gres par la machine et non par lutilisateur. Accder
un contenu est une opration dlicate car elle est en relation avec la mmoire, qui est une ressource
indispensable au fonctionnement de lordinateur. Chaque programme y stocke les valeurs dont
il a besoin pour sexcuter correctement. tant donn quun ordinateur de type PC classique fait
cohabiter plusieurs dizaines de programmes diffrents au mme instant, il doit dlguer la gestion
de la mmoire un programme particulier nomm le systme dexploitation. Le rle de ce dernier
est de veiller au bon fonctionnement de la partie hardware de lordinateur, partie qui concerne la
mmoire.

20

1.7. Pointeurs

En imaginant quun pointeur puisse stocker nimporte quelle valeur, accder au contenu se
rvlerait particulirement dangereux pour la stabilit du systme. Un simple exemple devrait vous
en convaincre.
Exemple
Soient la dfinition et linitialisation de pointeur suivantes :
VAR *ptr : entier
// lire : "le contenu de ptr est de type entier"
ptr 98120
// initialisation de la valeur du pointeur
// (ptr donne accs la valeur de ptr)
*ptr -74
// initialisation du contenu du pointeur ptr
// (*ptr est le contenu de ptr)

Ces simples instructions permettraient au programme dcrire une valeur arbitraire nimporte
quelle adresse de la mmoire de lordinateur, ce qui nest pas du got du systme dexploitation.
Dans le cas dun accs une adresse non autorise, ce dernier met une fin brutale lexcution du
programme responsable de cet accs : cest une des sources des plantages des programmes,
et mme la source la plus frquente de ce phnomne.
Afin dviter un trop grand nombre derreurs de ce type, lordinateur (le compilateur en ralit),
refusera dinitialiser un pointeur avec des valeurs arbitraires.

Le seul cas o un pointeur stocke une valeur arbitraire est lorsquil vient dtre dfini : comme
toute autre variable, il stocke alors une valeur alatoire. La rgle dor dutilisation de loprateur *
(accs au contenu, ou encore prise de contenu) est la suivante :
laccs au contenu dun pointeur ne se fait en toute scurit que si ce dernier a t correctement
initialis.

Dunod La photocopie non autorise est un dlit

La valeur NULL
Il est possible dinitialiser un pointeur avec une adresse qui est forcment inaccessible, quel que
soit le programme crit. Lutilit dune telle initialisation est quelle permet de savoir par un simple
test si le pointeur a t initialis avec une adresse valide, et ainsi viter les accs malencontreux
des adresses invalides. Ladresse qui est forcment inaccessible est ladresse 0. Selon le type de
machines, cette adresse stocke des informations plus ou moins vitales pour le compte du systme
dexploitation. En informatique, ladresse 0 porte un nom particulier : NULL, qui nest quun autre
nom qui est donn la valeur 0, mais NULL ne sutilise que dans des contextes particuliers. De
nombreuses instructions utilisent dailleurs cette valeur note NULL.
Exemple dutilisation de NULL
PROGRAMME test_NULL
// lire "le contenu de p_val est de type rel" :
VAR *p_val : rel
// initialisation de p_val avec NULL
// que lon sait inaccessible
p_val NULL
// plus loin dans le programme...
SI (p_val 6= NULL)

21

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 affiche un message davertissement plutt que de provoquer une erreur
qui nest pas vidente reprer et corriger : ce type de programmation est privilgier dans la
mesure o le programmeur matrise beaucoup mieux ce qui se passe au sein du programme.

Les adresses de variables existantes


Les variables que lutilisateur dfinit 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 dfinition 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 initialisation 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 dfini de la manire suivante :
VAR *ptr : rel
A priori, ptr nest pas initialis et il est donc hasardeux daccder son contenu. Afin dinitialiser
ptr correctement, il est possible de reserver de lespace mmoire pour son contenu.
RESERVER(ptr) aura cet effet.

Une rservation de mmoire se nomme galement une allocation dynamique de mmoire.


Lorsque la place en mmoire nest plus utile, il suffit 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 afficher plusieurs fois des tableaux dont les variables stockent des valeurs diffrentes.
Cependant, mme si les valeurs sont diffrentes, laffichage 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 affichage de chaque valeur grce linstruction AFFICHER().
Il serait utile de regrouper ces instructions daffichage, 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 dfinie avec le formalisme des mathmatiques :
F :RRR
x, y x 2 + x y + 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 + x y + y 2 .
Types des entres et sorties Daprs la dfinition (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, effectue un calcul,
et a comme sortie une valeur de type rel.

1.8.1 Dfinition dune fonction


Comme de nombreuses entits en informatique, une fonction doit tre dfinie avant dtre utilise,
cest--dire que lon doit indiquer quelles sont les instructions qui la composent : il sagit de la
dfinition de la fonction, o lon associe les instructions lidentification de la fonction.
On doit donc trouver une dfinition de la fonction, qui comporte :
Une identification ou en-tte de la fonction, suivie des instructions de la fonction, ou corps de
la fonction.
La fonction doit tre dfinie et comporter : un en-tte, pour lidentifier et un corps contenant ses
instructions, pour la dfinir.

Comment crire une dfinition de fonction


En-tte

Len-tte dune fonction contient les informations ncessaires pour identifier la fonction : son nom,
ses entres et leur type, le type de sa sortie (sans la nommer, car cest inutile).
Syntaxe de len-tte
FONCTION nom(liste entres avec leurs types ): type de la sortie

La liste des entres avec leur type suit exactement la mme syntaxe que les dfinitions de
variables, et ceci nest pas un simple hasard, car les entres sont des variables de la fonction.
La seule diffrence rside dans le fait que si plusieurs entres ont le mme type, on ne peut pas
les crire comme on le ferait pour une dfinition multiple de plusieurs variables du mme type. Il
faut rappeler le type de lentre pour chacune des entres.
La sortie ne ncessite pas dtre nomme, car cest la fonction ou au programme appelant la
fonction de rcuprer cette valeur. Le rle de la fonction se limite uniquement, dans le cas de la
sortie, pouvoir fournir une valeur numrique, seul son type est donc important.
Corps

Le corps de la fonction est constitu des instructions de la fonction, et est plac directement la
suite de len-tte de la fonction, entre les mots DEBUT et FIN.
Dans le corps de la fonction, outre les instructions, on peut galement trouver des dfinitions de
variables qui peuvent tre utiles pour faire des calculs intermdiaires lorsque lon utilise la fonction.
Les dfinitions des variables se trouvent avant le mot DEBUT.
Les variables locales

Ces variables, dfinies lintrieur de la fonction, sont des variables dites locales, car elles ne
sont connues que par la fonction. Cest logique, car cest la fonction qui les dfinit (en quelque
24

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 dfinition de variable.
Enfin, les dfinitions des variables locales la fonction suivent exactement les mmes rgles que
celles que nous avons dj rencontres pour les dfinitions de variables dun programme.
Linstruction de retour

Pour traiter intgralement les fonctions, nous avons besoin dune nouvelle instruction permettant
que la fonction communique sa sortie la fonction qui lutilise. La sortie dune fonction (il y en
a au maximum une) est en fait une valeur numrique que celle-ci fournit, et la seule information
ncessaire la bonne interprtation de cette valeur numrique est son type : cest pourquoi on ne
prcise que le type de cette sortie, il est inutile de lui associer un nom. Il nous faudrait donc une
instruction dont leffet serait : rpondre la fonction appelante la valeur quelle demande .
Cette instruction existe, son nom est RETOURNER.
Exemple
Syntaxe : RETOURNER expression
Effet : transmet la valeur de lexpression et met fin lexcution de la fonction.

Toute instruction place aprs linstruction RETOURNER est purement et simplement ignore, ce
qui fait que cette instruction semploie forcment comme dernire instruction dune fonction.
La seule contrainte respecter est que le type de lexpression soit le mme que celui de la sortie,
qui est indiqu dans len-tte de la fonction.
Lorsquune fonction ne possde pas de sortie, il nest pas indispensable dutiliser linstruction
retourner, part pour indiquer que la fonction se termine, ce qui peut aussi tre repr par le mot
FIN de la fonction.
Dunod La photocopie non autorise est un dlit

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. Afin 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)

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 : affectation
Lorsquune fonction est appele de cette manire, la valeur de la sortie retourne par la fonction
appele est obtenue ainsi : la valeur numrique de lexpression suivant linstruction RETOURNER
est calcule puis transmise la fonction appelante. Au sein de cette fonction appelante, lcriture
nom_de_fonction(liste_des_arguments) est en ralit une expression dont le type est celui de
la sortie de la fonction, qui est prcis dans len-tte de cette dernire. Il sagit donc dune valeur
numrique que lon doit affecter une variable si on veut la conserver.
Lorsque vous voulez conserver le rsultat retourn par une fonction appele, il faut affecter ce
rsultat (obtenu par un appel) dans une variable du mme type que celui de la sortie de la fonction
appele.
Le passage des paramtres
Nous allons tudier, dans cette partie, le mcanisme de passage des paramtres, qui est un point
fondamental concernant les fonctions. Nous allons voir en dtail la manire dont se fait la communication entre les arguments fournis par la fonction appelante et les paramtres (ou entres) reus
par la fonction appele. Ce passage de paramtre est effectu chaque appel dune fonction.
Le mcanisme de recopie
Les actions effectues par lordinateur lors du passage des paramtres (transmission de la valeur
des arguments au moment dun appel de fonction) sont les suivantes :
les valeurs des arguments sont calcules (ou values) ;
26

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 modifies par une
autre fonction lorsque lon applique ce mcanisme pour des paramtres de type simple (entier,
caractere ou reel). En effet, il ne faut pas confondre les arguments dune fonction appele et
les variables du programme qui appelle cette fonction. Un argument nest pas ncessairement une
variable, et mme si cest le cas, cest la valeur de largument qui est transmis la fonction et non
la variable elle-mme. On peut donc en conclure quune variable utilise comme argument dun
appel de fonction ne sera pas modifie par lappel, car cest simplement sa valeur qui est recopie.
1.8.3 Les fonctions et les tableaux
Syntaxe utilise pour les entres de type tableau
Un tableau est une adresse, et cest donc une adresse qui sera transmise par le biais dune entre
dun tel type. Il nest pas possible de transmettre, avec un seul paramtre, dautres informations
que ladresse, cest--dire la taille utile ou mme la taille maximum du tableau. Pour transmettre
lune de ces informations, il faudra utiliser un paramtre supplmentaire. Ainsi, un paramtre de
type tableau sera dfini comme un tableau contenant un certain type de valeurs, mais sans fournir
ni la taille maximum, ni la taille utile.
Exemple
Une entre de type tableau sera fournie de la manire suivante :
nom_entree : type_des_valeurs_stockes[]
Les crochets ne contiennent aucune valeur, ils sont juste prsents pour indiquer que le type de
lentre est un tableau contenant des valeurs dun certain type.
Lorsque lon voudra traiter les valeurs stockes dans ce tableau, il faudra par contre avoir une
information concernant la taille utile de ce tableau : il faudra donc associer systmatiquement
cette entre une entre de type tableau.

Noubliez pas que, mme si la taille utile est dfinie dans le programme principal ou dans une
autre fonction que celle qui traite le tableau, cette taille utile sera stocke dans une variable
laquelle la fonction ne pourra pas accder ! Il faudra donc la lui transmettre.

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 dfinir 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 dfinition suivante :
tab_car : caractere[20]
cela indique que tab_car est un tableau contenant au plus 20 caractres. Donc tab_car est de
type : tableau de caractere, ou encore pointeur vers des caractres. tab_car est une adresse.

Lorsque lon veut fournir une fonction un argument qui est un tableau, on doit lui fournir une
adresse.

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 dfinition de celle-ci, lorsque le
paramtre est un pointeur est la suivante :
*nom_du_paramtre : type_point

Cela revient donc utiliser la syntaxe de dfinition dune variable de type pointeur. Pour un
paramtre, cette criture sinterprte un peu diffremment, mme si cette interprtation est tout
fait cohrente avec tous les aspects abords avec les pointeurs.
Exemple
Le paramtre nom_du_paramtre est ladresse dune valeur de type_point.
Pourquoi faire cette distinction ici ? Tout simplement parce quun argument fourni une fonction
lors de son appel est une expression, et non une variable : cela signifie, entre autres, que lors
de lappel une fonction dont un paramtre est un pointeur, largument associ ne devra pas
obligatoirement tre un pointeur, mais tout simplement une adresse.
Il pourra donc sagir : de ladresse dune variable existante ou dune adresse stocke dans un pointeur.

28

1.9. Cration de types par le programmeur : les types composs ou structures

Les donnes modifies


Les paramtres dune fonction sont une copie des arguments. Ainsi, la modification de la valeur
dun paramtre naura aucune influence sur la valeur de largument. Une fonction ne peut pas
modifier la valeur des arguments qui lui sont transmis. Cependant, il est parfois intressant quune
fonction puisse modifier une variable de la fonction qui lappelle. Pour ce faire, la technique consiste
transmettre la fonction ladresse de la variable modifier. Ainsi, la fonction rcupre ladresse
de la variable et accde sa valeur grce loprateur * (contenu de). La notation donnes
modifies 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 (dfini dans la fonction) ;
une fonction peut retourner une allocation obtenue par la fonction RESERVER().

1.9 CRATION DE TYPES PAR LE PROGRAMMEUR : LES TYPES

Dunod La photocopie non autorise est un dlit

COMPOSS OU STRUCTURES
linstar des tableaux permettant de regrouper des variables de mme type, il existe la possibilit
de regrouper des variables de types quelconques au sein de types cres par le programmeur en
fonction de ses besoins. En effet, les types de base que sont rel, entier, caractre et pointeur
(adresses) sont souvent trs limitatifs lorsque le programmeur souhaite dvelopper des applications
professionnelles. Sans entrer dans les dtails de lutilisation prcise de ces nouveaux types, il est
possible de dire que ces types sont issus dun travail de modlisation, travail dont le but est de
choisir, pour un objet du monde rel qui doit tre manipul dans une application informatique, la
liste des proprits (valeurs) qui vont reprsenter cet objet.
Exemple
Lobjet personne du monde rel possde normment de proprits (vous pourriez en dresser
une liste trs longue); mais toutes ne sont pas forcment adaptes pour une application prcise.
Un logiciel de gestion de compte bancaire naura par exemple pas besoin de connatre la taille, le
poids, la couleur des cheveux ni le plat prfr dune personne.

Pour dfinir un type compos, il faut en premier lieu lui choisir un nom, puis dresser la liste de
toutes les proprits, galement appeles champs, que lon souhaite stocker lintrieur de ce type.
Chacun des champs est identifi par un nom, ce qui permet au programmeur de le choisir parmi
ceux qui sont stocks dans le type compos, et dun type, pour que lordinateur sache le manipuler.
29

Chapitre 1 Les bases de la programmation

Par convention de nommage, un nom de type compos doit systmatiquement commencer par
t_.

Pour dfinir un type compos, aussi appel structure, on utilise simplement la syntaxe suivante :
STRUCTURE nom_du_type
nom_champ_1 : type_champ_1
...
nom_champ_n : type_champ_n
Le mot STRUCTURE permet de dfinir un nouveau type, et non pas une variable.

Dfinition 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 dfini, il est utilisable dans un programme presque au mme titre que
les types de base de lalgorithmique, qui sont entier, rel et caractre. Il est notamment possible de
dfinir des variables dont le type est un type compos ou structure, en utilisant la syntaxe classique
de dfinition dune variable :
VAR nom_de_variable : type
VAR var_comp : t_complexe est une dfinition de variable tout fait correcte, et dont la
signification est : la variable nomme var_comp est de type t_complexe.

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 dfinition de variable suivante : VAR z : t_complexe
Afin 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 dfini 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 dfinies, 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.

Dunod La photocopie non autorise est un dlit

1.9.4 Structures dfinies 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 dfinie 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 dfini laide du type t_date.

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.
Afin 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 dfinition 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 fixe (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
dfinir 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). Dfinir 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 dfini de la manire suivante :
VAR *p_tot : t_complexe
Son initialisation est la suivante :
RESERVER(p_tot)
Cest donc le contenu de la zone mmoire pointe par p_tot et not *p_tot, qui est de type
t_complexe (regardez la dfinition du pointeur p_tot, elle ne dit pas autre chose). Il est alors
possible daccder ses champs comme pour nimporte quelle autre variable de ce type, laide
des notations classiques.
(*p_tot).re une valeur
(*p_tot).im une valeur

La notation (flche)
Dernier oprateur abord pour ce qui concerne les structures, loprateur nest pas proprement parler indispensable, son rle est de procurer une syntaxe plus intuitive que le couple * et
. voqu dans le paragraphe prcdent. Lutilisation de cet oprateur est des plus simples.
Exemple
Soit ptr un pointeur vers une variable de type structure, et soit ch un des champs de cette variable.
Dans ce cas, deux critures sont quivalentes :
(*ptr).ch

ptrch
Ainsi, lexemple prcdent peut tre crit :
p_totim une valeur
p_totim une valeur

1.9.6 Types pointeurs et raccourcis de notation

Dunod La photocopie non autorise est un dlit

Il est parfois pratique de disposer dun nom de type raccourci pour dsigner un pointeur vers une
structure. Ce nom raccourci de type se dfinit avec la syntaxe suivante :
TYPE *type_pointeur : type

Exemple :
TYPE *ptr_comp : t_complexe

Le raccourci de type prcdent signifie que le type ptr_comp dsigne un pointeur vers un
t_complexe. Ainsi, les deux dfinitions de variables suivantes sont quivalentes :
VAR *pz : t_complexe
VAR pz : ptr_comp

Dans les deux cas, la variable pz stocke ladresse dune valeur de type t_complexe.
33

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 spcifier 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 suffit 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 dfini : 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

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 Dfinition
Une liste linaire est une structure de donnes correspondant une suite dlments. Les lments
ne sont pas indexs dans la liste, mais pour chaque lment (sauf le dernier) on sait o se trouve
llment suivant. Par consquent, on ne peut accder un lment quen passant par le premier
lment de la liste et en parcourant tous les lments jusqu ce quon atteigne llment recherch.
2.1.2 Reprsentation
Reprsentation tabulaire
On peut reprsenter une liste par deux tableaux :
le tableau 2.1 contient les lments de la liste, dans un ordre quelconque.
le tableau 2.2 est organis de faon suivante : si la case dindice i du premier tableau contient
llment dont le suivant se trouve dans la case dindice j, alors la case dindice i de second
tableau contient lentier j.
On peut extraire les lments du premier tableau dans lordre alphabtique si on connat le
point de dpart : 1 dans notre cas. Ce point de dpart doit videmment tre prcis lentre de
35

Chapitre 2 Structures squentielles simples

Tableau 2.1
0

\0

Tableau 2.2
3
0

5
2

2
3

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 fin.
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

\0
4

Tableau 2.4
2
0

3
1

5
2

0
3

Cette reprsentation nest pas trs intressante : les fonctions de traitement sont relativement
lourdes.

Reprsentation chane
On utilise des pointeurs pour chaner entre eux les lments successifs, et la liste est alors dtermine
par ladresse de son premier lment. On va dfinir des enregistrements (structures) dont un des
champs est de type pointeur vers une structure chane du mme type.
Exemple de dfinition dune structure chane
Pseudo code formel

STRUCTURE nud
nom : caractre[20]
prenom : caractre[20]
*suiv : nud
TYPE *ptr_nud : nud
Implantation C

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 suppression dun lment une place quelconque, la concatnation de deux listes, se font par une simple
manipulation des pointeurs. Cependant le calcul de la longueur ncessite le parcours de toute la
liste ; de mme, laccs au kime lment nest plus direct : on doit parcourir k1 pointeurs partir
de la tte pour trouver le kime lment.
Avant de passer aux exemples de fonctions qui traitent les listes chanes il faut faire un petit
rappel sur les variables dynamiques.
2.1.3 Variables dynamiques

Cest une variable dont la place mmoire est alloue en cours dexcution.
On ne prend de la place mmoire que lorsquon en a besoin.
Cette place mmoire est alloue explicitement, cest--dire par une instruction du langage.
La dsallocation est galement effectue par lintermdiaire dune instruction.
Il nest donc pas ncessaire de rserver ds la compilation tout un espace mmoire.
On utilise la place juste lorsquon en a besoin, puis on peut la rutiliser par autre chose.

Exemples
Les exemples sont toujours donns en langage algorithmique.
Les donnes, les donnes modifies et les sorties sont systmatiquement prcises en prambule
des dclarations.
titre dillustration, une ralisation (ou implantation, ou implmentation) est donne en C99
pour chacun des types abstraits et algorithmes proposs.

37

Chapitre 2 Structures squentielles simples

Dfinition 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


Spcification 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 6= k parce
que si l->succ pointe sur NULL, l->succ->content nexiste pas.

Une alternative sans variable boolenne


Spcification de lalgorithme recherche (b)

Dunod La photocopie non autorise est un dlit

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;

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 fin
Spcification 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 6= fin ALORS
cons(e, l)
FINSI
FIN
Alternative

DEBUT
l listevide()
FAIRE
SAISIR e
SI e 6= fin ALORS
cons(e, l)
FINSI
TANTQUE e =
6 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

Dunod La photocopie non autorise est un dlit

Spcification 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 =
6 listevide() FAIRE
{ cour pointe sur celui supprimer, }
{ prec pointe sur le prcdent }
SI contenu(cour) = x ALORS
ok vrai

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

Dunod La photocopie non autorise est un dlit

Il est parfois commode de dfinir une liste non pas comme un pointeur sur une cellule, mais par un
bloc de cellules du mme type que les autres cellules de la liste. Ce bloc ne contient que ladresse
du premier lment de la liste. Lutilisation dun tel bloc permet dviter un traitement spcial pour
linsertion et la suppression en dbut de liste.

Figure 2.2

Liste (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.
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

Dfinition 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

Afficher le contenu dune liste lenvers


Spcification 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

Dunod La photocopie non autorise est un dlit

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 L1 et L2 dont les lments sont des entiers. crire un algorithme
qui rattache la liste L2 la suite de la liste L1 . Tous les cas particuliers doivent tre pris en compte.
45

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 kime position (cest--dire k maillons dans la premire liste de sortie et le reste
dans la seconde) ;
D juste devant le maillon contenant la valeur minimale de la liste ;
E telles que les deux listes de sortie aient mme longueur n, ou que la premire soit de longueur
n+1 et la seconde de longueur n.
46

Problme

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 coefficients et les exposants dun polynme dans une liste chane. valuer ce
polynme pour une valeur donne de x.
Utiliser au moins deux modules dans ce programme :
lun pour construire la liste sur la base des coefficients et des exposants, qui sont saisis au clavier ;
et le second afin dvaluer le polynme pour la valeur x, galement saisie au clavier.

Corrigs des exercices et des problmes


EN

PRAMBULE

Dunod La photocopie non autorise est un dlit

Pour la ralisation en C de tous les algorithmes spcifis ci-dessous, on dfinit la structure de liste
chane suivante dont on prcisera au cas pas cas, le type <element>. Les structures utilises pour
les spcifications des algorithmes portent le mme nom et sont construites de la mme manire.
typedef struct listNode
{
<element> content;
struct place *succ; // listes simplement chanes
struct place *prev; // listes doublement chanes
} listNode;
typedef listNode* list;
typedef list* plist;
#define ERR -1 // code derreur (lisibilit des exemples)

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
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

Enfin, le constructeur suivant pourra vous aider simplifier vos critures pour lallocation dun
nouveau nud de liste :
list newSingletonList(int content)
{
list l = malloc(sizeof(listNode));
l->content = content;
l->succ = NULL;
return l;
}

// allocation mmoire
// affectation du contenu

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


Spcification de lalgorithme itratif

Dunod La photocopie non autorise est un dlit

Cest une simple adaptation de lalgorithme recherche (b) du rappel de cours vu plus haut.
Entre : liste de positifs (non vide)
Sortie : un positif
FONCTION maxOf(l: liste<positif> 6= ): positif
VAR max: positif, p: place
DEBUT
p tte(l)
max contenu(p)
TANTQUE dernier(p) FAIRE
p succ(p)
SI contenu(p) > max ALORS
max contenu(p)
FINSI
FAIT
RETOURNER max
FIN
49

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;
}

Spcification de lalgorithme rcursif

Entre : liste de positifs (non vide)


Sortie : un positif
FONCTION maxOf(l: liste<positif> 6= ): 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 :

Dunod La photocopie non autorise est un dlit

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 :
l1 et l2 vides,
l1 vide mais pas l2 ,
l2 vide mais pas l1 ,
ni l1 , ni l2 vides.
Si l2 est vide, on laisse l1 inchange. Si l1 est vide mais pas l2 , on affecte directement l2 l1 . Si ni
l1 ni l2 ne sont vides, on parcourt l1 jusqu sa dernire cellule et on y rattache l2 .
51

Chapitre 2 Structures squentielles simples

Figure 2.5

Concatnation standard

Enfin, si on va un peu trop vite dans ltude, on peut oublier le cas l1 = l2 (cest--dire mme liste
et non pas, listes dont les valeurs sont gales), cas particulier qui croise le premier et le dernier des
quatre cas. Ce cas donne une bonne illustration de la diffrence entre copies et rfrences.

Figure 2.6

Concatnation circulaire

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).
Spcification de lalgorithme

Entres modifies : deux listes dentiers


FONCTION concat(l1 , l2 : liste<entier>)
VAR p1 , p2 : place
DEBUT
SI estvide(l1 ) ALORS
SI estvide(l2 ) ALORS
l1 l2
SINON
52

Corrigs des exercices

p1 tte(l1 )
TANTQUE dernier(p1 ) FAIRE
p1 succ(p1 )
FAIT
succ(p1 ) tte(l2 )
FINSI
FINSI
FIN

Ralisation en C
void concat(list *pl1, list *pl2)
{
if (*pl2 == NULL) return; // ras, l1 soit nulle ou non
if (*pl1 == NULL)
// affectation directe de l2 l1
{
*pl1 = *pl2;
return;
}
// sinon :
list l = *pl1;
while (l->succ != NULL) l = l->succ; // itration l1
l->succ = *pl2;
// rattachement de l2
}

Dunod La photocopie non autorise est un dlit

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
}

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.
Spcification de lalgorithme

Entres modifies :
La liste dentier initiale
Les deux listes dentiers produites
FONCTION separer(l0 , l1 , l2 : liste<entier>)
VAR p0 , p1 , p2 : place
DEBUT
SI estvide(l0 ) ALORS
p0 tte(l0 )
SI contenu(p0 ) > 0 ALORS
p1 tte(l1 ) tte(l0 )
SINON
p2 tte(l2 ) tte(l0 )
FINSI
TANTQUE dernier(p0 ) FAIRE
54

Corrigs des exercices

p0 succ(p0 )
SI contenu(p0 ) > 0 ALORS
SI estvide(l1 ) ALORS
p1 tte(l1 ) p0
SINON
p1 succ(p1 ) p0
FINSI
SINON
SI estvide(l2 ) ALORS
p2 tte(l2 ) p0
SINON
p2 succ(p2 ) p0
FINSI
FINSI
FAIT
FINSI
FIN

Dunod La photocopie non autorise est un dlit

Ralisation en C
void separate(list *pl0, list *pl1, list *pl2)
{
liste l0 = *pl0, l1 = NULL, l2 = NULL;
if (l0 == NULL) return;
// si l0 vide ne rien faire
if (l0->content >= 0) l1 = *pl1 = *pl0; // tte de l1
else l2 = *pl2 = *pl0;
// tte de l2
while (l0->succ != NULL) // itration de l0
{
l0 = l0->succ;
// itration de l0
if (l0->content >= 0)
// transfert vers l1
{
if (l1 == NULL) *pl1 = l1 = l0; // tte de l1
else
// au-del de la tte
{
l1->succ = l0;
// complte l1
l1 = l1->succ;
// itration de l1
}
}
else
// transfert vers l2
{
if (l2 == NULL) *pl2 = l2 = l0; // tte de l2
else
// au-del de la tte
{
l2->succ = l0;
// complte l2
55

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 didentifier 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 fin de liste).

Les deux pointeurs identifient des places contiges (cf. figure 2.9 ce cas est dangereux si trait
comme prcdemment).

Figure 2.9
56

... avec son exception dangereuse en labsence de traitement spcifique

Corrigs des exercices

Dunod La photocopie non autorise est un dlit

Spcification 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
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

Dunod La photocopie non autorise est un dlit

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;
}

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.

Dunod La photocopie non autorise est un dlit

Algorithme rcursif
Retrait de toutes les occurrences dun lment

Entre : x, lentier dont on supprime toutes les occurrences dans la liste.


Entre modifi : 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 dfini 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
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 kime

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 modifies : 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

Dunod La photocopie non autorise est un dlit

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
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 =
6 NULL FAIRE
succ currsucc
currsucc prev
prev curr
curr succ
FAIT
currsucc prev
*pl curr
FIN
// Inversion dune liste (utilise pour lexo 1.4 (normalisation))
void reverse(list *pl)
{
if (*pl == NULL) return;
// liste vide
if ((*pl)->succ == NULL) return; // singleton
list prev = *pl;
// tte
list curr = prev->succ;
// 2d lment
if (curr->succ == NULL)
// si cas 2 lments
{
curr->succ = prev;
// pointage du 2d lt sur le 1er
prev->succ = NULL;
// et du 1er sur NULL (queue)
*pl = curr;
// le 2d devient tte
return;
// termin
}
else
// sinon, au moins 3 elts :
{
prev->succ = NULL;
// pointage du 1er ->NULL (queue)
}
list succ;
while (curr->succ != NULL)
// Expl. sur 1re itration
{
succ = curr->succ;
// sauv. du succ (le 3me lt)

64

Corrigs des exercices

curr->succ = prev;
prev = curr;
curr = succ;

// pointage du 2d elt sur le 1er


// le nouveau prev cest le 2d
// le nouveau curr, cest le 3me

}
// si par exemple le 3me na pas de successeur :
// cest la queue (pas dautre passage dans la boucle) :
curr->succ = prev;
// pointage du 3me sur le 2d
*pl = curr;
// le 3me (dernier) devient tte
}

Dunod La photocopie non autorise est un dlit

Algorithme rcursif
FONCTION esreverse(*pl : list) : list*
VAR head : list
DEBUT
SI *pl=NULL ALORS
// cas de la liste vide
RETOURNER
FINSI
SI (*pl) succ = NULL ALORS // cas de la liste un seul lment
RETOURNER
FINSI
head *pl
*esreverse(&(headsucc)) succ head
*pl headsucc
headsucc NULL
RETOURNER &head
FIN
// reverse2 en mthode rcursive
list * esreverse(list* pl)
{
if (*pl == NULL) return;
// cas liste vide
if ((*pl)->succ == NULL) return pl; // cas singleton
// autres cas :
list head = *pl;
(*esreverse(&(head->succ)))->succ = head;
*pl = head->succ;
head->succ = NULL;
return &head;
}

65

Chapitre 2 Structures squentielles simples

Exercice 2.7 Construire une liste partir dune source de donnes


Cration depuis un tableau dentiers
Spcification 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 1re 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


Spcification 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 =
6 NULL) ET (lsucc 6= 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 6= NULL) ALORS
copiesuccprev copie
FINSI
copie copiesucc
FAIT
SI (lsucc 6= NULL) ALORS
copiesucc copie_tte
FINSI
RETOURNER copie_tte
FIN

Dunod La photocopie non autorise est un dlit

Implantation C

list duplicate(list l)
{
if (l == NULL) return NULL;
list head = l;
list dupl = newSingletonList(l->content);
list dupl_head = dupl;
while (l->succ != NULL && l->succ != head)
{
l = l->succ;
// itration de la liste de rfrence
dupl->succ = newSingletonList(l->content);
if (l->prev != NULL) dupl->succ->prev = dupl;
dupl = dupl->succ; // itration de la liste copie
}

67

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


Spcification

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;
l->succ = *pl;
}

// itration de la liste

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 difficult de cet exercice tient galement au problme du double rsultat quil sagit de retourner.
Plusieurs solutions sont possibles, la plus simple consistant utiliser deux entres modifies.
On utilisera un code de statut pour rendre compte des rsultats spciaux vs. standard dont les
valeurs signifient les configurations suivantes :
4 : au moins un positif et au moins un ngatif.
2 : au moins un positif mais aucun ngatif.
1 : au moins un ngatif mais aucun positif.
0 : ni ngatif, ni positif : liste vide ou de zros.

Dunod La photocopie non autorise est un dlit

Spcification 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
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

Dunod La photocopie non autorise est un dlit

tude

Dans les cinq cas de figure, il sagit de vrifier lexistence dune position de coupe dans la chane,
et le cas chant deffectuer celle-ci, puis de savoir retourner les deux chanes.
Le cas vacuer en dbut de fonction est celui de la liste vide.
Ensuite, nous devons, respectivement pour les cinq cas :
A vrifier que le pointeur dsigne bien un maillon de la liste ;
B vrifier que la liste contient bien la donne x ;
C vrifier que la liste contient au moins k lments ;
D identifier la valeur minimale de la liste ;
E mesurer la longueur de la liste.
Les cas A, B et C sont trs similaires et doivent intgrer les cas de tte et de queue de liste.
Les cas D, E ncessitent un premier parcours complet de la liste.
Tous les cas sauf le cas E doivent intgrer la possibilit dune coupure devant la tte de liste.
Le cas E revient quasiment au cas B aprs lidentification du minimum de la liste.
Dans tous les cas, il est pertinent de retourner un statut dexcution qui indique si lopration a t
effective ou bien transparente (auquel cas, il ny a pas eu production dune seconde liste).
Il peut y avoir erreur si lun ou lautre des deux pointeurs sur les listes modifier est NULL ou bien
dans le cas particulier du A (B et C sont discutables) si le pointeur donn pointe sur une autre liste.
Comme nous sommes scrupuleux, nous indiquons galement par le statut dexcution si la seconde
liste ntait pas initialise NULL avant lopration effective (mise en garde : crasement dune
ancienne valeur).
Implantation C
Schma commun

Donnes modifies : l1 , la liste dentre coupe en deux qui devient la liste


amont, et l2 la liste aval ventuellement produite en sortie.
Rsulat : un entier comme statut dexcution
-1 = ERR :ERREUR,
71

Chapitre 2 Structures squentielles simples

0
: opration blanche,
1
: une coupe a t effectue,
2
: statut 1 + l2 ntait pas nulle.
int cut<Version>(plist pl1, plist pl2, ...)
{
if (pl1 == NULL || pl2 == NULL) return ERR;
list l1 = *pl1;
if (l1 == NULL) return 0;
// on fixe le statut par dfaut selon que *pl2 est NULL ou non
int status = *pl2 == NULL ? 1 : 2;
/// CODE SPECIFIQUE <Version> ///
// on coupe
*pl2 = l1->succ;
l1->succ = NULL;
return status;
}
Cas du slecteur pointeur

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 ///
}

Dunod La photocopie non autorise est un dlit

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 :
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 ///
}

Spcification abstraite

Une fois nest pas coutume (petit mensonge dingnieur), nous procdons une rtro conception
du principe algorithmique gnral partir des cinq cas tudis et raliss prcdemment.
Donnes modifies : l1 , la liste dentre coupe en deux qui devient la liste
amont, et l2 la liste aval ventuellement produite en sortie.
Donne : c, un paramtre du critre pour reprer la place qui suit
directement le point de coupe et qui devient donc la tte de l2
Rsultat : un statut dexcution
-1 = ERR :ERREUR,
0 = ID : opration blanche,
1 = OK : une coupe a t effectue,
FONCTION scinder(modif l1 , l2 = : liste; c: critre): statut
VAR p: place
74

Corrigs des exercices

DEBUT
c mettre__jour(c, l1 )
SI estvalide(c) ALORS
RETOURNER ERR
FINSI
SI estvide(l1 ) ALORS
RETOURNER ID
FINSI
p tte(l1 )
SI est_successeur_point_de_coupe(p) ALORS
l2 l1
l1
RETOURNER OK
FINSI
TANTQUE dernier(p) est_successeur_point_de_coupe(succ(p)) FAIRE
p succ(p)
c mettre_ventuellement__jour(c)
FAIT
SI dernier(p) ALORS
RETOURNER ID
SINON
tte(l2 ) succ(p)
succ(p)
RETOURNER OK
FINSI
FIN

Exercice 2.11 Supprimer un sous-ensemble dune liste


tude

Cet exercice avec ses cinq cas de figure est une extension du principe algorithmique vu dans
lexercice 2.5 (supprimer des lments). La faon de dfinir le critre de slection des nuds
supprimer change selon les cas, mais le schma gnral ne change pas.
Dans tous les cas, il sagit de traiter spcialement la chane prfixe (de tte) constitu dune
succession de nuds liminer, puis sil reste au moins un lment, dliminer tous les lments
rpondant au critre et situs au-del de la tte.
Le cas A donne lieu une version simplifie de lalgorithme gnral, laquelle profite plein de la
rgularit des intervalles de suppression.
Schma gnral pour les cas B E
La mthode gnrale de rsolution reste la mme, seul le critre de choix des lments supprimer
change. Pour illustrer ce fait, dans la partie Spcification de lalgorithme, le second paramtre
de la fonction removeAll est en ralit une fonction nomme critre. Cette fonction critre a pour
paramtre un entier (la valeur extraite de la liste) et pour sortie un boolen indiquant si la valeur
75

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 effectuer la suppression pour les cas D et E.
Spcification 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 =
6 NULL) ET (critre(lcontent,...)=vrai) FAIRE
suppr l
l supprsucc
LIBERER(suppr)
FAIT
*pl l
// suite de la liste
TANTQUE (l 6= NULL) FAIRE
TANTQUE (lsucc =
6 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 figure B, C, D, E :

Dunod La photocopie non autorise est un dlit

Spcification 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)
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 figure A, du fait de la rgularit des intervalles de suppression conduit une version
simplifie du schma gnral prcdent. On limine notamment un niveau dimbrication de boucles
tantque, traduction du fait quil nexiste pas de chane de plusieurs lments directement successifs
supprimer.

78

Corrigs des exercices

Spcification 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 =
6 NULL) ET (lsucc 6= NULL) FAIRE
suppr lsucc
lsucc suppr succ
LIBERER(suppr)
l lsucc
FAIT
RETOURNER 1
FIN

Dunod La photocopie non autorise est un dlit

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;

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 daffichage 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

Dunod La photocopie non autorise est un dlit

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


Spcification de lalgorithme

FONCTION normalizePolynomial(*pp : polynome)


VAR ???
DEBUT
???
FIN
81

Chapitre 2 Structures squentielles simples

Implantation C

void normalizePolynomial(polynomial* pp)


{
A VOUS DE JOUER !!
}

valuation du polynme (instanciation)


Spcification 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 =
6 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

Affichage du polynme
Spcification de lalgorithme

FONCTION printPolynomial(p : polynome)


DEBUT
SI (p = NULL) ALORS
RETOURNER
FINSI
printTerm(p)
TANTQUE (psuivant 6= 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("]");
}

Dunod La photocopie non autorise est un dlit

Affichage dun terme du polynme


Spcification 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
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

Dunod La photocopie non autorise est un dlit

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");
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

STRUCTURES

SQUENTIELLES
COMPLEXES

RAPPELS DE COURS

3.1 PILES
Pour beaucoup dapplications, les seules oprations effectuer sur les listes sont des insertions et
des suppressions aux extrmits. Dans les piles les insertions et les suppressions se font une seule
extrmit, appele sommet de pile.
Les piles sont aussi appeles LIFO, pour Last-In-First-Out, cest--dire dernier entr, premier
sorti ; une bonne image pour reprsenter une pile est une ... pile dassiettes : cest en haut de la pile
quil faut prendre ou mettre une assiette !
Les oprations sur les piles sont : tester si une pile est vide, accder au sommet dune pile,
empiler un lment, retirer llment qui se trouve au sommet (dpiler).
On peut utiliser pour implmenter les piles toutes les reprsentations possibles pour les listes.
On va parler en dtail dune reprsentation chane, mais il faut comprendre que la reprsentation
contigu est tout fait possible.
3.1.1 Reprsentation contigu des piles
Les lments de la pile sont rangs dans un tableau, et lon conserve aussi lindice du sommet de
pile.
Dfinition dune structure de pile ralise avec un tableau
Pseudo code formel

STRUCTURE tStack
content : T[n]
top : entier
Implantation C

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

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 dfinition 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 modifie : 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

Dunod La photocopie non autorise est un dlit

Retirer un lment du sommet de la pile (dpiler)


Dclarations
Donne modifie : 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,

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 file on fait les adjonctions une extrmit, les accs et les suppressions lautre
extrmit. Par analogie avec les files dattente on dit que llment prsent depuis le plus longtemps
est le premier, on dit aussi quil est en tte.
Les files sont aussi appeles FIFO pour First-In-First-Out, cest--dire premier entr, premier
sorti.
Les oprations sur les files sont :
tester si la file est vide ;
accder au premier lment de la file ;
ajouter un lment dans la file ;
retirer le premier lment de la file.
3.2.1 Reprsentation contigu des files
Dans ce cas on doit conserver lindice i du premier lment et lindice j de la premire case libre
aprs la file. On fait progresser ces indices modulo la taille lmax du tableau. Le seul point dlicat
est la dtection des dbordements.
Dfinition dune structure de file 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 files

3.2.2 Reprsentation chane des files


Dans le cas dune reprsentation chane, soit on a deux pointeurs, tte et dernier, vers le premier
et le dernier lment de la file, 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 file (mthode avec deux pointeurs)
Dfinition dune structure de file 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

Dunod La photocopie non autorise est un dlit

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 file 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

91

Chapitre 3 Structures squentielles complexes

Tester si la file est vide


Dclarations
Donne : file f
Rsultat : de type boolen
En-tte en C : int file_vide (file f)
Algorithme file 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 fin de la file (enfiler)


Dclarations
Donne : T elt__enf
Donne modifie : file *ff
En-tte C : void enfiler(T elt_a_enf, file *ff);
Algorithme enfiler

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 file (dfiler)


Dclarations
Donne modifie : file *ff, T *elt_def
Rsultat : de type boolen
En-tte : int defiler(file* ff, T *elt_dep);

92

3.2. Les files

Algorithme dfiler

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 file se reprsente beaucoup mieux en dynamique (reprsentation chane).

Dunod La photocopie non autorise est un dlit

Utilisation des files : tous les cas o lon dispose dune ressource destine tre utilise
par plusieurs programmes, les cas o plusieurs donnes sont manipules par cette ressource.
Exemple : le buffer dimprimante est gr en file dattente.

Exemple de gestion dun buffer clavier en liste circulaire (le dernier poste dune
liste circulaire est rattach au premier)
On a un ensemble des donnes homognes gres sur un principe de lecture/criture concurrente :
lecture et criture peuvent se produire de manire simultane mais sont conditionnes par lautre
opration.
Une lecture ne peut tre faite que si une criture a dj t faite, mais une criture ne peut tre faite
que si la lecture a t effectue sous peine de perdre linformation. Plus gnralement : il sagit dune
file dattente particulire o lon na pas besoin de produire toutes les donnes pour commencer les
consommer. Le nombre de postes est fix au dpart et ne varie plus au cours du programme.
Il faut crire deux algorithmes qui utilisent respectivement un pointeur de lecture et un pointeur
dcriture :
Lire : cest prendre linformation rfrence par lecteur, condition que cela soit possible et
dplacer le pointeur de lecture sur le poste suivant.
93

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 files

Figure 3.3

Lecture et criture simultanes

Modle statique
Pseudo code formel

STRUCTURE elt
info : T
lire : boolen

Dunod La photocopie non autorise est un dlit

Implantation C

typedef struct elt {


T info;
boolen lire;
} elt;

lire est un flag (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

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 modifie : 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 modifie : T *elt__lire, liste_circ *l
Rsultat de type boolen
En-tte : int lecture(liste_circ *l, T *elt__lire)
FONCTION lecture(*l : liste_circ, *elt__lire : T)
VAR ok : boolen
DEBUT
SI ldonne[llecteur].lire = faux ALORS
ok faux
SINON
ok vrai
*elt__lire ldonne[llecteur].info
ldonne[llecteur].lire faux
llecteur (llecteur + 1) mod n
FINSI
RETOURNER ok
FIN

96

3.2. Les files

criture
Donne : int n, T elt_ecr
Donne modifie : liste_circ *l
Rsultat de type boolen
En-tte : int criture(T elt_ecr, liste_circ *l);
FONCTION criture(elt_ecr : T, *l : liste_circ) : boolen
VAR ok : boolen
DEBUT
SI ldonne[lecrivain].lire = vrai ALORS
ok faux
SINON
ok vrai
ldonne[lecrivain].info elt_ecr
ldonne[lecrivain].lire vrai
lecrivain (lecrivain + 1) mod n
FINSI
RETOURNER ok
FIN

Modle dynamique
Pseudo code formel

Dunod La photocopie non autorise est un dlit

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;
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 Afficher une liste chane

****

crire un algorithme pour afficher une liste chane dentiers sous la forme suivante :
Si la liste est linaire :
Nom_liste: .->[elt1 ]->[elt2 ]->...->[eltn ]->[eltlast ] |

Si la liste est circulaire :


Nom_liste: .->[elt1 ]->[elt2 ]->...->[eltn ]->[eltlast ] ...

Si certains maillons contigus sont doublement chans, afficher ]<->[ au lieu de ]->[.
Apportez une attention particulire aux traitements spcifiques de dbut, milieu et fin de liste ainsi
quaux cas spciaux comme la liste vide ou singleton.

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 file

**

Concevoir deux algorithmes, qui crent respectivement :


la file inverse dune file ;
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 kimes nuds de la liste, en effectuant un
parcours circulaire dans celle-ci et jusqu ce que tous les nuds aient t supprims. Ds quun
nud est supprim, le suivant dans la boucle est considr comme la nouvelle tte de liste et ainsi
de suite. Si n = 8 et k = 3, par exemple, la suppression des nuds contenant les huit entiers se fait
dans cet ordre :
3, 6, 1, 5, 2, 8, 4, 7
(Ce procd peut tre illustr par un groupe compos lorigine de n personnes formant un cercle,
que lon limine successivement en dsignant le kime de ceux restant dans le cercle que lon
continue de parcourir).
Problme 3.2 Mesurer la longueur dune liste de type Heqat

*****

Dunod La photocopie non autorise est un dlit

Soit une liste simplement chane de type Heqat, cest--dire obtenue par lopration concat(&l,
sublist(&l, n)), concevoir un algorithme pour en mesurer la longueur en nombre de nuds, qui
retourne dune part la longueur de la partie amont linaire, et dautre part la longueur (primtre)
de la partie circulaire en aval.
Vous devrez respecter les contraintes suivantes :
pas de modification de la liste pendant le traitement (lecture seule) ;
pas de copie de linformation des nuds traverss.

Corrigs des exercices et des problmes


PRAMBULE
Reprsentation dune structure de file laide dune liste
Pseudo code formel

STRUCTURE lQueue
head : list
queue : list
length : entier
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 Afficher une liste chane

Dunod La photocopie non autorise est un dlit

Spcification 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 6= NULL) ET (lsucc 6= 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
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 finaliser en la refermant sur elle-mme avec un
concat(&l, &l).
Il sagit de raliser un tri :
Soit en triant avant ou aprs lappel au constructeur (mthode en deux temps) :
soit tri du tableau avant dappeler ce constructeur,
soit tri de la liste aprs lavoir construite.
Soit en triant au fur et mesure de la construction de la liste (mthode en flux tendu) :

soit au moment de choisir le prochain lment insrer en tte de liste (itration non linaire
sur le tableau),
soit au moment de choisir lemplacement dinsertion dans la liste de llment suivant du
tableau (itration linaire du tableau).
Avec une difficult (mais quelle lgance) supplmentaire si on choisit dinsrer dans une liste

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 flux tendu), lune itrative qui parcourt le tableau, et qui pour
chaque tape cre puis insre un nouveau maillon dans la liste, puis se termine en refermant la
chane sur elle-mme, lautre, plus compacte, rcursive, qui travaille sur une liste circulaire initie
avec le premier lment du tableau.
102

Corrigs des exercices

Mthode itrative classique

Dunod La photocopie non autorise est un dlit

Spcification 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 6= 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

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

Dunod La photocopie non autorise est un dlit

Spcification 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 =
6 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
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 effectuer 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

Dunod La photocopie non autorise est un dlit

Spcification de lalgorithme

FONCTION doubleChain(l : list)


VAR tte : list
DEBUT
SI (l = NULL) ALORS
RETOURNER // ceci termine la fonction
FINSI
tte l
TANTQUE (lsucc =
6 NULL) ET (lsucc 6= tte) FAIRE
SI (lsuccprev = NULL) ALORS
lsuccprev l
FINSI
l lsucc
FAIT
SI (lsucc 6= NULL) ALORS
tteprev l
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 file


Inversion de pile
Spcification 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

Dunod La photocopie non autorise est un dlit

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:");
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
Spcification 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


Dunod La photocopie non autorise est un dlit

Spcification 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)
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 difficult.
Pour le reste, il sagit de mettre jour les prdcesseurs en sus des successeurs.
Les problmes dinsertion/suppression en dbut ou fin de listes, vs. en milieu de liste restent peu
prs les mmes quavec une liste simplement chane.
Insertion
Spcification de lalgorithme

// Insertion dans une liste doublement chane


// pl : liste o seffectue linsertion
// place : place dans la liste (on insre juste devant)
112

Dunod La photocopie non autorise est un dlit

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)
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

Dunod La photocopie non autorise est un dlit

Spcification 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 6= NULL ALORS
(*pl)succprev NULL
FINSI
LIBERER(place)
RETOURNER vrai
FINSI
prec placeprev
precsucc placesucc
SI placesucc =
6 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
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

116

Liste Joseph

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

Dunod La photocopie non autorise est un dlit

Pour la finalisation dune liste circulaire doublement chane (reste deux, puis un lment, il
convient dtre prudent) :

Figure 3.6

Finalisation dune LDCC (Liste doublement chane circulaire)

117

Chapitre 3 Structures squentielles complexes

Architecture fonctionnelle

Figure 3.7

Architecture fonctionnelle de PlayJoseph

Fonction principale
Spcification 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 =
6 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

Dunod La photocopie non autorise est un dlit

Spcification 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;
}
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 kime lment


Spcification 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 =
6 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

Dunod La photocopie non autorise est un dlit

Implantation C

// Supprime le kime et retourne le suivant


//
Si le 1er 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)
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

Dunod La photocopie non autorise est un dlit

Spcification 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 =
6 NULL) ET (lsucc 6= tte) FAIRE
l lsucc
longueur longueur+1
SI (lsucc 6= NULL) ET (lsucc = tte) ALORS
*longueurLinaire 0
*longueurCirculaire longueur
RETOURNER longueur
FINSI
t2 tte
longLigne 0
TANTQUE (t2 =
6 l) ET (t2succ 6= NULL) FAIRE
t2 t2succ
longLigne longLigne+1
SI (lsucc =
6 NULL) ET (lsucc = t2) ALORS
*longueurLinaire longLigne
*longueurCirculaire longueur-longLigne
RETOURNER longueur
FINSI
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

Dunod La photocopie non autorise est un dlit

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;
}

125

STRUCTURES

ARBORESCENTES

RAPPELS DE COURS
Un arbre est un ensemble de nuds, organiss de faon hirarchique, partir dun nud
distingu, appel racine. La structure darbre est lune des plus importantes et des plus spcifiques
de linformatique : par exemple, cest sous forme darbre que sont organiss les fichiers dans des
systmes dexploitation tels quUNIX ; cest aussi sous forme darbres que sont reprsents les
programmes traits par un compilateur...
Une proprit intrinsque de la structure darbre est la rcursivit, et les dfinitions des caractristiques des arbres, aussi bien que les algorithmes qui manipulent des arbres scrivent trs
naturellement de manire rcursive.

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 finale contre Paul.

Figure 4.2
Le pedigree dun cheval Zoe ; son pre est Tonnerre et sa mre est Belle ; la mre de Belle est Rose et
le pre de Belle est clair...

127

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 Dfinition
Le vocabulaire concernant les arbres informatiques est souvent emprunt la botanique ou la
gnalogie ; tant donn un arbre B = <o, B1, B2> :
o est la racine de B.
B1 est le sous-arbre gauche (sag) de la racine de B, (ou, plus simplement, le sous-arbre gauche
de B), et B2 est son sous-arbre droit (sad).
On dit que C est un sous-arbre de B si, et seulement si : C = B, ou C = B1, ou C = B2, ou C est
un sous-arbre de B1 ou de B2.
On appelle fils gauche (respectivement fils droit) dun nud la racine de son sous-arbre gauche
(respectivement sous-arbre droit), et lon dit quil y a un lien gauche (respectivement droit) entre
la racine et son fils gauche (respectivement fils droit).
Si un nud ni a pour fils gauche (respectivement droit) un nud nj , on dit que ni est le pre de
nj (chaque nud na quun seul pre).
Deux nuds qui ont le mme pre sont dits frres.
Le nud ni est un ascendant ou un anctre du nud nj si, et seulement si, ni est le pre de nj ,
ou un ascendant du pre de nj ; ni est un descendant de nj si, et seulement si ni est fils de nj , ou
ni est un descendant dun fils de nj .
Tous les nuds dun arbre binaire ont au plus deux fils :
un nud qui a deux fils est appel nud interne ou point double
un nud sans fils est appel nud externe ou feuille.
Donc, un arbre binaire est :
soit vide ;
soit constitu dun lment de type T et dau plus deux arbres binaires.
4.1.2 Reprsentation
La reprsentation la plus naturelle reproduit la dfinition rcursive des arbres binaires. Elle peut
tre ralise en allouant la mmoire soit de faon chane soit de faon contigu.
128

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 sousarbre droit, et larbre est dtermin par ladresse de sa racine. Lorsque larbre est tiquet, on
reprsente dans un champ supplmentaire linformation contenue dans le nud.
Implantation en C

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 fils gauche de a ; a->sad pointe sur le fils droit de a ; a->info permet daccder au
contenu du nud.
On ne parle darbres binaires que par lintermdiaire des algorithmes qui utilisent le type arbre.
4.1.3 Algorithmes de parcours dun arbre binaire
Il y a deux types de parcours :
Parcours en profondeur dabord Examiner compltement un chemin et passer au chemin
suivant tant quil en reste.

Figure 4.4

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 fils. Cela doit tre trait itrativement.

129

Chapitre 4 Structures arborescentes

Figure 4.5

Parcours en largeur ( laide dune file)

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 =
6 NULL ALORS
afficher(ainfo)
prordre(asag)
prordre(asad)
FINSI
FIN

Exemple

Figure 4.6

Parcours en prordre

On affiche : 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 6= 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 affiche : 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 6= NULL ALORS
postordre(asag)
postordre(asad)
afficher(ainfo)
FINSI
FIN

Figure 4.8

Parcours en postordre

On affiche : u m t c o y b a

4.1.4 Arbres binaires de recherche (ABOH = Arbres Binaires Ordonns


Horizontalement)
Dfinitions 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 dfinition, 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 dfinition 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.
Dunod La photocopie non autorise est un dlit

Principe

SI larbre est vide ALORS


fin et chec
SINON
SI llment cherch = lment
fin et russite
SINON
SI llment cherch < lment
rechercher llment dans le
SINON
rechercher llment dans le

point ALORS

point ALORS
sag
sad

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 nime 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 6 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

Dunod La photocopie non autorise est un dlit

On veut ajouter 6 et 12

Figure 4.13
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 modifie : 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 6 x ALORS
ajout(x, &(*aasad))
SINON
ajout(x, &(*aasag))
FINSI
FINSI
FIN

*aa est en donne modifie, donc on gre bien le lien avec la rcursivit.
1) Le mode de transmission par rfrence cre automatiquement le lien entre le pre et le fils
2) Ce qui ne marche pas :
q *aasad
ajout(x, &q)

Dans ce cas le lien est cass, car q est une variable locale. Cest ce quil ne faut surtout pas
faire !!!

Suppression 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

Dunod La photocopie non autorise est un dlit

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
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.
Dfinition dun arbre quilibr

quilibre parfait : pour tout nud de larbre, la valeur absolue de la diffrence entre le nombre
des nuds du sad et le nombre des nuds du sag est infrieure ou gale 1.
|ng nd | 6 1
138

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.
|hg hd | 6 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

Dunod La photocopie non autorise est un dlit

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

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 Afficher le contenu des feuilles dun arbre

**

crire un algorithme permettant dafficher tous les entiers enregistrs dans les feuilles dun arbre
binaire (en ignorant les entiers contenus dans tous les autres nuds).
Exercice 4.3 Rechercher itrativement un lment dans un ABOH

crire un algorithme 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 afficher 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 ncessairement la liste de tous ses lments.
Exercice 4.10 Produire des coupes transversales dun arbre

***

Coupe transversale dun arbre sur un niveau donn, restitue sous forme de liste.
crire un algorithme qui retourne la coupe transversale.

PROBLMES
Problme 4.1 Le triangle Chinois de Pascal

****

Isaac Newton a donn une formule gnrale du dveloppement de la puissance nime dun binme :
n

(x + y) =

n
X

Cnk x nk y k

k=0

o Cnk , le coefficient binomial , alternativement dnot par


Cnk =

n!
= Cnnk
k! (n k)!

n
k

, se calcule par la formule :

Dunod La photocopie non autorise est un dlit

En combinatoire, Cnk dcompte le nombre de combinaisons de k lments parmi n.


Le triangle de Pascal, ou triangle arithmtique Chinois date du XIIIe sicle et na donc pas t
invent par notre Blaise national.
Vous avez certainement dj rencontr cet objet mathmatique qui exploite une mthode de construction par rcurrence pour produire un arrangement gomtrique arborescent des coefficients binomiaux.
k1
k
En effet, il est trivial de constater que : Cnk = Cn1
+ Cn1
avec pour chaque ligne :
Cn0 =

n!
= Cnn = 1
0!n!

C00 =

0!
= C00 = 1
0!0!

et en particulier pour la premire :

Il vous est demand de raliser lalgorithme itratif qui construit le triangle sous forme dun arbre
binaire non quilibr, jusqu un niveau de profondeur n donn, dont la racine contient la valeur
C00 , le fils gauche de la racine C10 , le fils droit de la racine C11 , puis, ds la profondeur n = 2, dont
0
les fils gauche et droit du fils le plus gauche de profondeur n 1 (contenant Cn1
) contiennent
respectivement les valeursCn0 et Cn1 , et dont tous les nuds situs droite de ces deux fils (contenant
k1>0
Cnk>1 ) soient les fils droits de leur pre de niveau n 1 (contenant Cn1
).
143

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 dfinie par :


s0N = N
n N :

144

pair s N  s
n

n+1

 
impair snN sn+1

snN
2
= 3snN + 1

Problmes

Il semblerait que quel que soit N, la suite sN converge vers 1, i.e. :


N N : q N/sqN = 1

Dunod La photocopie non autorise est un dlit

Dans cet interlude, il vous est demand de raliser une fonction rcursive qui construit un arbre de
Syracuse de racine un entier donn e, de profondeur donne q, dont les branches sont toutes les
suites de Syracuse qui convergent vers e en q tapes.
Pour cela, il suffit de procder une inversion de la suite.
Partons de e, le terme de convergence vis, et racine de larbre : e peut tre le successeur direct de
2e, et ventuellement de (e 1)/3 si cet entier existe et sil est impair. Disons que e a ncessairement
un fils droit 2e, et ventuellement un fils gauche (e 1)/3. En appliquant rcursivement ce procd,
on peut donc construire un arbre binaire de profondeur quelconque dont chaque branche est lune
des suites de Syracuse qui converge vers e.

Figure 4.23

Arbre de Syracuse

crire une fonction qui retourne le prdcesseur entier impair dun entier donn sil existe, et 0
sinon.
145

Chapitre 4 Structures arborescentes

crire une fonction rcursive qui construit larbre de Syracuse partir dune feuille donne contenant la valeur e et dvelopp jusqu la profondeur p. Si un fils gauche (e 1)/3 vaut 0 ou 1 une
tape quelconque de la rcursion (i.e. e vaut 1 ou 4), ce fils gauche nest pas cr.

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 spcifis ci-dessous, on dfinit 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 implmentations :
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 afficher le contenu.
Le rsultat produit sur lexemple donn est : 1, 2, 8, 12, 10, 5.

Dunod La photocopie non autorise est un dlit

Spcification 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 afficher 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 fils droit, ni fils gauche.
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 affiche laide dune fonction dimpression de
liste.
Spcification 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


Spcification de lalgorithme

FONCTION containsElement(t : tree, k : entier) : boolen


DEBUT
TANTQUE (t 6= 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 vrifier que la condition fils gauche < parent < fils droit
sur tous les nuds et non pas descendants gauches < parent < descendants droits.
Donnons donc la mauvaise et la bonne solution toutes fins utiles.

Dunod La photocopie non autorise est un dlit

Spcification 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
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


Spcification 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

Dunod La photocopie non autorise est un dlit

tude

Lquilibre partiel dun arbre binaire est dtermin sur la base du diffrentiel des mesures des
hauteurs des fils droit et gauche de la racine.
Par extension, nous proposons une solution qui peut sappuyer sur diffrentes mesures, autres que
la hauteur, pour valuer le statut dquilibre partiel dun arbre dans une varit largie de termes.
Le dnominateur commun de toutes les mesures, est quelles sont calcules selon un procd
rcursif.
Spcification de lalgorithme

/**
* Retourne lcart en valeur absolue entre
* la mesure de la partie droite et celle de la partie gauche
* Le second paramtre utilis indique la mesure utilise
* 1 : masse - 2 : profondeur - 3 : surface
*/
FONCTION getBalance(t : tree, selectedMeasure : entier) : entier
VAR balance : entier
151

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 file 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

Dunod La photocopie non autorise est un dlit

Spcification 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 =
6 NULL) ALORS
sPush(tsag, s)
FINSI
SI (tsad 6= 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 6= NULL) ALORS
qPush(tsag, q)
153

Chapitre 4 Structures arborescentes

FINSI
SI (tsad =
6 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.
Spcification de lalgorithme
Fonction principale simple

/**
* Retourne la moyenne des lments positifs
* et celle des lments ngatifs dun arbre dentiers
* Si larbre est vide, ppo_av et pne_av sont fixs -1,
* Sinon retour respectif des deux moyennes en valeur absolue
*/
FONCTION signedAveragesMain(t : tree, *ppo_av : rel, *pne_av : rel)
VAR po_sum, po_count, ne_sum, ne_count : entier
DEBUT
signedAverages(t, &po_sum, &po_count, &ne_sum, &ne_count)
SI po_count = 0 ALORS
*ppo_av -1
SINON
*ppo_av po_sum / po_count
FINSI
SI ne_count = 0 ALORS
*pne_av -1
SINON
*pne_av ne_sum / ne_count
FINSI
FIN
Fonction secondaire rcursive

/**
* Fonction (prive) rcursive qui calcule respectivement
* les sommes et comptes de positifs et ngatifs
* ppo_sum : somme des positifs, ppo_count : nombre de positifs,
* pne_sum : somme des ngatifs, pne_count : nombre de ngatifs
154

Corrigs des exercices

Dunod La photocopie non autorise est un dlit

*/
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;
}

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

Dunod La photocopie non autorise est un dlit

Analyse

Il sagit dextraire dun arbre tous les fils premiers avec leur parent pour les restituer sous forme de
liste chane. Rien nindique quil sagit dextraire une liste sans rptitions (voir exemple darbre
avec rotation trois lments ci-dessous, dans la section ralisation C/pour tester ).
Nous nous proposons de raliser un algorithme bas sur le calcul du PGCD(parent, enfant) laide
de lalgorithme dEuclide, esprant ainsi obtenir un bonus de la part du correcteur : le PGCD doit
valoir 1 pour caractriser la primalit respective du parent et de lenfant. Mais la simple vrification
de la nullit du modulo de lun par lautre est suffisante pour dterminer cette proprit.
Peu importe le type de parcours choisi (pr, in, ou post fixe), lalgorithme est naturellement
rcursif et bas sur la fusion de proche en proche des listes denfants premiers de chaque sousarbre : les parents des feuilles retournent les premires listes singletons, lesquelles sont enrichies
et concatnes (lopration de fusion) au fur et mesure du dpilement de la pile dappel, et
finalement fusionnes en une seule et unique liste au moment du dpilement final de lappel initial
de la fonction.
157

Chapitre 4 Structures arborescentes

Mthodes de construction

Un arbre vident dont tous les fils sont premiers avec leur parent est larbre qui ne contient que des
nombres premiers. Une autre faon de construire un tel arbre est deffectuer la rptition dun motif
approprie de trois lments premiers entre eux, par exemple 4, 9 et 25 : par exemple, 4 prend la
place de racine, 9 celle de fils gauche, 25 celle de fils droit, puis 4 prend la place de fils gauche de
9 et de 25, et 25 celle de fils droit de 9 et 9 celle de fils droit de 25, et ainsi de suite...
Il existe une multitude de faons de procder la construction dun arbre qui possde cette proprit
de primalit relative parent/enfant.
Cependant, lexactitude mathmatique invite prciser quen ralit, il nexiste aucune faon de
construire un tel arbre qui nexiste pas. En effet, la racine de larbre ne peut tre premire avec son
parent qui nexiste pas.
Spcification de lalgorithme

/**
* Retourne la liste chane des lments dun arbre dentiers,
* premiers avec leur parent
*/
FONCTION getPrimeChildren(t : tree) : list
VAR sagprimechildren, sadprimechildren, primechild : list
DEBUT
SI (t = NULL) ALORS
RETOURNER NULL
FINSI
sagprimechildren NULL
sadprimechildren NULL
SI (tsag =
6 NULL) ALORS
sagprimechildren getPrimeChildren(tsag)
SI (gcd(tcontent, tsagcontent) = 1) ALORS
primechild newSingletonList(tsagcontent)
SI (primechild 6= NULL) ALORS
primechildsucc sagprimechildren
sagprimechildren primechild
FINSI
FINSI
FINSI
SI (tsad 6= NULL) ALORS
sadprimechildren getPrimeChildren(tsad)
SI (gcd(tcontent, tsadcontent) = 1) ALORS
primechild newSingletonList(tsadcontent)
SI (primechild 6= NULL) ALORS
primechildsucc sadprimechildren
sadprimechildren primechild
FINSI
158

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

Dunod La photocopie non autorise est un dlit

Ralisation en C

list getPrimeChildren(tree t)
{
if (t == NULL) return NULL;
list sagprimechildren = NULL;
list sadprimechildren = NULL;
list primechild;
if (t->sag != NULL)
{
sagprimechildren = getPrimeChildren(t->sag);
if (gcd(t->content, t->sag->content) == 1)
{
primechild = newSingletonList(t->sag->content);
if (primechild != NULL)
{
primechild->succ = sagprimechildren;
sagprimechildren = primechild;
}
}
}
if (t->sad != NULL)
{
sadprimechildren = getPrimeChildren(t->sad);
if (gcd(t->content, t->sad->content) == 1)
{
primechild = newSingletonList(t->sad->content);
if (primechild != NULL)
{
primechild->succ = sadprimechildren;
159

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


Spcification 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 filiations croises en filiations simples en supprimant les relations parent-enfant
sur la gauche, sauf pour le ct droit de larbre.

Figure 4.25

162

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 prohibitif.
Ne pas exploiter cette rcurrence est une vritable erreur professionnelle pour un informaticien...
Cest ce qui a justifi un bonus supplmentaire pour les quelques-unes et quelques-uns qui y ont
pens.
Approche rcursive ou itrative ?

Dunod La photocopie non autorise est un dlit

Comme on supprime les filiations gauches, il faut quun parent gauche puisse passer la valeur
du parent droit , non reli, tout nouveau fils droit quil cre.
Si on reste sur une vision arbre , a peut devenir compliqu, en particulier en cherchant une
mthode rcursive.
Or, si lon constate que larbre nest rien dautre quune liste de listes, la premire liste tant la
branche de droite (1, 1, 1, ...), puis la branche (1, 2, 3, ...), puis (1, 3, 6, ...), alors le problme
apparat plus simple et se prte naturellement une procdure itrative deux boucles.
La figure 4.26 (la mme sous une perspective peine diffrente) devrait vous aider vous le
reprsenter.

Figure 4.26
163

Chapitre 4 Structures arborescentes

Spcification 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

Dunod La photocopie non autorise est un dlit

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 :
165

Chapitre 4 Structures arborescentes

Cnk

=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
X

2 (2i 1) (i + 1)

i=1

avec la mauvaise mthode, et un cot de

n
X

2 (i + 1) = (n + 1)(n + 2), avec la bonne.

i=1

Problme 4.2 interlude Syracusien


Fonction de retour du prdcesseur
Le principe

Il sagit de vrifier dans un premier temps que (n 1) est divisible par 3. Cela se vrifie facilement
laide du test (n - 1) % 3 = 0, soit m % 3 = 0 avec m = n - 1.
Mais ce nest pas suffisant, puisquil faut par ailleurs que m/3 soit impair, car sinon, le terme trouv
aurait pour successeur sa moiti et non pas n, cest--dire que son successeur serait m/6 et non pas
n.
Limparit de m/3 peut se vrifier laide de (m/3) % 2 = 1 ou 6= 0, mais si le rsultat invalide
cette imparit, une division aura t effectue pour rien.
Il suffit de constater que si m est la fois multiple de 3, et multiple de 2, alors il est multiple de 6,
et donc m % 6 = 0.
La condition dimparit de m/3 peut donc tre vrifie par m % 6 6= 0. Si les deux conditions sont
valides, alors et seulement alors, on calcule et on retourne m/3.
Cette optimisation est importante, car cette fonction utilitaire est appele chaque tape de la
rcursion ci-aprs, et que son cot dexcution reprsente un cot lmentaire facteur direct du
cot global de lexcution de lalgorithme principal.
Ralisation
/** Retourne le prdcesseur impair de n tel que n = prec * 3 + 1
sil existe 0 sinon **/
FONCTION precInf(n : entier) : entier
VAR m : entier
DEBUT
m n - 1
166

Corrigs des problmes

SI ((m % 3)
RETOURNER
FINSI
SI ((m % 6)
RETOURNER
FINSI
RETOURNER m
FIN

=
6 0) ALORS
0
= 0) ALORS
0
/ 3

Optimisation

Une solution plus efficace peut tre trouve moyennant un petit raisonnement darithmtique
modulaire :
n N : p N : n = 3 p + 1 impair [ p]
!k N : n = 3 (2k + 1) + 1 = 6k + 4
n 4 (6)
Et donc la fonction se rduit :
FONCTION precInf(n : entier) : entier
DEBUT
SI n%6 = 4 ALORS
RETOURNER (n-1)/3
SINON
RETOURNER 0
FINSI
FIN

Dunod La photocopie non autorise est un dlit

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
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

AUTOMATES

RAPPELS DE COURS

5.1 HISTORIQUE
Alan Turing a dcrit 1936 un modle de machine idale auquel il a laiss son nom. Dautres
modles furent proposs, qui sont tous quivalents la machine de Turing. Church a dmontr
dans sa thse que ces modles sont les plus gnraux possibles.
Ces travaux amenrent distinguer entre les fonctions qui sont calculables en thorie de celles
qui ne le seraient mme pas. (Toute fonction calculable peut tre calcule par la machine de Turing
en temps (nombre doprations) fini).
Un modle plus simple mais fort utile est celui dun automate fini. Un automate fini est une
machine abstraite constitue dtats et de transitions. Cette machine est destine traiter des mots
fournis en entre : lautomate passe dtat en tat, suivant les transitions, la lecture de chaque
lettre de lentre. Lautomate est dit fini car il possde un nombre fini dtats distincts : il ne
dispose donc que dune mmoire borne, indpendamment de la taille de la donne sur laquelle on
effectue les calculs.
Un automate fini forme un graphe orient tiquet, dont les tats sont les sommets et les transitions les arcs tiquets.
Les automates finis fournissent un outil de construction dalgorithmes particulirement simple.
Il existe plusieurs types de machines tats finis. Les accepteurs produisent en sortie une
rponse oui ou non , cest--dire quils acceptent (oui) ou rejettent (non) lentre. Les
systmes de reconnaissance classent lentre par catgorie. Les capteurs produisent un certain
rsultat en fonction de lentre.
Les automates finis peuvent caractriser des langages (cest--dire des ensembles de mots) finis
(le cas standard), des langages de mots infinis (automates de Rabin, automates de Bchi), ou encore
divers types darbres (automates darbres).
Les machines tats finis se rencontrent galement dans les circuits digitaux, o lentre, ltat
et le rsultat sont des vecteurs de bits de taille fixe (machines de Moore et machines de Mealy).
Dans les machines de Mealy, les actions (sorties) sont lies aux transitions, tandis que dans les
machines de Moore, les actions sont lies aux tats.

169

Chapitre 5 Automates

5.2 QUELQUES DFINITIONS


Pour dfinir un automate fini, on a besoin des lments suivants :
Un alphabet A qui est un ensemble fini 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 finie, mais il nest pas interdit dutiliser des mots de
longueur infinie (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 fini sur lalphabet A est donn par un quadruplet (Q, I , T , E) o :
Q est un ensemble fini 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 flches ou les
transitions de lautomate.
Remarque
Nous allons temporairement oublier la notion du mot vide, jusqu lintroduction explicite dautomates asynchrones. Dans tous les exemples que nous allons traiter jusque ce point-l, on pourra
donc dfinir 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 flche.
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
(entre)

(sortie)

tat rsultant
en lisant a

en lisant b

Remarque
Si vous regardez cet automate de prs, vous verrez que nous avons libell les tats de telle faon
que lautomate se trouve dans ltat numro i ssi le plus long suffixe du mot dj lu qui est en
mme temps un prfixe de abaa, est de longueur i.

On peut montrer que cet automate ralise de faon optimale lalgorithme de recherche de la
squence abaa dans un texte : il rsout pour ce mot lalgorithme du string matching utilis par
exemple dans les diteurs de textes.

Dunod La photocopie non autorise est un dlit

La lecture
Lautomate prend un mot (constitu de symboles de son alphabet) en entre et dmarre dans son ou
ses tat(s) initial(-aux). Il parcourt ensuite le mot de gauche droite : si lautomate se trouve dans
un tat p et le symbole lire est a, alors sil existe(nt) un ou des tat(s) qi tels que la transition
( p.a.qi ) existe, il passe aux tats qi , et de suite. (Sil y a plus dun tat qi , on considre chacun
deux sparment). La lecture se termine soit si la lettre suivante ne peut pas tre lue (il ny a pas
de transition y correspondant), soit si on a atteint la fin du mot. Si, la fin de la lecture, lautomate
est dans un tat final (accepteur), on dit quil accepte lentre ou quil la reconnat. Autrement, on
dit quil la rejette.
Notation
w

Pour un mot w A on note p q sil existe un chemin de ltat p ltat q obtenu en lisant w.
Dfinitions formelles
a

a A, on a p q ssi il existe une flche ( p.a.q) E.


ua

Ensuite, pour u A et a A on a p q ssi r Q tq p r et r q.


w

Quand on a p q on va dire quil existe un chemin de p q dtiquette w.


171

Chapitre 5 Automates

On peut facilement voir (par rcurrence) que pour u, v A on a :


uv

p q ssi r Q tq p r et r q.
uv

Un mot w A est donc reconnu par lautomate fini sil existe un chemin i t o i I et
t T c.--d. quen partant dun tat initial et quen lisant le mot w, on atteint un tat terminal
la fin de la lecture.
Le langage L reconnu par lautomate fini est lensemble de tous les mots reconnus.

Exemple dexercice type


Construire un automate fini reconnaissant les entiers crits en base deux et divisibles par trois.
Solution
Ajout dun 0 la fin dun nombre binaire le multiplie par 2.
Ajout dun 1 la fin 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 fin

reste

ajout dun 1 la fin

reste

3n

6n

6n + 1 = 3 2n + 1

3n + 1

6n + 2 = 3 2n + 2

6n + 3 = 3 (2n + 1)

3n +2

6n + 4 = 3 (2n + 1 ) + 1

6n + 5 = 3 (2n + 1) + 2

Notons les tats par le reste de la division entire par 3. On obtient :

Tableau 5.3
tat

tat rsultant
0

Figure 5.2
En fait, cet automate est bon pour reconnatre des nombres en criture binaire avec nimporte
quel reste de la division entire par 3, au prix de choisir quel tat est terminal. Ltat terminal
0 correspond au reste 0 (divisibilit par 3) ; ltat terminal 1, au reste 1 ; ltat terminal 2, au
reste 2.

172

5.3. Linterprtation intuitive

5.3.1 Automates dterministes


On distingue des automates finis 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 fini
quelconque, un autre automate qui est dterministe et qui lui est quivalent (reconnat le mme
langage).

Dunod La photocopie non autorise est un dlit

Automate dterministe, dfinition


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 vrifier par rcurrence sur la longueur des mots que pour un automate
u
dterministe C, pour p Q et mot u A , il existe au plus un tat q Q tq p q.
On notera alors p.u = qen convenant que p.u nest pas dfini 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 fini.
Notons P lensemble des parties de Q. Cest un ensemble fini puisque si Q a n lments, P en
a 2n .
Un automate dterministe D correspondant C prend :
comme ensemble dtats un sous-ensemble P de lensemble P des parties de Q,
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 flches 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
(entre)

(sortie)

tats rsultant
en lisant a

en lisant b

(0)

(0,1)

(0)

(0,1)

(0,1)

(0,2)

(0,2)

(0,1,3)

(0)

(0,1,3)

(0,1,4)

(0,2)

(0,1,4)

(0,1)

(0,2)

Explications

On commence par ltat initial qui dans ce cas particulier concide avec ltat initial de lautomate
non dterministe (0), car notre automate non dterministe nen a quun seul. (Sil en avait plusieurs,
on les aurait regroups pour former ltat initial de lautomate dterministe).
partir de cet tat (0), en a on va vers les tats (0), (1) de lautomate initial. Pour lautomate
dterministe, on les regroupe dans ltat quon appelle (0,1). En b, on va vers ltat (0) de lautomate
initial. Donc, (0) est lui aussi un tat de lautomate dterminis.
Chaque fois quon obtient un tat de lautomate dterminis, on le met dans la colonne de gauche
pour agir sur cet tat par les lettres de lalphabet. Ainsi on obtient la deuxime ligne, o figure un
nouvel tat (0,2). On procde ainsi tant quil y reste des tats non traits.
Les tats finaux sont ceux qui contiennent un tat final de lautomate initial. Dans notre cas, il
ny en a quun : (0,1,4).
174

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 (dfinition)


Pour u P et a A , v P tel que u.a = v.
Cest--dire que de chaque tat sortent des flches 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 suffit, dans lautomate dterminis, dintroduire un tat poubelle sil nest pas dj complet.
Exemple

Dunod La photocopie non autorise est un dlit

Figure 5.6
Dterminisation
Tableau 5.5
tat
(entre)
(sortie)

tats rsultant
en lisant a

en lisant b

(0)

(0,1)

(0)

en lisant c

(0,1)

(0,1)

(0)

(2)

(2)

Nous remplaons les traits ( pas de transition ) dans ce tableau par un nouvel tat appel
poubelle (P), qui a la proprit que toutes les transitions partir de cet tat reviennent sur luimme :
175

Chapitre 5 Automates

Tableau 5.6
tat
(entre)
(sortie)

tats rsultant
en lisant a

en lisant b

(0)

(0,1)

(0)

en lisant c
P

(0,1)

(0,1)

(0)

(2)

(2)

Voici donc lautomate dterminis complet, dans lequel (0) est rest ltat initial, et le seul tat
terminal reste (2) :

Figure 5.7

Remarque
Il ny a pas trop de sens de parler dun automate non dterministe complet ou pas complet. De
mme, mme si introduire un tat poubelle dans un automate non dterministe est possible
et ne nuit pas son fonctionnement, ceci nest pas utile pour la dterminisation de cet automate.
Normalement, on nintroduit un tat poubelle si besoin est quune fois lautomate est devenu
dterministe.

Un automate est accessible si nimporte quel tat est accessible partir dun tat initial (c..d.
sil existe un chemin y menant partir dun tat initial).
Un automate est coaccessible si partir de nimporte quel tat on peut accder un tat
terminal.
Accessible + coaccessible = mond.
Un ensemble de mots X est un langage reconnaissable ssi il existe un automate fini D qui le
reconnat.

Si X est un langage reconnaissable sur lalphabet A, on peut le reconnatre avec un automate


dterministe et complet, car lautomate D figurant dans la dfinition 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 6 X ssi i.w 6 T .
176

5.3. Linterprtation intuitive

Le complment dun langage reconnaissable

Le complment dun langage reconnaissable (lensemble de mots sur le mme alphabet nappartenant pas au langage en question) est encore reconnaissable.
Soit D un automate dterministe et complet reconnaissant le langage X .
Pour construire un automate reconnaissant le complment de X , il suffit de prendre comme
ensemble dtats terminaux le complment de lensemble dtats terminaux de D.
Exemple
Construisons un automate sur A = {a, b} qui reconnat lensemble des mots nayant pas aba en
facteur.
On commence par la construction dun automate (non dterministe) reconnaissant tous les mots
ayant aba en facteur.

Figure 5.8
Appliquons lalgorithme de dterminisation :

Tableau 5.7
tat

Dunod La photocopie non autorise est un dlit

(initial)

tats rsultant
en lisant a

en lisant b

(0)

(0,1)

(0)

(0,1)

(0,1)

(0,2)

(0,2)

(0,1,3)

(0)

(terminal)

(0,1,3)

(0,1,3)

(0,2,3)

(terminal)

(0,2,3)

(0,1,3)

(0,3)

(terminal)

(0,3)

(0,1,3)

(0,3)

Figure 5.9
Cet automate dterministe et complet reconnat tous les mots ayant aba en facteur. Il a trois
tats terminaux : (0,1,3), (0,2,3) et (0,3).

177

Chapitre 5 Automates

Maintenant pour obtenir un automate (lui aussi dterministe et complet) reconnaissant tous les
mots qui nont pas aba en facteur, il suffit de faire de tous les tats terminaux, des tats non
terminaux, et vice versa :

Figure 5.10
Ici, on a obtenu un automate complet sans quil y ait besoin de le complter en introduisant
ltat poubelle. videmment, ceci nest pas toujours le cas. Si on a affaire un automate non
complet, on na pas le droit de remplacer directement les tats terminaux par des tats
non terminaux et vice versa : il faut dabord le complter. Alors ltat poubelle (qui ne
peut pas tre terminal) devient un tat terminal de lautomate reconnaissant le complment du
langage.

Minimisation
Pour tout langage reconnaissable il existe le plus petit (c.--d. contenant le plus petit nombre dtats)
automate dterministe qui le reconnat, et il est unique : cest lautomate minimal du langage.
Algorithme de minimisation par la mthode des quivalences

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 facilement 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 fini dterministe
complet D = (Q, i, T , E).
Rsultat obtenir : Un automate fini dterministe complet D qui reconnat le mme langage
que D et qui a aussi peu dtats que possible.
Mthode Construire une partition initiale Q0 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 Qi , commencer par Q0 :


POUR chaque groupe G de Qi 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 Qi ;
/* 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, Qi+1 ;
fin
Si Qi+1 = Qi
alors Q f inal = Qi et continuer ltape 4.
Sinon, rpter ltape (2) avec Qi+1 comme partition courante.

Choisir un tat dans chaque groupe de la partition Q f inal en tant que reprsentant de ce groupe.
Les reprsentants seront les tats de lautomate fini dterministe rduit D .
a

Soit e un tat reprsentatif, et supposons que dans D il y a une transition et.


Soit r le reprsentant du groupe de t (r peut tre gal t).
a

Alors D a une transition de e vers r sur a (er ).


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 inal , soit G est entirement compos dtats de T , soit G nen contient
aucun.
Remarque

Dunod La photocopie non autorise est un dlit

En ralit, il est parfois plus facile, au lieu de choisir un reprsentant, marquer les groupes finaux
comme tels, ou bien les renommer par, par exemple, A, B, C ... , et remplacer dans les transitions
tout tat par le nom de son groupe :
a
a
Si er , e A, r B o les groupes A, B Q f inal , alors dans lautomate minimis on a AB o
maintenant on considre A et B comme tats de lautomate minimis. Cette remarque deviendra
tout fait claire la fin de lexemple suivant.

Exemple 1
Minimisons lautomate dterministe complet :

Figure 5.11

179

Chapitre 5 Automates

dcrit par le tableau de transitions suivant :

Tableau 5.8
tats rsultant

tat
(initial)

(terminal)

en lisant a

en lisant b

Lunique tat terminal est ltat 4.


Donc, la partition initiale : Q0 = {(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

II

Donc, le groupe I se spare en deux, et on a Q1 = {(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 Q1 = {(0,1, 2), (3), (4)}.

Tableau 5.10
tat

Groupes rsultant
en lisant a

en lisant b

II

Donc, le groupe I se spare en deux, et on a Q2 = {(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 Q2 = {(0,2), (1), (3), (4)}.

180

5.3. Linterprtation intuitive

Tableau 5.11
Groupes rsultant

tat

en lisant a

en lisant b

II

II

Le groupe I ne se spare pas, Q3 reste identique la sparation de ltape prcdent :

Q3 = Q2 = {(0,2), (1), (3), (4)}.


Fin de la procdure itrative de sparation.
Voici les itrations quon a effectues :
tape 1 : Qcourant = {(0,1, 2,3), (4)}

Qnew = {(0,1, 2), (3), (4)}


tape 2 : Qcourant = {(0,1, 2), (3), (4)}

Qnew = {(0,2), (1), (3), (4)}


tape 3 : Qcourant = {(0,2), (1), (3), (4)}

Qnew = {(0,2), (1), (3), (4)} = Q f inal


Passons aux reprsentants :

Dunod La photocopie non autorise est un dlit

Tableau 5.12
Groupe

Reprsentant

0 ou 2

II

III

IV

Il devient clair maintenant quon peut marquer les tats de lautomate minimis soit par un
reprsentant, soit par les chiffres I, II, III, IV suivant le groupe du dernier tape, soit par le contenu
du groupe (dans ce cas, ltat initial sera marqu (0,2) ; cette dernire solution est pratique tant
que le groupe consiste en peu dtats).
Lautomate minimis :

Figure 5.12
181

Chapitre 5 Automates

On peut maintenant poser la question suivante : pourquoi les tats 0 et 2 sont rest ensemble
aprs la minimisation ? Quy a-t-il de spcial concernant ces deux tats par rapport aux autres ?
Regardons nouveau lautomate initial :

Figure 5.13
et introduisons la terminologie suivante :
on dit que la chane w distingue ltat s de ltat t si quand on commence dans ltat s et lit w
on arrive dans un tat terminal, et si on commence dans t et lit w on arrive dans un tat non
terminal ou vice-versa.
Ici, tous les tats peuvent tre distingus par une chane ou une autre, sauf les tats 0 et 2. Cest
facile voir : et partir de 0, et partir de 2 on arrive en 1 en lisant un nombre quelconque (y
compris zro) de b et un a ; puis, comme on est dans le mme tat (1), il ne nous reste que les
mmes chanes pour arriver ltat final (ici, il y en a un seul). Donc, les tats non distinguables
se fondent dans un mme tat (en loccurrence (0,2)) de lautomate minimis.
En fait, on peut montrer que lalgorithme de minimisation quon a expliqu, est bas sur la
recherche des tous les groupes qui peuvent tre distingus par une chane ou I = {1}, T = {1,2}
une autre.

Exemple 2
Voici un automate dterministe complet avec deux tats terminaux, avec lalphabet A = {0, 1} :

Figure 5.14
La procdure de minimisation donne :

I = {1},
182

T = {1,2}

5.3. Linterprtation intuitive

Q0 = {(1,2), (3,4, 5,6)} = (I , I I }


Q1 = ({(1,2).(3,6), (4,5)} = {I , I I , I I I } (en recyclant les chiffres romains)
Q2 = Q1
Voici lautomate minimis :

Figure 5.15

5.3.2 Automate asynchrone

Dunod La photocopie non autorise est un dlit

Souvenons-nous que jusqu ce point nous avons vit le mot vide.


Un automate fini dans lequel certaines flches sont tiquetes par le mot vide ( -transitions
)

sappelle automate asynchrone. Lensemble des flches vrifie donc E Q A {} Q.
Proposition : pour tout automate asynchrone, il existe un automate fini 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 flches et
ce, suivant une rgle bien dtermine :
par dfinition on a ( p.a.q) F sil existe un chemin

c : p0 p1 . . . pn q0 q1 . . . qm
et p0 = p qm = q

Figure 5.16
a

Le nombre de -transitions droite ou gauche de la transition pn q0 peut tre nul (dans ce

cas pn = p0 ou pn = p0 ).
183

Chapitre 5 Automates

Exemple
a) Prenons un automate asynchrone :

Figure 5.17

(A1)

Ici, suivant le raisonnement prcdent, il existent les chemins : 1a1, 1a2, 1b2, 1c3, 2b2, 2c3.
b) Donc cet automate est quivalent lautomate non dterministe suivant :

Figure 5.18

(A2)

c) Maintenant on peut le dterminiser :

Tableau 5.13
tat

Figure 5.19
184

tats rsultant
en lisant a

en lisant b

en lisant c

1,2

1,2

1,2

(A3)

5.3. Linterprtation intuitive

ou bien, en ajoutant ltat poubelle :

Tableau 5.14
tat

Figure 5.20

tats rsultant
en lisant a

en lisant b

en lisant c

1,2

1,2

1,2

(A4)

Dunod La photocopie non autorise est un dlit

d) Mais en ralit, il est plus simple de se passer de ltape (b) et construire un automate
dterministe directement partir dun automate asynchrone.
Redessinons lautomate (A1) :

Figure 5.21

(A1)

Ltat initial de lautomate dterministe correspondant (A1) est (1,2), car en lisant le mot vide on
arrive et en 1, et en 2. Continuons dterminiser en marquant dans les colonnes correspondant
la lecture de chaque caractre, tous les tats o on arrive aprs la lecture du caractre en
question, c..d. non seulement ltat o mne la flche tiquete par ce caractre (disons, a ),
mais aussi tous les tats o on peut arriver en lisant a, a etc. Dans notre cas particulier, il ny
a pas de telles transitions, mais il faut systmatiquement en tenir compte.

185

Chapitre 5 Automates

Tableau 5.15
tat

Figure 5.22

tats rsultant
en lisant a

en lisant b

en lisant c

1,2

1,2

(A5)

On voit que lautomate (A5) nest pas le mme que lautomate (A3) ou (A4) ! Pourquoi ?
Parce que lautomate dterministe reconnaissant un certain langage nest pas unique, ce nest
quen minimisant quon arrive un automate unique. En minimisant (A4) et (A5), on doit obtenir
la mme chose.
Minimisons (A4) donc on rappelle la table de transitions :

Tableau 5.16
tat

tats rsultant
en lisant a

en lisant b

en lisant c

1,2

1,2

1,2

Ici , on voit immdiatement que les tats 1 et 1,2 ne se spareront jamais !


Q0 = {(1, (1,2), 2, P), (3)} = {I , (3)} (Utilisons une notation un peu plus intelligente pour ne
pas modifier le sens des chiffres romains sans cesse)

Tableau 5.17
tat

Groupes rsultant
en lisant a

en lisant b

en lisant c

1,2

(5) se spare en formant un groupe part (cest toujours le cas)

Q1 = {(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,2

(2) se spare.

Q2 = {(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,2

Le groupe (1,(1,2)) na aucun chance se scinder en deux, car ds le dbut (1) et (1,2) ont les
mmes transitions. Donc on a termin, et on obtient :

Dunod La photocopie non autorise est un dlit

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 fini dont les tats correspondent aux situations de famille

possibles dune personne (clibataire, mari, divorc, veuf) et dont les flches correspondent
aux changements de situation possible. tiqueter ces flches par M (mariage), D (divorce) et V
(veuvage).
Exercice 5.2 Construire des automates finis qui reconnaissent les langages suivants :
(a) Lensemble des mots sur lalphabet {0, 1} dont lavant dernier symbole est 0.
187

Chapitre 5 Automates

(b) Lensemble des mots sur lalphabet {0, 1} qui commencent et qui finissent par 0. (On

considre que le mot consistant en un seul 0 vrifie 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) {an bm |n + m pair}
Exercice 5.3 Construire un automate fini reconnaissant les entiers crits en base deux divisibles

par cinq.
Exercice 5.4 Construire un automate fini reconnaissant les lignes contenant des commentaires

dun programme crit en langage C. La sortie de lautomate correspond la fin du commentaire.


On prendra comme alphabet {/, , , a} o a reprsentera tous les caractres autres que ", * et /.
Tout commentaire commence par /* et finit par */.
Le commentaire peut contenir des / et des *, mais il ne peut pas contenir le motif */, sauf lintrieur
dune chane qui commence par " et finit 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 An (ils nont rien voir les uns avec les autres, ce nest

pas une squence !). Lalphabet A = {a, b} sauf pour A4 o il est {0,1}. Pour chacun de ces
automates :
Dcrire L(An ) en langage ordinaire (sauf pour A5 ).
Caractriser An (dire sil est accessible, coaccessible, mond...)
Calculer lautomate dterministe quivalent An .
188

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(A5 ) 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 fini 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 dfinit la famille dautomates suivants :

en = (Q n , I , T , E), n > 1
A

avec :
A = {a, b}
Q n = {0,1, ..., n 1}
I = T = {0}
Comme flches tiquetes par a lensemble de (q.a.((q + 1)mod n)) pour q : 0 6 q 6 n 1
Comme flches tiquetes par b lensemble de (q.b.0) et (q.b.q) pour q : 1 6 q 6 n 1
(attention : la premire ingalit commence par 0, et la seconde, par 1)
Dessiner f
A3 et f
A4 .
Puis montrer que le dterminis complet de f
An a toujours 2n tats.

Corrigs des exercices

Dunod La photocopie non autorise est un dlit

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 :
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 finissent 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 suivant :

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 {an bm |n + m pair}, est le suivant :

Figure 5.38

Cet automate est dterministe.

Dunod La photocopie non autorise est un dlit

Exercice 5.3 Ajout dun 0 la fin dun nombre binaire le multiplie par 2.
Ajout dun 0 la fin dun nombre binaire le multiplie par 2 et lui ajoute 1.
Tableau 5.20
N

N mod 5

Ajout dun 0
la fin de
lcriture binaire
donne N :

N mod 5

Ajout dun 1
la fin de
lcriture binaire
donne N :

N mod 5

5n

10n

10n + 1

5n + 1

10n + 2

10n + 3

5n + 2

10n + 4

10n + 5

5n + 3

10n + 6

10n + 7

5n + 4

10n + 8

10n + 9

Cela se traduit par lautomate suivant :


193

Chapitre 5 Automates

Tableau 5.21
tat

En lisant 0

En lisant 1

Figure 5.39
Exercice 5.4

Figure 5.40
Exercice 5.5 tats 2 et 3 ne mnent pas la sortie. Ils sont donc inutiles. Le langage reconnu par

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 A1
Cet automate lit tous les mots se terminant par bbb.
Il est mond.
Dterminisation

Tableau 5.22

Figure 5.42

tat

01

01

012

012

0123

0123

0123

A1

Solution A2
Cet automate lit tous les mots avec un b en 3ime position de la fin.
Il est mond.

Dunod La photocopie non autorise est un dlit

Dterminisation

Tableau 5.23
tat
Initial

01

01

02

012

012

023

0123

02

03

013

Terminal

0123

023

0123

Terminal

023

03

013

Terminal

03

01

Terminal

013

02

012

195

Chapitre 5 Automates

Figure 5.43
Solution A3
Cet automate lit tous les mots contenant au moins trois caractres ...a...b...a... o ... peut tre

vide.
Il est mond.
Dterminisation

Tableau 5.24
tat

01

01

01

01

012

012

0123

012

0123

0123

0123

Figure 5.44
Solution A4
Cet automate lit tous les mots consistant en des 0 ou en des 0 suivis dun seul 1, ou le mot 1.
196

Corrigs des exercices

Il est accessible mais non pas coaccessible.


Dterminisation

Tableau 5.25
tat

AB

AB

ABC

BC

ABC

ABC

BC

BC

On voit que C est ltat poubelle.

Dunod La photocopie non autorise est un dlit

Figure 5.45
Ici on a obtenu donc un automate dterministe avec poubelle. Mais il y avait une poubelle ds
le dbut ltat C dont on ne pouvait pas revenir. On aurait pu construire dabord un automate non
dterministe plus simple qui reconnat le mme langage quA4 :

Figure 5.46
et le dterminiser. En ce faisant on arrive un automate dterministe nettement plus simple que
tout lheure :
197

Chapitre 5 Automates

Tableau 5.26
tat

AB

AB

AB

Figure 5.47
Dans lexercice 10 on va voir que cet automate est en effet minimal.

Solution A5
On ne dcrit pas le langage (cest trop compliqu ce stade).
Il est mond.
Dterminisation :

Tableau 5.27

Figure 5.48
198

tat

12

12

12

Corrigs des exercices

(Maintenant il devient bien plus facile de dcrireL(A5 ) si lon veut : il consiste en tous les mots
commenant par a, finissant par a, et o tout b est entour par des a de faon ne jamais avoir bb).
Exercice 5.7
Tableau 5.28
tat

02

12

01

01

12

12

01

01

Figure 5.49
Exercice 5.8

Dunod La photocopie non autorise est un dlit

Solution (a)

Un automate dterministe complet qui reconnat lensemble des mots sur lalphabet {0, 1} qui
contiennent exactement trois fois le symbole 1 se construit directement, sans quon ait besoin de
construire un automate non dterministe dabord :

Figure 5.50
199

Chapitre 5 Automates

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

Figure 5.53
200

tat

01

01

01

01

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 complment du langage, il suffit de modifier 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 finaux,
tats non finaux et vice versa. La minimisation se fera ensuite.

Figure 5.55

Dunod La photocopie non autorise est un dlit

Cet automate lit tous les mots se terminant en .


Dterminisation

Tableau 5.30
tat

01

01

01

02

02

013

013

014

02

014

01

025

025

013

201

Chapitre 5 Automates

Figure 5.56
Cet automate est dj complet, sinon il faudrait le complter en ajoutant une poubelle.
Lautomate dterministe lisant tous les mots ne se terminant pas par abaab, sobtient comme
suit :

Figure 5.57
Il nest pas minimisable, c.--d. quil est dj minimal. Pour le voir, il faut essayer de le minimiser :
Q0 = {I , I I } avec I = {025}, I I = {0,01, 02,013, 014}
Tableau 5.31

202

tat

01

II

II

01

01

02

II

II

02

013

II

II

013

014

02

II

II

014

01

025

II

025

013

II

II

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 :
Q1 = {I , I I , I I I } avec I = {025}, I I = {0,01, 02,013}, I I I = {014}
Tableau 5.32
tat

01

II

II

01

01

02

II

II

02

013

II

II

013

014

02

III

II

Le groupe II se spare en deux :


Q2 = {I , I I , I I I , I V } avec I = {025}, II = {0, 01, 02}, III={014}, IV = {013}
Tableau 5.33
tat

01

II

II

01

01

02

II

II

02

013

IV

II

Le groupe II se spare en deux :


Q3 = {I , I I , I I I , I V , V } avec I = {025}, II = {0, 01}, III = {014}, IV = {013}, V={02}

Dunod La photocopie non autorise est un dlit

Tableau 5.34
tat

01

II

II

01

01

02

II

Q4 = Q3 ; tout les tats se sont spars ; lautomate tait minimal.


Exercice 5.10
Solution A1

Reprenons lautomate dterministe complet A1 :

203

Chapitre 5 Automates

Tableau 5.35
tat

01

01

012

012

0123

0123

0123

Figure 5.58
Minimisation

Q0 = {I, II} avec I = {0, 01, 012}, II = {0123}.


Tableau 5.36
tat

01

01

012

012

0123

II

Le groupe I se spare en deux. Modifiant la notation, on a :


Q1 = {I, II, III} avec I = {0, 01}, II = {012}, III = {0123}.
Tableau 5.37
tat

01

01

012

II

Les deux tats du groupe I se sont spars, donc tous les tats se sont spars lautomate tait
minimal ds le dbut.

204

Corrigs des exercices

Solution A2

Reprenons lautomate dterministe complet A2 mais modifions la notation des tats pour plus de
clart :
Tableau 5.38
En renommant
les tats
tat

tat

01

01

02

012

012

023

0123

02

03

013

Terminal

0123

023

0123

Terminal

023

03

013

Terminal

03

01

Terminal

013

02

012

Initial

(Le dessin nest pas trop utile pour cet automate.)


Minimisation

Q0 = {I, II} avec I = {A, B, C, D}, II = {E, F, G, H}.

Groupe
non terminal
Groupe terminal

Dunod La photocopie non autorise est un dlit

Tableau 5.39
tat

II

II

II

II

II

II

II

II

Q1 = {I, II, III, IV} avec I = {A, B}, II={C, D}, III={E, F}, IV={G, H}.

205

Chapitre 5 Automates

Tableau 5.40
tat

b
I

II

II

III

III

IV

IV

III

III

IV

IV

II

II

Tous les tats se sont spars, lautomate tait minimal ds le dbut.


Solution A3

Reprenons lautomate dterministe complet A3 :


Tableau 5.41
tat

01

01

01

01

012

012

0123

012

0123

0123

0123

Figure 5.59
Minimisation

Q0 = {I, II} avec I = {0, 01, 012}, II = {0123}


Tableau 5.42
tat

01

01

01

01

012

012

0123

012

II

Ltat 012 est sorti du groupe I. Nouveaux groupes :


Q1 = {I, II, III} avec I = {0, 01}, II = {012}, III = {0123}.
206

Corrigs des exercices

Tableau 5.43
tat

01

01

01

01

012

II

Tous les tats se sont spars, lautomate tait minimal ds le dbut.


Solution A4

Reprenons lautomate dterministe complet A4 :

Dunod La photocopie non autorise est un dlit

Tableau 5.44
tat

AB

AB

ABC

BC

ABC

ABC

BC

BC

Figure 5.60
Minimisation

Q0 = {I, II} avec I = {A, C}, II = {AB, ABC, BC, B}.


207

Chapitre 5 Automates

Groupe terminal

Groupe non
terminal

Tableau 5.45
tat

AB

II

II

AB

ABC

BC

II

II

ABC

ABC

BC

II

II

BC

On peut voir immdiatement que les tats A et C se sparent, et que le groupe II se divise en deux
groupes, qui ne pourront plus se sparer. On obtient donc les tats de lautomate minimis : A, (B,
BC), (AB, ABC), et C.
Graphiquement, cet automate minimal se prsente comme :

Figure 5.61
o ltat C sert comme ltat poubelle. Lautomate dterministe correspondant A4 ntait donc
pas minimal. On voit quen le minimisant on est arriv au mme automate quon a obtenu en
dterminisant lautomate A4 initial (non dterministe) duquel on a coup sa poubelle .
Remarque
On rappelle encore une fois que la poubelle dun automate non dterministe (ltat ou le groupe
dtats dont on ne peut pas allez vers la ou les sortie(s) nest pas la mme chose que la poubelle de
lautomate dterministe qui en rsulte aprs la dterminisation.
La seule utilit de la poubelle dun automate non dterministe consiste en la possibilit de la
couper pour simplifier la dterminisation.
208

Corrigs des exercices

Solution A5

Figure 5.62

A5

Minimisation

Q0 = {I, II} avec I = {0, P}, II = {12}.


Tableau 5.46
tat

12

II

12

12

II

Les deux tats du groupe I se sont spars ; donc tous les tats se sont spars, lautomate tait
minimal ds le dbut.
Exercice 5.11
Dunod La photocopie non autorise est un dlit

Solution (a)
Dterminisation

Tableau 5.47
tat

01

01

012

012

02

012

02

02

012

209

Chapitre 5 Automates

Figure 5.63
Minimisation

Q0 = {I, II} avec I = {0, 01}, II = {012, 02}.


Tableau 5.48
tat

01

b
I

01

012

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 modifiant les
tiquettes des tats
tat

tat

02

12

01

01

12

012

12

02

012

12

012

02

Tous les tats sont des tats finaux sauf ltat poubelle.
210

Corrigs des exercices

Tableau 5.50
tat

II

II

II

II

II

II

II

II

II

Minimisation

Q0 = {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

III

III

III

III

II

III

III

III

Nouveaux groupes dtats : I = {P}, II = {C}, III = {B}, IV = {A, D, E}.

Dunod La photocopie non autorise est un dlit

Tableau 5.52
tat

III

IV

III

IV

III

IV

Le groupe IV ne se spare pas.


Le rsultat :

Figure 5.65

211

Chapitre 5 Automates

Exercice 5.12
3

tats 0, 1, 2;
flches 0.a.(1 mod 3) = 0.a.1 ; 1.a.(2 mod 3) = 1.a.2 ; 2.a.(3 mod 3) = 2.a.0 ;
1.b.0; 1.b.1; 2.b.0; 2.b.2.
Tableau 5.53
tat

0,1

0,2

Figure 5.66
4

tats 0, 1, 2, 3;
flches 0.a.(1 mod 4) = 0.a.1 ; 1.a.(2 mod 4) = 1.a.2 ;
2.a.(3 mod 4) = 2.a.3 ; 2.a.(4 mod 4) = 2.a.0 ;
1.b.0; 1.b.1; 2.b.0; 2.b.2; 3.b.0; 3.b.3.
Tableau 5.54

212

tat

0,1

0,2

0,3

Corrigs des exercices

Figure 5.67
n

Dunod La photocopie non autorise est un dlit

Tableau 5.55
tat

0,1

0,2

0,3

...

...

...

n1

0, n 1

Figure 5.68

213

Chapitre 5 Automates

Dterminisation

Tableau 5.56
tat

01

02

03

...

...

...

n1

(0, n 1)

01

12

01

02

13

02

...

...

...

(0, n 1)

01

(0, n 1)

12

23

012

13

24

013

...

...

...

(1, n 1)

02

(0,1, n 1)

...

...

...

Ici figurent toutes les combinaisons possibles de m tats parmi n cest--dire : 1 6 m 6 n.


n
X
Cnm = 2n 1 de tels tats. Si lon y ajoute ltat poubelle (o mne la flche tiquete b
Il y a
m=0

partant de ltat 0), pour que lautomate soit complet, on obtient 2n tats.

214

BIBLIOGRAPHIE

BAYNAT B., C HRTIENNE P., H ANEN C., K EDAD -S IDHOUM S., M UNIER -KORDON A. et P ICOU LEAU C. Exercices et problmes dalgorithmique 2e dition. Dunod, Paris, 2007.
C ARREZ C. Des structures aux bases de donnes. Dunod, Paris, 1994.
C ORMEN T., L EISERSON C., R IVEST R. et S TEIN C. Introduction lalgorithmique 3e dition.
Dunod, Paris, 2010.
F ROIDEVAUX C., G AUDEL M.-C. et S ORIA M. Types de donnes et algorithmes. Ediscience
International, Paris, 1994.
G UYOT J. et V IAL C. Arbres, tables et algorithmes. Eyrolles, Paris, 1992.
L UC B OUGE L., K ENYON C., M ULLER J.-M. et ROBERT Y. Algorithmique. Exercices corrigs.
Ellipses, Paris, 1993.

215

INDEX

A
accepteurs 169
adresse 18
affectation 7
afficher 8, 45
algorithme 1
allocation dynamique 23
alphabet 170, 171, 174, 176, 177, 182
arbre 127129, 131, 137139, 143
binaire 127129, 131, 142, 145
de Syracuse 145
automate
fini 169, 170, 172, 173, 176, 178

E
mond 176, 188, 195, 196, 198
quifinalit 1
ET 11
tat 169172, 174, 178, 182, 204
accepteur 171
initial 170, 174
terminal 170, 174, 177, 178, 182
valuation 13
expression 8

F
B
boucle 12

Dunod La photocopie non autorise est un dlit

C
calcul 1
champ 29
complexit algorithmique 1
concatnation 37, 45, 51, 52
condition 9
constante 7

FIFO 90
file 90
fonction 23
appel 25
appelante 25
appele 25
argument 26
corps 24
en-tte 24

H
D
dcidabilit 1
algorithmique 1
logique 1
pratique 1
thorique 1
dterminisation 173177, 195198, 200, 201,
208210, 214

Heqat 99

I
insertion 35, 37, 43, 99, 112
point d 112
itratif 12, 45, 49, 50, 54, 63, 142, 178
217

Exercices et problmes dalgorithmique

L
LIFO 87
liste
doublement chane 44, 99, 112
linaire 35
simplement chane 38, 46, 112

R
racine 127
raisonnement 1
rcursif 45, 5052, 61, 65, 136, 142
rcursivit 127, 136

M
machine de Turing 169
minimisation 178, 182, 201
mot
vide 170

saisir 9, 40
solution 1
sommet de pile 87
sous-programme 23
structures de contrle 9
successeur 56, 112
suppression 37, 41, 43, 46, 61, 99, 112, 138

NON 11
NULL 21

T
O

OU 11

P
pile 8789
pile 89
pointeur 18, 19
prdcesseur 60, 61, 112, 145, 166
problme 1
problme de Joseph 99
pseudo code IX

218

tableaux 14
tables de vrit 11
transition 33, 169171, 174, 175, 214
table de 171
tri 2
type 5
point 20

V
variable 6
locale 24

You might also like