Analyse Conception Algorithm e
Analyse Conception Algorithm e
1.1.1 Algorithme
Une succession finie d’opérations qui donne la solution d’un problème donné. Pour
écrire un algorithme, on utilise un pseudo-langage compréhensible par une com-
munauté. Donc, l’idée générale d’un algorithme est de donner la solution d’un
problème sous forme d’opérations qui peuvent être traduites dans un langage
compréhensible par la machine tout en le gardant lisible et compréhensible par
une personne ordinaire.
1
Comme illustré par la figure ci-dessus, plusieurs étapes sont nécessaires pour
résoudre un problème en informatique :
1. Définition du problème
Il s’agit de déterminer toutes les informations disponibles et la forme des
résultats désirés.
2. Analyse du problème
Elle consiste à trouver le moyen de passer des données aux résultats. Dans
certains cas on peut être amené à faire une étude théorique. Le résultat de
l’étape d’analyse est un algorithme.
Note: Sachez qu’il existe des problèmes pour lesquels on ne peut trouver
une solution et par conséquent il est impossible de donner l’algorithme corre-
spondant. Cette catégorie de problèmes sera étudiée dans une autre unité
d’enseignement prévue dans ce cursus.
2
exprimé dans un langage de programmation.
On peut énoncer les cinq propriétés suivantes que doit satisfaire un algorithme :
– Définitude : toutes les opérations d’un algorithme doivent être définies sans
ambiguı̈té.
3
1.1.3 Programme
La première ligne (1) permet juste d’identifier l’algorithme. Donc, le nom at-
4
tribué ne change pas l’exécution et les résultats. Avant de mettre les instructions,
il faut déclarer (2)les constantes(3) et les variables(4) utilisées dan l’algorithme.
La partie principale de l’algorithme se trouve entre les mots clés ‘début’ et ‘fin’
(5). Elle contient la suite d’instructions à exécuter.
Une variable est un mot qui permet l’identification de l‘emplacement mé- moire
où on stocke une valeur à manipuler. Malgré que le choix est libre du nom de la
variable, il préférable, pour des raisons de lisibilité et de compréhension de choisir
des noms de variables en fonctions de ce qu’elles représentent. Par exemple :
Moyenne, Poids, Taille,. . .etc.
La notion de type d’une variable est capitale, car elle implique le choix du codage
de la variable (par exemple, un entier sur 32 bits et un caractère sur 16 bits) ainsi
que les possibilité d’usage ( par exemple : on ne peut pas multiplier un caractère
par un nombre entier).
Exemple: TVA = 0,17
5
N = 50
Admis = Vrai
Ce type d’instruction est utilisé pour interagir avec l’utilisateur, en lui permettant
d’entrer des valeurs des variables. Ainsi, le résultat des traitements et des messages
d’information peuvent être affichés à l’utilisateur via ce type d’instruction. En
réalité, il existe plusieurs périphériques et manières pour échanger des données
avec un algorithme ou un programme (via des fichiers, des bases de données, des
formulaires,. . .). Toutefois, pour se concentrer sur les principes de l’algorithmique,
on se contente par les entrées et les sorties standards, à savoir, l’écriture sur le
clavier et l’affichage sur écran. L’exploitation des autres possibilités vont être
abordées dans la matière dédiée à la programmation.
Exemple :
6
Lire(Taille)
Lire (x, y)
Exemple :
Écrire (moyenne)
Écrire (‘ entrer la taille’)
Écrire (‘le résultat est :’, max)
Cette instruction permet d’affecter une valeur à une variable, après l’évaluation
d’un expression logique ou arithmétique. A la place d’une expression, on peut
utiliser une simple valeur ou une variable Il faut noter que le résultat de l’évaluation
de l’expression doit avoir le même type que la variable qui va le recevoir.
Structure générale :
Exemple 1 :
x ← 15.2
x←y
7
x←y+4
Exercice Quelles sont les valeurs des variables a,b et c écrites par l’algorithme
suivant ? Algorithme Affectation1
Variables :a,b,c : entier
Début a ← 10
b←a∗2
a ← a − b/2
c←b+a∗2
Ecrire (a,b,c)
Fin.
Solution :
a = 10, b = 20, c = 40
a = −5, b = 20, c = 10
a = 0, b = 20, c = 20
Ces instructions sont utilisées pour faire le choix entre l’exécution ou non des blocs
d’instructions suite à l’évaluation d’une condition. On en distingue trois types :
Ce type d’instructions permet de faire le choix entre l’exécution ou non d’un seul
boc d’instructions.
8
Structure :
Exemple :
A←5
B ← 12
SI(B > A ∗ 2) alors B ← A
Fin Si
Ecrire(B)
Exemple:
9
[Link] L’instruction conditionnelle de choix
Structure :
Exemple : Exemple:
10
1.2.5 Les instructions itératives
Structure:
11
[Link] L’instruction ‘Tant que’
Exemple : i = 1
Tant que (i ⩽ 10) faire
Ecrire (‘L2 MSI’)
Fin Tant que
Comme le cas pour l’instruction ‘Tant que’, l’instruction ‘Répéter’ ne permet pas
de connaitre forcement à l’avance le nombre d’itérations. En effet, on continue la
répétition de l’exécution du bloc d’instructions jusqu’à la satisfaction d’une con-
dition.
Structure:
12
‘Répéter’ s’exécute une ou plusieurs fois.
Les procédures et les fonctions sont des abstractions d’ opérations. Chaque opération
est décrite par son nom, quelques paramètres et ce qu’elle fait. Chaque procédure
ou fonction définit une opération en fournissant : son nom, une description de ses
paramètres, une valeur facultative qu’il peut renvoyer, et une spécification de la
relation entre les paramètres, la valeur renvoyée et toute autre valeur de données .
Une procédure est un bloc d’instructions nommé et déclaré dans l’entête de l’algorithme
et appelé dans son corps à chaque fois que le programmeur en a besoin.
13
1.3.2 Les fonctions
Une fonction est un bloc d’instructions qui retourne obligatoirement une et une
seule valeur résultat à l’algorithme appelant. Une fonction n’affiche jamais la
réponse à l’écran car elle la renvoie simplement à l’algorithme appelant.
Les types de données primitifs sont les types de données de base, souvent fournis
par les langages de programmation de manière native. Ils représentent des valeurs
indivisibles et simples. Voici les types de données primitifs les plus courants :
14
1.4.2 Types de données composés (ou complexes)
Les types de données composés sont des combinaisons de types de données primitifs
qui permettent de regrouper plusieurs valeurs dans une seule entité.
15
[Link] Les types énumérés
Les types énumérés constituent un autre type qui peuvent être définis par l’utilisateur/programme
où il peut listez un ensemble de valeurs dans une énumération. En d’autres mots,
une énumération est une liste de valeurs définie par l’utilisateur.
La déclaration des types énumérés se fait avec une syntaxe spéciale et voici
quelques exemples :
Les types prédéfinis sont peu nombreux. C’est pourquoi des constructeurs de
types permettent de définir des types plus complexes, et donc des structures de
données moins élémentaires. Là encore, l’implémentation précise d’un array of char
par exemple importe peu au programmeur (est-il rangé par ligne, par colonne ?),
aussi longtemps que sa manipulation satisfait à des règles précises. En revanche,
lorsque l’on veut réaliser une pile (disons de caractères) , les problèmes se posent
autrement, puisqu’il n’existe pas, en Pascal par exemple, de constructeur perme-
ttant de définir des stack of char : il est nécessaire de recourir à une structure de
données.
16
1.5.1 Définitions
Une structure de données est un moyen pour stocker et organiser les données
pour en faciliter l’accès et la modification. C’est également une implémentation
explicite d’un ensemble organisé d’objets, avec la réalisation des opérations d’accès,
de construction et de modification afférentes.
Une structure de données regroupe :
17
– Les modalités d’utilisation des opérations pouvant être effectuées indépendamment
de toute représentation interne de ces données ainsi que l’implémentation des
opérations .
– Son nom ;
– Axiomes : Ils définissent les règles logiques qui régissent les opérations du
TAD et permettent de maintenir un comportement cohérent et prévisible
dans toutes les situations
– L’utilisation d’un TDA n’a pas besoin de connaı̂tre les détails de codage ;
18
Chapter 2
Structures séquentielles
19
de définir la taille de ce tableau avec précision même si nous connaissons la valeur
de n (par exemple 10000). On est donc, ici, en face d’un problème où la réservation
de l’espace doit être dynamique
Une liste linéaire chaı̂née (LLC) est un ensemble de maillons, alloués dynamique-
ment, chaı̂nés entre eux. Schématiquement, on peut la représenter comme suit
:
Un élément ou maillon d’une LLC est toujours une structure (Objet composé)
avec deux champs : un champ Valeur contenant l’information et un champ
Adresse donnant l’adresse du prochain élément. A chaque maillon est associée
une adresse. On introduit ainsi une nouvelle classe d’objet : le type Pointeur
qui est une variable contenant l’adresse d’un emplacement mémoire.
Une liste linéaire chainée est caractérisée par l’adresse de son premier élément
souvent appelé tête. Nil constitue l’adresse qui ne pointe aucun maillon, utilisé
pour indiquer la fin de la liste dans le dernier maillon.
20
La représentation réelle en mémoire de cette liste ressemble à la représentation
suivante :
Afin de développer des algorithmes sur les LLCs, on construit une machine ab-
straite avec les opérations suivantes : Allouer, Libérer, Aff Adr, Aff Val, Suivant
et Valeur définies comme suit :
21
– Valeur(P) : consultation du champ Valeur du maillon pointé par P.
De même que sur les tableaux, on peut classer les algorithmes sur les LLCs comme
suit :
– Algorithmes de tri sur les LLCs : trie par bulle, tri par fusion,...
22
Création d’une liste et listage de ses éléments
Algorithme CréerListe;
Type TMaillon = Structure
Valeur : Typeqq ;
Suivant : Pointeur(TMaillon) ;
Fin ;
Var P, Q, Tete : Pointeur(TMaillon ;
i, Nombre : entier ;
Val : Typeqq ;
Début
Tete Nil ;
P Nil ;
Lire(Nombre) ;
Pour i de 1 à Nombre faire
Lire(Val) ;
Allouer(Q) ;
Aff_val(Q, val) ;
Aff_adr(Q, NIL) ;
Si (Tete 6= Nil) Alors
Aff_adr(P, Q)
Sinon
Tete Q
Fin Si;
P Q;
Fin Pour;
P Tete ;
Tant que ( P 6= Nil) faire
Ecrire(Valeur(P)) ;
P Suivant(P) ;
Fin TQ;
Fin.
23
cet algorithme crée une liste linéaire chaînée à partir d’une suite de valeurs données,
puis imprime la liste ainsi créée.
Algorithme Recherche;
Type TMaillon = Structure
Valeur : Typeqq ;
Suivant : Pointeur(TMaillon) ;
Fin ;
Var P, Tete : Pointeur(TMaillon ;
Trouv : booleen ;
Val : Typeqq ;
Début
Lire(Val) ;
P Nil ;
Trouv Faux ;
Tant que ( P 6= Nil et non Trouv) faire
Si (Valeur(P)=Val) Alors
Trouv Vrai
Sinon
P Suivant(P)
Fin Si;
Fin TQ;
Si (Trouv=Vrai) Alors
Ecrire(Val,’Existe dans la liste’)
Sinon
Ecrire(Val,’n”existe pas dans la liste’
Fin Si;
Fin.
24
2.1.6 Listes linéaires chaînées bidirectionnelles
C’est une LLC où chaque maillon contient à la fois l’adresse de l’élément précédent et
l’adresse de l’élément suivant ce qui permet de parcourir la liste dans les deux sens.
Déclaration
25
2.2 Les piles (stacks)
2.2.1 Définition
Une pile est une liste ordonnée d’éléments où les insertions et les suppressions d’éléments
se font à une seule et même extrémité de la liste appelée le sommet de la pile.
Le principe d’ajout et de retrait dans la pile s’appelle LIFO (Last In First Out) : "le dernier
qui entre est le premier qui sort"
Une pile sert à mémoriser les pages Web visitées. L’adresse de chaque nouvelle page
visitée est empilée et l’utilisateur dépile l’adresse de la page précédente en cliquant le
bouton "Afficher la page précédente".
Fact(4)=4*Fact(3)=4*3*Fact(2)=4*3*2*Fact(1)=4*3*2*1=4*3*2=4*6=24
26
[Link] Parcours en profondeur des arbres
B C
D E F G
Exemple
L’expression ((a + (b ⇤ c))/(c d) est exprimée, en postfixé, comme suit : bc ⇤ a + cd /
Pour l’évaluer, on utilise une pile. On parcourt l’expression de gauche à droite, en exécutant
l’algorithme suivant :
27
i 1;
Tant que (i < Longeur(Expression)) faire
Si (Expression[i] est un Opérateur) Alors
Retirer deux éléments de la pile ;
Calculer le résultat selon l’opérateur ;
Mettre le résultat dans la pile ;
Sinon
Mettre l’opérande dans la pile ;
Fin Si;
i i + 1;
Fin TQ;
28
2.2.4 Implémentation des piles
Les piles peuvent être représentés en deux manières : par des tableaux ou par des LLCs :
L’implémentation statique des piles utilise les tableaux. Dans ce cas, la capacité de la
pile est limitée par la taille du tableau. L’ajout à la pile se fait dans le sens croissant des
indices, tandis que le retrait se fait dans le ses inverse.
L’implémentation dynamique utilise les listes linéaires chinées. Dans ce cas, la pile peut
être vide, mais ne peut être jamais pleine, sauf bien sur en cas d’insuffisance de l’espace
mémoire. L’empilement et le dépilement dans les piles dynamiques se font à la tête de la
liste.
Les deux algorithmes "PileParTableaux" et "PileParLLCs" suivant présentent deux
exemples d’implémentation statique et dynamique des piles.
29
Algorithme PileParTableaux;
Var Pile : Tableau[1..n] de entier ; Sommet : Entier ;
Procédure InitPile();
Début
Sommet 0;
Fin;
Fonction PileVide() : Booleen;
Début
P ileV ide (Sommet = 0) ;
Fin;
Fonction PilePleine() : Booleen;
Début
P ileP leine (Sommet = n) ;
Fin;
Procédure Empiler( x : entier);
Début
Si (PilePleine) Alors
Ecrire(’Impossible d’empiler, la pile est pleine ! !’)
Sinon
Sommet Sommet + 1 ;
P ile[Sommet] x;
Fin Si;
Fin;
Procédure Dépiler( x : entier);
Début
Si (PileVide) Alors
Ecrire(’Impossible de dépiler, la pile est vide ! !’)
Sinon
x P ile[Sommet] ;
Sommet Sommet 1;
Fin Si;
Fin;
Début
... Utilisation de la pile ...
Fin.
30
Algorithme PileParLLCs;
Type TMaillon = Structure
Valeur : entier ;
Suivant : Pointeur(TMaillon) ;
Fin ;
Var P, Sommet : Pointeur(TMaillon) ;
Procédure InitPile();
Début
Sommet N il ;
Fin;
Fonction PileVide() : Booleen;
Début
P ileV ide (Sommet = N il) ;
Fin;
Procédure Empiler( x : entier);
Début
Allouer(P) ; Aff_Val(P,x) ;
Aff_Adr(P,Sommet) ; Sommet P;
Fin;
Procédure Dépiler( x : entier);
Début
Si (PileVide) Alors
Ecrire(’Impossible de dépiler, la pile est vide ! !’)
Sinon
x V aleur(Sommet) ; P Sommet ;
Sommet Suivant(Sommet) ; Libérer(P) ;
Fin Si;
Fin;
Début
... Utilisation de la pile ...
Fin.
31
2.2.5 Exemple d’application : Remplissage d’une zone d’une image
Une image en informatique peut être représentée par une matrice de points 0 Image0
ayant M colonnes et N lignes. Un élément Image[x, y] de la matrice représente la couleur
du point p de coordonnées (x, y). On propose d’écrire ici une fonction qui, à partir d’un
point p, étale une couleur c autour de ce point. La progression de la couleur étalée s’arrête
quand elle rencontre une couleur autre que celle du point p. La figure suivante illustre cet
exemple, en considérant p = (3, 4).
Pour effectuer le remplissage, on doit aller dans toutes les directions à partir du point p.
Ceci ressemble au parcours d’un arbre avec les noeuds de quatre fils. La procédure suivante
permet de résoudre le problème en utilisant une pile.
32
Procédure Remplir( Image : T ableaux[0..M 1, 0..N 1] de couleur ; x, y : entier ;
c : couleur);
Var c1 : couleur ;
Début
c1 Image[x, y] ;
InitPile ;
Empiler((x, y)) ;
Tant que (¬ PileVide) faire
Depiler((x,y)) ;
Si (Image[x, y] = c1 ) Alors
Image[x, y] c;
Si (x > 0) Alors
Empiler((x 1, y))
Fin Si;
Si (x < M 1) Alors
Empiler((x + 1, y))
Fin Si;
Si (y > 0) Alors
Empiler((x, y 1))
Fin Si;
Si (y < N 1) Alors
Empiler((x, y + 1))
Fin Si;
Fin Si;
Fin TQ;
Fin;
33
2.3 Les Files d’attente (Queues)
2.3.1 Définition
La file d’attente est une structure qui permet de stocker des objets dans un ordre donné
et de les retirer dans le même ordre, c’est à dire selon le protocole FIFO ’first in first out’.
On ajoute toujours un élément en queue de liste et on retire celui qui est en tête.
Les files d’attente sont utilisées, en programmation, pour gérer des objets qui sont
en attente d’un traitement ultérieur, tel que la gestion des documents à imprimer, des
programmes à exécuter, des messages reçus,...etc. Elles sont utilisées également dans le
parcours des arbres.
Exercice
De même que pour les piles, les files d’attente peuvent être représentées en deux ma-
nières :
– par représentation statique en utilisant les tableaux,
– par représentation dynamique en utilisant les listes linéaires chaînées.
34
[Link] Implémentation statique
L’implémentation statique peut être réalisée par décalage en utilisant un tableau avec
une tête fixe, toujours à 1, et une queue variable. Elle peut être aussi réalisée par flot
utilisant un tableau circulaire où la tête et la queue sont toutes les deux variables.
1. Par décalage
35
[Link] Implémentation dynamique
Une file d’attente avec priorité est une collection d’éléments dans laquelle l’insertion
ne se fait pas toujours à la queue. Tout nouvel élément est inséré, dans la file, selon sa
priorité. Le retrait se fait toujours du début.
Dans une file avec priorité, un élément prioritaire prendra la tête de la file même s’il arrive
le dernier. Un élément est toujours accompagné d’une information indiquant sa priorité
dans la file.
L’implémentation de ces files d’attente peut être par tableau ou listes, mais l’implémen-
tation la plus efficace et la plus utilisée utilise des arbres particuliers qui s’appellent ’les
tas’.
36
Algorithme FileParFlot;
Var File : Tableau[1..n] de entier ; Tête, Queue : Entier ;
Procédure InitFile();
Début
T ête 1 ; Queue 1;
Fin;
Fonction FileVide() : Booleen;
Début
F ileV ide (T ête = Queue) ;
Fin;
Fonction FilePleine() : Booleen;
Début
F ileP leine (((Queue + 1) mod n) = T ête) ;
Fin;
Procédure Enfiler( x : entier);
Début
Si (FilePleine) Alors
Ecrire(’Impossible d”enfiler, la file est pleine ! !’)
Sinon
F ile[Queue] x;
Queue (Queue + 1) mod n ;
Fin Si;
Fin;
Procédure Défiler( x : entier);
Début
Si (FileVide) Alors
Ecrire(’Impossible de défiler, la file est vide ! !’)
Sinon
x F ile[T ete] ;
T ête (T ête + 1) mod n ;
Fin Si;
Fin;
Début
... Utilisation de la File ...
Fin.
37
Algorithme FileParLLCs;
Type TMaillon = Structure
Valeur : entier ;
Suivant : Pointeur(TMaillon) ;
Fin ;
Var P, Tête, Queue : Pointeur(TMaillon) ;
Procédure InitFile();
Début
T ête N il ; Queue N il ;
Fin;
Fonction FileVide() : Booleen;
Début
F ileV ide (T ête = N il) ;
Fin;
Procédure Enfiler( x : entier);
Début
Allouer(P) ; Aff_Val(P,x) ;
Aff_adr(P,Nil) ;
Si (Queue = Nil) Alors
T ête P;
Sinon
Aff_Adr(Queue,P) ;
Fin Si;
P;
Queue
Fin;
Procédure Défiler( x : entier);
Début
Si (FileVide) Alors
Ecrire(’Impossible de défiler, la file est vide ! !’)
Sinon
x V aleur(T ete) ; P T ête ;
T ête Suivant(T ête) ; Libérer(P) ;
Fin Si;
Fin;
Début
... Utilisation de la File ...
Fin.
38
Chapitre 3
Structures Hiérarchiques
3.1.1 Introduction
3.1.2 Définitions
Un arbre est une structure non linéaire, c’est un graphe sans cycle où chaque nœud a
au plus un prédécesseur.
Graphe
39
Arbre
40
[Link] Niveau d’un nœud
– Le niveau de la racine = 0
– Le niveau de chaque nœud est égale au niveau de son père plus 1
– Niveau de E,F,G,H,I,J,K = 2
41
– Représentation d’un arbre généalogique
– Codage
Exemple : coder la chaine "structure arbre"
42
Caractère Code Caractère Code
s 0000 c 0001
t 010 e 001
r 11 a 100
u 011 b 101
4. Coder la chaine :
"structure arbre" ) 0000 010 11 011 0001 010 011 11 001 100 11 101 11 001
Les arbres peuvent être représentés par des tableaux, des listes non linéaires ou tous
les deux :
L’arbre du premier exemple peut être représenté par un tableau comme suit :
43
[Link] Représentation dynamique
Pour manipuler les structures de type arbre, on aura besoin des primitives suivantes :
– Allouer (N) : créer une structure de type Tnœud et rendre son adresse dans N.
– Liberer(N) : libérer la zone pointée par N.
– Aff_Val(N, Info) : Ranger la valeur de Info dans le champs info du nœud pointé
par N.
– Af f _F ilsi (N1,N2) : Rendre N2 le fils numéro i de N1.
– F ilsi (N) : donne le fils numéro i de N.
– Valeur(N) : donne le contenu du champs info du nœud pointé par N.
Le parcours d’un arbre consiste à passer par tous ses nœuds. Les parcours permettent
d’effectuer tout un ensemble de traitement sur les arbres. On distingue deux types de
parcours :
44
1. Parcours en profondeur
Dans un parcours en profondeur, on descend le plus profondément possible dans
l’arbre puis, une fois qu’une feuille a été atteinte, on remonte pour explorer les autres
branches en commençant par la branche "la plus basse" parmi celles non encore
parcourues. L’algorithme est le suivant :
45
Procédure PPPostfixe( nœud : Pointeur(Tnœud));
Début
Si (nœud 6= Nil) Alors
Pour i de 1 à NbFils faire
PPPostfixe(F ilsi (nœud))
Fin Pour;
Afficher(Valeur(nœud)) ;
Fin Si;
Fin;
A,B,E,F,C,G,H,L,M,I,D,J,K
E,F,B,G,L,M,H,I,C,J,K,D,A
2. Parcours en largeur
Dans un parcours en largeur, tous les nœuds à une profondeur i doivent avoir été
visités avant que le premier nœud à la profondeur i + 1 ne soit visité. Un tel parcours
nécessite que l’on se souvienne de l’ensemble des branches qu’il reste à visiter. Pour
ce faire, on utilise une file d’attente.
46
Procédure PL( nœud : Pointeur(Tnœud));
Var N : Pointeur(Tnœud) ;
Début
Si (nœud 6= Nil) Alors
InitFile ; Enfiler(Neuud) ;
Tant que (Non(FileVide)) faire
Défiler(N) ;
Afficher(Valeur(N)) ;
Pour i de 1 à NbFils faire
Si (F ilsi (N) 6= Nil) Alors
Enfiler(F ilsi (N)) ;
Fin Si;
Fin Pour;
Fin TQ;
Fin Si;
Fin;
A,B,C,D,E,F,G,H,I,J,K,L,M
47
[Link] Recherche d’un élément
48
[Link] Calcul de la taille d’un arbre
49
3.2 Les arbres binaires de recherche
3.2.1 Définition
Les arbres binaires de recherche sont utilisés pour accélérer la recherche dans les arbres
m-aires. Un arbre binaire de recherche est un arbre binaire vérifiant la propriété suivante :
soient x et y deux nœuds de l’arbre, si y est un nœud du sous-arbre gauche de x, alors
clé(y) clé(x), si y est un nœud du sous-arbre droit de x, alors clé(y) clé(x).
G1: Les arbres m-maires
Les arbres de recherche binaires sont implémentés de la même manière que celles m-aires
(statique ou dynamique)
50
[Link] Représentation dynamique
– Allouer (N) : créer une structure de type TNoeud et rendre son adresse dans N.
– Liberer(N) : libérer la zone pointée par N.
– Aff_Val(N, Info) : Ranger la valeur de Info dans le champs info du nœud pointé
par N.
– Aff_Clé(N, Clé) : Ranger la valeur de Clé dans le champs Clé du nœud pointé
par N.
– Aff_FG(N1,N2) : Rendre N2 le fils gauche de N1.
– Aff_FD(N1,N2) : Rendre N2 le fils droit de N1.
– FG(N) : donne le fils gauche de N.
– FD(N) : donne le fils droit de N.
– Valeur(N) : donne le contenu du champs info du nœud pointé par N.
– Clé(N) : donne le contenu du champs Clé du nœud pointé par N.
51
3.2.4 Traitements sur les ABR
[Link] Parcours
De même que pour les arbres m-aire le parcours des ARB peut se faire en profondeur
ou en largeur :
– En profondeur
Trace : 15 6 3 2 4 7 13 9 18 17 20
52
Procédure Infixe( noeud : Pointeur(TNoeud));
Début
Si (nœud 6= Nil) Alors
Infixe(FG(noeud)) ;
Ecrire(Valeur(noeud)) ;
Infixe(FD(noeud)) ;
Fin Si;
Fin;
Trace : 2 3 4 6 7 9 13 15 17 18 20
Trace : 2 4 3 9 13 7 6 17 20 18 15
– En largeur
53
Procédure PL( noeud : Pointeur(TNoeud));
Var N : Pointeur(TNoeud) ;
Début
Si (noeud 6= Nil) Alors
InitFile ;
Enfiler(noeud) ;
Tant que (Non(FileVide)) faire
Défiler(N) ;
Afficher(Valeur(N)) ;
Si (FG(N) 6= Nil) Alors
Enfiler(FG(N)) ;
Fin Si;
Si (FD(N) 6= Nil) Alors
Enfiler(FD(N)) ;
Fin Si;
Fin TQ;
Fin Si;
Fin;
54
[Link] Recherche
[Link] Insertion
L’élément à ajouter est inséré là où on l’aurait trouvé s’il avait été présent dans l’arbre.
L’algorithme d’insertion recherche donc l’élément dans l’arbre et, quand il aboutit à la
conclusion que l’élément n’appartient pas à l’arbre (l’algorithme aboutit sur NIL), il insère
l’élément comme fils du dernier nœud visité.
[Link] Suppression
55
Cas
Action
FG(N) FD(N) Exemple
Nil Nil Feuille (2,4,17) Remplacer N par Nil
Nil 6= Nil 7 Remplacer N par FD(N)
6= Nil Nil 13 Remplacer N par FG(N)
1- Rechercher le plus petit descendant à droite de
6= Nil 6= Nil 6 N soit P (7)
2- Remplacer Valeur(N) par Valeur(P) (6 7)
3- Remplacer P par FD (P) (7 13 )
3.2.5 Equilibrage
Ces deux ARB contiennent les mêmes éléments, mais sont organisés différemment. La
profondeur du premier est inférieure à celle du deuxième. Si on cherche l’élément 10 on
devra parcourir 3 éléments (50, 20, 10) dans le premier arbre, par contre dans le deuxième,
on devra parcourir 5 élément (80, 70, 50, 20,10). On dit que le premier arbre est plus
équilibré.
Si on calcule la complexité de l’algorithme de recherche dans un arbre de recherche binaire,
on va trouver O(h) ou h est la hauteur de l’arbre. Donc plus l’arbre est équilibré, moins
élevée est la hauteur et plus rapide est la recherche.
On dit qu’un ARB est équilibré si pour tout nœud de l’arbre la différence entre la hauteur
du sous-arbre gauche et du sous-arbre droit est d’au plus égalé à 1. Il est conseillé toujours
de travailler sur un arbre équilibré pour garantir une recherche la plus rapide possible.
L’opération d’équilibrage peut être faite à chaque fois qu’on insère un nouveau nœud où
à chaque fois que le déséquilibre atteint un certain seuil pour éviter le coût de l’opération
d’équilibrage qui nécessite une réorganisation de l’arbre.
56
3.3 Les tas (Heaps)
3.3.1 Introduction
Pour implémenter une file d’attente avec priorité, souvent utilisée dans les systèmes
d’exploitation, on peut utiliser :
– Une file d’attente ordinaire (sans priorité), l’insertion sera alors simple (à la fin) en
O(1), mais le retrait nécessitera la recherche de l’élément le plus prioritaire, en O(n).
– Un tableau (ou une liste) trié où le retrait sera en O(1) (le premier élément), mais
l’insertions nécessitera O(n).
Les tas apportent la solution à ce problème.
3.3.2 Définition
Un tas (heap en anglais) est un arbre qui vérifie les deux propriétés suivantes :
1. C’est un arbre binaire complet c’est-à-dire un arbre binaire dont tous les niveaux sont
remplis sauf éventuellement le dernier où les éléments sont rangés le plus à gauche
possible.
57
3.3.3 Opérations sur les tas
Pour coder une file d’attente avec priorité par un tas, on doit définir les opérations
d’ajout et de retrait.
[Link] Ajout
Pour ajouter un nouvel élément dans la file avec priorité c-à-d dans le tas on doit :
2. Attacher ce nœud dans le dernier niveau dans la première place vide le plus à gauche
possible (créer un nouveau niveau si nécessaire). On obtient toujours un arbre binaire
complet mais pas nécessairement un tas.
3. Comparer la clé du nouveau nœud avec celle de son père et les permuter si nécessaire,
puis recommencer le processus jusqu’il n’y ait plus d’éléments à permuter.
[Link] Retrait
58
1. Remplacer la valeur de la racine par la valeur de l’élément le plus à droite dans le
dernier niveau.
2. Supprimer de l’arbre cet élément (le plus à droite dans le dernier niveau), on obtient
un arbre binaire mais pas nécessairement un tas.
3. On compare la valeur de la racine avec les valeurs de ses deux fils et on la permute
avec la plus grande. On recommence le processus jusqu’il n’y ait plus d’éléments à
permuter.
Exemple :
Les tas peuvent être implémentés dynamiquement exactement comme les ARB, et sont
utilisés par le même modèle.
Une représentation statique très efficace utilisant des tableaux est très utilisée en pratique,
elle consiste à ranger les éléments du tas dans un tableau selon un parcours en largeur :
59
On remarque sur le tableau obtenu que le fils gauche d’un élément d’indice i se trouve
toujours s’il existe à la position 2i, et son fils droit se trouve à la position (2i + 1) et son
père se trouve à la position i/2. Les opérations d’ajout et de retrait sur le tas statique se
font de la même façon que dans le cas du tas dynamique. Avec ce principe les opérations
d’ajout et de retrait se font d’une manière très simple et extrêmement efficace.
Les tas sont utilisés même pour le tri des tableaux : on ajoute les éléments d’un tableau à
un tas, puis on les retire dans l’ordre décroissant.
60
Chapitre 4
Les graphes
4.1 Introduction
La notion de graphe est une structure qui permet de représenter plusieurs situations
réelles, en but de leur apporter des solutions mathématiques et informatique, tel que :
– Les réseaux de transport (routiers, ferrés, aériens · · · ),
– Les réseaux téléphoniques, électriques, de gaz,· · · etc,
– Les réseaux d’ordinateurs,
– Ordonnancement des tâches,
– Circuits électroniques,
– ···
Les arbres et les listes linéaires chaînées ne sont que des cas particuliers des graphes.
4.2 Définitions
4.2.1 Graphe
Un graphe est défini par un couple (S, A) où S est un ensemble de sommets (nœuds ou
points) et A est un sous ensemble du produit cartésien (S ⇥ S) représentant les relations
existant entre les sommets.
Exemple : S = 1, 2, 3, 4, 5, 6, 7
A = (1, 2), (1, 4), (2, 5)(3, 5), (3, 6), (4, 2), (5, 4), (6, 6)
61
4.2.2 Graphe orienté
C’est un graphe où les relations entre les sommets sont définies dans un seul sens
(exemple précédent). Dans ce cas les relations sont appelées "arcs".
C’est un graphe où les relations sont définies dans les deux sens. Dans ce cas, les
relations sont appelées "arêtes".
C’est un graphe orienté ou non où à chaque arc ou arête correspond une valeur ou une
étiquette représentant son coût (ou distance).
62
4.2.5 Origine et extrémité
4.2.6 Chemin
4.2.7 Circuit
4.2.8 Chaine
4.2.9 Cycle
Un graphe connexe est un graphe où pour tout couple de sommets (x, y), il existe une
chaîne d’arcs les joignant.
Exemple : Pour le couple (1, 6), il existe une chaîne d’arcs, et il n’en existe pas pour (1,7).
Un graphe fortement connexe est un graphe où pour tout couple de sommets (x, y), il
existe un chemin d’arcs les joignant.
63
Exemple : Pour (3, 2) il existe un chemin {(3, 5), (5, 4), (4, 2)} mais il n’en existe pas pour
(2, 3).
Les graphes peuvent être représentés en deux manières : en listes d’adjacence (dyna-
mique) ou en matrice d’adjacence (statique)
Dans cette représentation, les successeurs d’un nœud sont rangés dans une liste linéaire
chainée. Le graphe est représenté par un tableau T où T [i] contient la tête de la liste des
successeurs du sommet numéro i. Le graphe (S, A) présenté au début de cette section peut
être représenté comme suit :
Les listes d’adjacence sont triées par numéro de sommet, mais les successeurs peuvent
apparaître dans n’importe quel ordre.
64
4.3.2 Matrice d’adjacence
Dans cette représentation le graphe est stocké dans un tableau à deux dimensions de
valeurs booléennes ou binaires. Chaque case (x, y) du tableau est égale à vrai (1) s’il existe
un arc de x vers y, et faux (0) sinon. Dans la matrice suivante, on représente le graphe
précédent (1 pour vrai et 0 pour faux ) :
1 2 3 4 5 6 7
1 0 1 0 1 0 0 0
2 0 0 0 0 1 0 0
3 0 0 0 0 1 1 0
4 0 1 0 0 0 0 0
5 0 0 0 1 0 0 0
6 0 0 0 0 0 1 0
7 0 0 0 0 0 0 0
Il est clair que la matrice d’adjacence d’un graphe non orienté est symétrique puisque
chaque arête existe dans les deux sens.
Pour un graphe étiqueté les valeurs de la matrice peuvent être les étiquettes elles-
mêmes, avec une valeur particulière pour les arcs inexistant. Par exemple le graphe étiqueté
de l’exemple peut être représenté comme suit :
1 2 3 4 5 6 7
1 -1 10 -1 200 -1 -1 -1
2 -1 -1 -1 -1 125 -1 -1
3 -1 -1 -1 -1 22 17 -1
4 -1 96 -1 -1 -1 -1 -1
5 -1 -1 -1 12 -1 -1 -1
6 -1 -1 -1 -1 -1 3 -1
7 -1 -1 -1 -1 -1 -1 -1
65
La représentation matricielle permet de tirer des conclusions intéressantes sur les graphes
en utilisant les calculs matriciels.
De même que pour les arbres, il est important de pouvoir parcourir un graphe selon
certaines règles, cependant, le parcours des graphe est un peut différent de celui des arbres.
Dans un arbre, si on commence à partir de la racine on peut atteindre tous les nœuds,
malheureusement, ce n’est pas le cas pour un graphe où on est obligé de reprendre le
parcours tant qu’il y a des sommets non visités. En plus un graphe peut contenir des
cycles, ce qui conduit à des boucles infinies de parcours.
Il existe deux types de parcours de graphes : le parcours en profondeur d’abord (Depth
First Search) et le parcours en largeur d’abord (Breadth First Search).
Le principe du DFS est de visiter tous les sommets en allant le plus profondément
possible dans le graphe.
66
Var Graphe : Tableau[1..n,1..n] de booleen ;
Visité : Tableau[1..nn] de booleen ;
Procédure DFS( Sommet : entier ;);
var i : entier ;
Début
Visité[Sommet] Vrai ;
Afficher(sommet) ;
Pour i de 1 à n faire
Si (Graphe[sommet,i] et non Visité[i]) Alors
DFS(i)
Fin Si;
Fin Pour;
Fin;
Appel :
Pour s de 1 à n faire
Si (Non Visité[s]) Alors
DFS(s)
Fin Si;
Fin Pour;
Trace : 1 2 5 4 3 6 7
67
4.4.2 En largeur d’abord (BFS)
Dans ce parcours, un sommet s est fixé comme origine et l’on visite tous les sommet
situés à une distance k de s avant de passer à ceux situés à k + 1. On utilise pour cela une
file d’attente.
Fin;
Appel :
Pour s de 1 à n faire
Si (non Visité[s]) Alors
BFS(s)
Fin Si;
Fin Pour;
Trace : 1 2 4 5 3 6 7
68