Telechargé par pursang2006

Structure de données

publicité
Algorithmique et Structure de
Données
DUT MCW1
1
Qu'est ce que l'algorithmique ?

Un algorithme est une "spécification d'un
schéma de calcul sous forme d'une suite
finie d'opérations élémentaires obéissant à
un enchaînement déterminé", ou encore : la
description des étapes à suivre pour réaliser
un travail.
DUT MCW1
2
Programmation ?

Un programme
est la traduction
d'un algorithme
dans un langage
accepté par la
machine.
problème
Analyse
Algorithme
Programmation
Programme évolué
Compilation

Un algorithme, à
l'inverse d'un
programme, est
indépendant du
langage de
programmation
(et donc de la
machine).
Programme en langage machine
Édition des liens
Programme exécutable
Tests
Fonctionne correctement ?
Rapide ?
Programme accepté
DUT MCW1
3
Le langage algorithmique

Pour communiquer, deux personnes utilisent un
langage commun. Les informaticiens ont aussi
besoin d'un langage plus ou moins codifié pour se
comprendre, il faut donc se définir un langage
algorithmique.
Ce langage doit être:





spécialisé (pour écrire des algorithmes, pas des poèmes
ni des recettes de cuisine)
de haut niveau (déchargé de détails techniques, ce n'est
pas un langage de programmation
concis ("si ça ne tient pas en une page, c'est que c'est
trop long")
modulaire
typé
DUT MCW1
4
Les variables





Un algorithme manipule des données qui sont stockées
dans des variables. Chaque variable possède un nom,
donné par celui qui écrit l'algorithme. Elle possède
également un emplacement mémoire.
On peut voir la mémoire d'un ordinateur comme une
suite d'emplacements, chacun identifié par une adresse.
Les variables nécessitent des emplacement différents l’un
de l’autre.
Le type de la variable est donc important. L'adresse
d'une variable est en fait l'emplacement de là où elle
commence.
Type de données de base : entiers (12, 732),
caractères ('a', 'g', '3'), réels (-12.3 0,5 ), chaînes (de
caractères), booléens (vrai ou faux).
DUT MCW1
5
Les instructions (1)



L'affectation :
variable  expression
Lecture d'une donnée :
lire( une variable )
Affichage d'une expression :
écrire( liste d'expressions, de variables ou de constantes )
Sélection si :
si expression-logique alors
instruction(s) à effectuer si
l'expression est vraie
finsi

DUT MCW1
6
Les instructions (2)
Sélection si-sinon :
si expression-logique alors
instruction(s) à effectuer si
l'expression est vraie
sinon
instruction(s) à effectuer si
l'expression est fausse
finsi

DUT MCW1
7
Les instructions (3)
Itération répéter :
répéter
instruction(s) à répéter
jusqu‘à expression-logique
 Itération tant-que :
Tant que expression-logique
instruction(s) à répéter
fintantque
 Itération pour :
Pour variable  val-init à val-fin faire
instruction(s) à répeter
finpour

DUT MCW1
8
Écriture d'un algorithme

On donnera un nom à tout algorithme, afin de
pouvoir le réutiliser.
Il est essentiel de préciser (s'il y en a) :





les variables en entrée (les données du problèmes);
les variables en sorties (les résultats);
les variables à la fois en entrée et en sorties (les
données modifiées par l'algo);
les variables locales (utilisées juste dans cet algo).
Les variables autres que les variables locales sont
les paramètres formels de l'algorithme.
Lorsqu'un autre algo appelle cet algo, c'est avec
des paramètres effectifs.
DUT MCW1
9
Exemple d’algorithme
Algorithme Bonjour()
// simple ecriture d'un message de bienvenue
écrire("Bonjour les pogrammeurs !!")
Fin
Algo Multiplication(entier x, entier y) : retourne un entier
/* qui fait la multiplication des 2 entiers x et y avec des additions */
variables locales : entiers i, result ;
début
result <- 0;
pour i<- 1 à y faire
result <- result + x;
finpour
retourner result;
Fin
Algo Principal()
a, b : entiers ;
début
Bonjour();
écrire ("donnez deux entiers : ");
lire(a,b);
écrire (" leur multiplication donne : ", Multiplication(a,b) );
fin
DUT MCW1
10
Récursivité
Un algorithme est dit récursif lorsqu'il s'appelle lui
même. Attention, la récursivité peut être cachée si
par exemple un algorithme A appelle un
algorithme B qui appelle l'algorithme A.
Algo Factorielle(entier n) : retourne un entier
début
si n=1 alors
retourner 1;
sinon
retourner n*factorielle(n-1);
fin

DUT MCW1
11
exemples d'algos récursifs (1)
Algo pgcd(a,b:entier) : retourne un entier
// plus grand diviseur commun
début
si a=b alors
retourner a;
sinon si a>b alors
retourner pgcd(a-b,b);
sinon
retourner pgcd(b-a,a);
finsi
finsi
fin
DUT MCW1
12
exemples d'algos récursifs (2)
Algo puissance(x:reel, n:entier) :retourne un reel
// x^n d'une maniere moins habituelle
début
si n=0 alors
retourner 1;
sinon si n=1 alors
retourner x;
sinon si n=2 alors
retourner x*x;
sinon si n est pair alors
retourner puissance(x*x,n/2);
sinon
retourner x*puissance(x*x,(n-1)/2);
finsi
finsi
finsi
finsi
fin
DUT MCW1
13
Comment écrire un algo récursif :



exprimer le problème de "taille" n en fonction du
même problème de taille(s) inférieur(s)
mettre une condition d'arrêt : lorsque le problème
est de taille suffisamment petite pour être résolu
sans appel récursif. Attention : penser à tous les
cas d'arrêts !!
(on peut constater sur les exemples que c'est le
cas à chaque fois)
Un algorithme récursif est plus lent qu'un
algorithme itératif car il y a la gestion des appels
de fonctions (empilement et dépilement du
contexte).
DUT MCW1
14
Qualités d'un algorithme





Qualité d'écriture : un algorithme doit être structuré,
indenté, modulaire, avec des commentaires, etc.
Terminaison : le résultat doit être atteint en un nombre fini
d'étapes. Il ne faut donc pas de boucles infinies, il faut
étudier tous les cas possibles de données, ...
Validité : le résultat doit répondre au problème demandé.
Performance : étude du coût (complexité) en temps et en
mémoire.
La complexité en mémoire (c'est à dire la place mémoire
prise par l'algorithme) est un problème de moins en moins
primordial vu les capacités techniques actuelles. Remarque :
ne pas confondre un problème de mémoire saturée qui vient
du programme avec la complexité mémoire de l'algorithme.
On distingue la complexité en moyenne et la complexité
dans le pire des cas et parfois on s'intéresse aussi au
meilleur des cas.
DUT MCW1
15
Complexité (1)
Algo Somme(entier N) :
retourne un entier
locales : ...
Début
/* somme des N premiers
entiers*/
S  0;
nb  1;
tant que nb<=N faire
S  S + nb;
nb  nb+1;
fintantque;
retourner S;
fin
DUT MCW1

Cet algorithme demande:
2 affectations;

N+1 comparaisons;

N sommes et
affectations;

N sommes et
affectations
Ce qui fait au total 5N+3
opérations
élémentaires.
Ce qui nous intéresse
c'est un ordre de
grandeur donc : la
complexité de cet algo
est de l'ordre de N, ou
encore linéaire en N.



16
Complexité (2)
Algo Somme(entier N) : retourne un entier;
locales : ...;
Début
// somme des N premiers entiers
S  N*(N+1)/2;
retourner S;
fin

Même problème résolu en temps constant
(indépendant des données) !
DUT MCW1
17
Structure de données : liste linéaire (Liste chaînée)




Une liste linéaire est la forme la plus courante
d'organisation des données. On l'utilise pour stocker
des données qui doivent être traitées de manière
séquentielle. La structure doit également être
évolutive, c'est à dire que l'on doit pouvoir ajouter et
supprimer des éléments.
Définition : Une liste est une suite finie
(éventuellement vide) d'éléments de même type
repérés selon leur rang dans la liste.
On remarque que l'ordre des éléments est
fondamental. Mais attention, il ne s'agit pas d'un ordre
sur les valeurs des éléments mais d'un ordre sur les
places dans la liste (rang) !
Chaque élément est rangé à une certaine position. Il
ne faut pas confondre le rang et la position !
DUT MCW1
18
Primitives (1)
On définit le type abstrait de données par le définition des
primitives qui permettent de le manipuler :

liste créer_liste() : creation d'une liste vide

position début (liste L) : retourne la position du premier
élément de la liste, INCONNUE si la liste est vide

position fin (liste L) : retourne la position du dernier
élément de la liste, INCONNUE si la liste est vide

position suivante (position p, liste L) : retourne la position
de l'élément qui suit celui en position p, INCONNUE si on
sort de la liste

position précédente (position p, liste L) : retourne la
position de l'élément qui précède celui en position p,
INCONNUE si on sort de la liste

élément accès (position p,liste L) : retourne l'élément en
position p
DUT MCW1
19
Primitives (2)







entier longueur (liste L) : retourne le nombre d'éléments
contenus dans la liste
insérer (element e, rang r, liste L) : rajoute l'élément dans
la liste (qui est donc modifiée) au rang r
supprimer (rang r, liste L) : supprime l'élément de rang r
dans la liste (qui est donc modifiée)
booléen liste_est_vide (liste L) : teste si la liste est vide,
retourne VRAI ou FAUX
elément ieme (rang r, liste L) : retourne l'element de rang r
ajouter (element e, position p, liste L) : rajoute l'élément
dans la liste (qui est donc modifiée) après celui en position
p
enlever (position p, liste L) : supprime l'élément de position
p dans la liste (qui est donc modifiée)
DUT MCW1
20
Exemples d'utilisation :
Algo Afficher_Liste(liste L)
locales : position i;
Début
i<-debut(L);
tant que i<>INCONNUE faire
afficher(acces(i,L));
i <- suivante(i,L);
fin tant que
Fin
Algo Nb_Occurence(liste L,element e) : retourne un entier
locales : position i; entier nb;
Debut
i<-debut(L);
nb<-0;
tant que i<>INCONNUE faire
si (element_egaux(e,acces(i,L)) alors
nb<-nb+1;
i <- suivante(i,L);
fin tant que
retourner nb;
fin
DUT MCW1
21
Implémentation contiguë (1)



Les éléments sont rangés les uns à côté
des autres, dans un tableau.
La i-ème case du tableau contient le ième
élément de la liste.
Le rang est égal à la position (des entiers
tout simples) !
type position = entier
type liste = tableau[1..N] d‘éléments
DUT MCW1
22
Implémentation contiguë (2)
Algo acces (position p, liste L):retourne un element
debut
retourner L[p];
fin
Algo debut (liste L):retourne une position
début
si N >= 1 alors
retourner 1;
sinon
retourner INCONNUE;
finsi
fin
DUT MCW1
23
Implémentation contiguë (3)
Algo suivante(position p,liste L)
: retourne une position
début
si p<N alors
retourner p+1;
sinon
retourner INCONNUE;
finsi
fin
DUT MCW1
24
Implémentation contiguë (4)
Algo inserer(element e, rang r, liste L)
locales rang i
début
// décaler pour faire un trou
pour iN à r (en décrémentant i) faire
L[i+1] <- L[i]
finpour
// mettre l'element
L[r]  creer_element(e)
/* changer la taille car il y a un elt de
plus */
... PROBLEME !!! ...
DUT MCW1
25
Implémentation contiguë (5)





Comment changer le nombre d'éléments dans le
tableau ?
Il est où dans la structure de données ce nombre
d'éléments ?
Avec tableau[1..N] d'éléments on dit bien qu'il y a
N éléments mais on n'offre pas la possibilité de
modifier ce N.
C'est plus précis de faire :
type liste = {
tab : tableau d'éléments;
N : entier // nb effectifs d'éléments
}
Attention, on ne parle plus de L[i] mais de
L.tab[i]. Pas de N mais de L.N
DUT MCW1
26
Implémentation contiguë (6)
Algo longueur (liste L)
retourner L.N;
fin
Algo acces (position p, liste L): retourne un element
retourner L.tab[p];
fin
Algo fin (liste L) : retourne une position
retourner L.N;
fin
Algo est_vide (liste L) : retourne un booleen
si L.N=0 alors
retourner VRAI;
sinon
retourner FAUX;
fin
DUT MCW1
27
Implémentation contiguë (7)
Algo inserer(element e, rang r, E?S liste L)
locales rang, i;
début
// decaller pour faire un trou
pour iL.N à r (en decrementant i) faire
L.tab[i+1]  L.tab[i];
finpour
// mettre l'element
L.tab[r]  creer_element(e);
// changer la taille car il y a un elt de plus
L.N  (L.N) + 1;
fin

L.tab est un tableau d'éléments. Mais un tableau de
combien d'éléments ?

On est obligé de se poser la question car il faut une zone de
mémoire contigüe et donc il faut la réserver.
DUT MCW1
28
Implémentation contiguë : Tableau de taille fixe
On réserve à la création pour MAX éléments. Il faut néanmoins penser à tester
lors des insertions que l'on ne dépasse pas ce MAX.
Algo creer_liste_vide () : retourne une liste
locales liste L;
début
L.t  allouer_memoire(MAX);
L.N  0;
fin

Algo inserer (element e, rang r, E/S liste L)
locales rang, i;
début
si L.N=MAX alors
écrire "erreur !!« ;
sinon
.....
// la suite normale
fin
DUT MCW1
29
Implémentation chaînée (1)



Les éléments ne sont pas rangés les uns à coté des
autres.
On a besoin de connaître, pour chaque élément, la
position (l'adresse) de l'élément qui le suit.
La position n'a donc plus rien à voir avec le rang, mais
c'est l'adresse d'une structure qui contient l'élément ainsi
que la position du suivant .
DUT MCW1
30
Implémentation chaînée (2)
type cellule = {
valeur : élément;
suivant : adresse d'une cellule
}
type liste = adresse d'une cellule
 Une adresse s'appelle aussi un pointeur. On note
l'adresse d'un objet par &objet.

On note l'objet qui est à l'adresse ad par *ad.
L'adresse nulle sera notée VIDE.
DUT MCW1
31
Implémentation chaînée (3)
Algo debut (liste L) : retourne une position
début
// adresse de la 1ere cellule
retourner L;
fin
Algo creer_liste_vide () : retourne une liste
début
retourner VIDE;
fin
Algorithme est_vide (liste L) : retourne un booléen
début
retourner L=VIDE;
fin
DUT MCW1
32
Implémentation chaînée (4)
Algo suivante (position p,liste L) : retourne une position
debut
retourner (*p).suivant
Fin
Algo acces (position p,liste L) : retourne un element
debut
retourner (*p).valeur
fin
Algo longueur (liste L) : retourne un entier
locales :position p, entier l;
debut
l  0;
p  L;
tant que p<>VIDE faire
p  (*p).suivant;
l  l+1;
fintantque
retourner l;
fin
DUT MCW1
33
Implémentation chaînée (5)
Algo inserer (element e, rang k,E/S liste L)
locales : position p, nouv : adresse d'une cellule, entier(rang) i;
debut
//allouer de la memoire pour une cellule et y mettre e dans valeur
nouv  creer_cellule(e)
si k=1 alors // cas particulier : insertion en tete
(*nouv).suiv  L
L  nouv ; // ATTENTION : L est modifie !
sinon
// se positionner sur le k-1 ieme élément ATTENTION : s'il existe!
p  L
i  1
tant que p<>VIDE et i<k-1 faire
p  (*p).suivant
i  i+1
fintantque
si p=VIDE alors
erreur "la liste ne comporte pas assez d'éléments"
sinon
(*nouv).suivant  (*p).suivant
(*p).suivant  nouv
finsi
finsi
fin
DUT MCW1
34
Implémentation chaînée : Remarques



on est obligé de traiter le cas particulier d'insertion
en tête mais l'algorithme marche bien aussi si on
insère en queue.
Dans le cas d'insertion en tête, L est modifiée. Il
faut préciser dans l'écriture de l'algorithme que le
paramètre L est en entrée et sortie. On peut
écrire par exemple :
Algo inserer(element e,rang k, E/S liste L)
Pour cette raison, on pourra préférer retourner la
nouvelle liste, et donc déclarer l'algorithme :
Algo inserer(element e, rang k, liste L)
: retourne une liste
et terminer l'algo par retourner L;
DUT MCW1
35
Liste chaînée avec cellule de tête (1)


Lors d'une insertion ou d'une suppression, il
faut traiter à part le cas de la première
cellule. Pour éviter ce traitement à part, on
peut mettre une cellule bidon en tête de
liste.
L n'est plus la position de la première
cellule, mais celle d'une cellule bidon qui est
toujours là. En particulier, une liste vide
(donc lors de la création !) est une liste
contenant cette seule cellule bidon.
DUT MCW1
36
Liste chaînée avec cellule de tête (2)
algorithme creer_liste_vide() : retourne une liste
locales adresse de cellule : bidon
début
bidon <- creer_cellule(n_importe_quoi);
//si ce n'est pas déjà fait dans creer_cellule
bidon.suivant <- VIDE;
retourner bidon;
fin
algorithme est_vide (liste L) : retourne un booleen
début
si (*L).suivant = VIDE alors
retourner VRAI;
sinon
retourner FAUX;
fin
DUT MCW1
37
Liste chaînée avec cellule de tête (3)
Et donc l'insertion ne demande plus de cas particulier en tête :
Algo inserer (element e, rang k,E/S liste L)
locales : position p; ad d'une cellule nouv; entier(rang) i;
debut
/* allouer de la mémoire et y mettre e dans le chp valeur*/
nouv  creer_cellule(e);
// se positionner sur le k-1 ième élément
p  L;
i  1;
tant que p<>VIDE et i<k-1 faire
p <- (*p).suivant;
i<-i+1;
fintantque
// (si k=1 on n'a pas bouge !)
si p=VIDE alors
erreur "la liste ne comporte pas assez d'éléments";
sinon
(*nouv).suivant  (*p).suivant;
(*p).suivant  nouv;
finsi
fin

DUT MCW1
38
Listes doublement chaînées (1)


Certaines actions peuvent demander de parcourir
la liste à l'envers.
Si c'est une action souvent demandée, il peut être
avantageux de rajouter un pointeur vers l'élément
précédent dans la structure de données.
DUT MCW1
39
Listes doublement chaînées (2)
type cellule = {
valeur : element;
suivant : adresse d'une cellule;
precedent : adresse d'une cellule;
}
type liste = adresse d'une cellule;
 Il faut bien sûr penser à le mettre à jour comme il faut
dans toutes les primitives !
DUT MCW1
40
Listes chaînées circulaires

Et on peut bien sûr faire des listes circulaires doublement
chaînées.
DUT MCW1
41
Structure de données : pile
DUT MCW1
42
Structure de données : pile


Une pile est une liste linéaire particulière : on ne
peut accéder (= consulter, ajouter, supprimer)
qu'au dernier élément, que l'on appelle le
sommet de la pile. Dans une pile, il est
impossible d'accéder à un élément au milieu !
Les piles sont aussi appelées structures LIFO
pour Last In First Out : dernier-entré-premiersorti.
C'est une structure très utilisée en informatique.
Par exemple, les appels de fonctions d'un
programme utilisent une pile pour sauvegarder
toutes les informations (variables, adresse de
l'instruction de retour, etc. ).
DUT MCW1
43
Primitives (1)




pile créer_pile() : création d'une pile
vide
pile créer_pile (entier taille_max ) :
creation d'une pile vide d'au maximum
taille_max éléments
empiler (element, pile) : met l'élément
en sommet de pile, erreur si la taille est
limitée et la pile pleine
dépiler (pile) : enlève de la pile
l'élément en sommet de pile, erreur si la
pile est vide
DUT MCW1
44
Primitives (2)




élément dépiler (pile) : enlève
l'élément en sommet de pile et le
retourne, erreur si la pile est vide
élément sommet (pile) : retourne
l'élément en sommet de pile, erreur si la
pile est vide
booléen pile_est_vide (pile) : teste si
la pile est vide
booléen pile_est_pleine (pile) : teste
si la pile est pleine (seulement dans le
cas d'une taille limitée)
DUT MCW1
45
Implémentation d'une pile par un tableau (1)
type pile = {
tab : tableau d'éléments;
s : entier; // indice du sommet
}
Algorithme créerPile() : retourne une pile
locales : pile P;
début
P.s  0;
// alloc du tableau si nécessaire
retourner P;
fin
DUT MCW1
46
Implémentation d'une pile par un tableau (2)
Algorithme empiler (élément e, E/S pile p)
début
p.s  p.s+1; //si la pile n'est pas pleine !
p.tab[p.s]  e;
fin
Algorithme sommet (pile P): retourne un element
début
si P.s <> 0 alors
retourner P.tab[P.s];
sinon
erreur "on ne peut pas acceder au sommet d'une
pile vide !!« ;
finsi
fin
DUT MCW1
47
Implémentation d'une pile par un tableau (3)



Le problème est le problème des tableaux,
c'est à dire qu'on est limité par la taille.
L'avantage des tableaux sur les listes
chaînées c'est l'accès direct à un élément,
or ça ne nous intéresse pas dans les piles.
Donc c'est mieux d'implémenter par une
liste chaînée.
DUT MCW1
48
Implémentation d'une pile par liste chaînée (1)

Exemple : une liste chaînée avec lien vers le
premier (pour ne pas perdre la pile) et lien vers le
sommet :
DUT MCW1
49
Implémentation d'une pile par liste chaînée (2)
type cellule = {
valeur : element;
suivant : adresse d'une cellule;
}
type pile = {
prem : adresse d'une cellule;
sommet : adresse d'une cellule;
}
DUT MCW1
50
Implémentation d'une pile par liste chaînée (3)
Algorithme empiler (element e, E/S pile p)
locales nouv : adresse d'une cellule;
début
//allocation et initialisation des champs
nouv = creer_cellule();
(*nouv).valeur  e;
(*nouv).suivant  VIDE;
// faire les liens
si p.sommet <> VIDE alors // la pile pas vide
(*(p.sommet)).suivant  nouv;
p.sommet  nouv;
sinon // la pile est vide
p.sommet  nouv;
p.prem  nouv;
finsi
fin
DUT MCW1
51
Implémentation d'une pile par liste chaînée (4)

Ou bien aussi, puisqu'on fait des ajouts en tête, on
peut se passer du lien vers le premier
DUT MCW1
52
Implémentation d'une pile par liste chaînée (5)
type cellule = {
valeur : élément;
suivant : adresse d'une cellule;
// le PRECEDENT en fait ...
}
type pile = adresse d'une cellule;
// le sommet
DUT MCW1
53
Implémentation d'une pile par liste chaînée (6)
Algo empiler (élément e, E/S pile p)
// attention p est modifie !!
locales nouv : adresse d'une cellule;
début
/* allouer de la mémoire et initialiser les
champs de la nouvelle cellule */
nouv=creer_cellule();
(*nouv).valeur  e;
// faire les liens : ajout en tête
(*nouv).suivant  p;
p  nouv;
fin
DUT MCW1
54
exemple d'utilisation d'une pile :




Expressions arithmétiques postfixées et infixées
Les expressions arithmétiques sont habituellement écrites
de manière infixe, c'est à dire l'opérateur entre ses deux
opérandes (ou avant si c'est un opérateur unaire).
On trouve aussi une notation postfixée, que l'on appelle
également notation polonaise : l'opérateur est placé
après ses opérandes.
Par exemple :

13 + 8 s'écrit en postfixe : 13 8 +

(3+5) x 9 s'écrit en prefixe : x + 3 5 9
L'intérêt de cette notation est qu'il n'y a plus besoin de
connaître les priorités des opérateurs, et donc plus besoin
de parenthèses (qui servent à contrer les priorités).
DUT MCW1
55
Structure de donnée : file

Une File est une liste linéaire particulière:





on ne peut ajouter qu'en queue,
consulter qu'en tête,
et supprimer qu'en tête.
Comme pour une file d'attente ... !
Les files sont aussi appelées structures FIFO
pour First In First Out, cad premier-entrépremier-sorti
DUT MCW1
56
File : Primitives







file créer_file () : création d'une file vide
file créer_file (entier taille_max ) : creation d'une file
vide d'au maximum taille_max éléments
enfiler (element, file) : met l'élément à la fin de la file,
erreur si la taille est limitées et la pile pleine
défiler (file) : enlève le premier élément de la file,
erreur si la file est vide
élément consulter (file) : retourne le premier élément
de la file , erreur si la file est vide
booléen file_est_vide (file) : teste si la file est vide
booléen file_est_pleine (file) : teste si la file est
pleine (seulement dans le cas d'une taille limitée)
DUT MCW1
57
File : Implémentations par un tableau
type file = {
t : tableau[1..MAX] d'elements;
deb : entier;
fin : entier;
}
enfiler (element e, E/S file F)
debut
si F.fin < MAX alors
F.fin  F.fin +1;
F.t[F.fin]  e;
sinon
file pleine ???;
finsi
fin
defiler (E/S file F)
debut
si F.debut <= F.fin MAX alors
F.debut  F.debut +1;
sinon
erreur "file vide« ;
finsi
fin
DUT MCW1
58
File : Implémentation par un tableau circulaire
DUT MCW1
59
File : Implémentation par un tableau circulaire
enfiler (element e, E/S file F)
debut
si F.fin < MAX alors
F.fin  F.fin +1;
sinon
F.fin  1;
finsi
F.t[F.fin]  e;
fin
Ou encore :
F.fin  ((F.fin+1) modulo (MAX+1) ) -1
DUT MCW1
60
File : Implémentation par un tableau circulaire

Problème : comment savoir quand la file est pleine ?

deux cas :
Elle est pleine quand fin=deb-1 ou deb=1 et fin=MAX

quand est-elle vide ? (elle devient vide après avoir eu un seul
élément qu'on a défilé)

Elle est vide quand fin=deb-1 ou deb=1 et fin=MAX
DUT MCW1
61
File : Implémentation par un tableau circulaire

Comment différencier les deux ?

Idée 1 : rajouter un booléen vide dans la
structure, initialisé à VRAI lors de la
création, qui devient FAUX dès qu'on enfile,
et redevient VRAI si l'on défile lorsque
deb=fin.

Idée 2 : et si on faisait une liste chaînée ?
On ne sera plus dérangé avec des
limitations de taille !
DUT MCW1
62
Implémentation d'une file par une liste chaînée
type file {
debut : adresse d'une cellule;//la première
fin : adresse d'une cellule; //la dernière
}
DUT MCW1
63
Arbre binaire
Définitions
Primitives
Primitives
de construction/modifications
Parcours
Arbres
binaires particuliers
Implémentations
Tableaux FG et FD

Listes chaînées

DUT MCW1
64
Arbre binaire




La structure d'arbre est l'une des plus
importantes et des plus spécifiques de
l'informatique.
Par exemple : organisation des fichiers dans les
systèmes d'exploitation, représentation des
programmes traités par un ordinateur, d'une
table des matières, d'un questionnaire, d'un
arbre généalogique...
C'est une structure de données qui permet
d'écrire des algorithmes très performants.
Nous verrons tout d'abord les arbres binaires, qui
sont un cas particulier des arbres généraux.
Une propriété intrinsèque de cette structure est
la récursivité.
DUT MCW1
65
Définitions


Soit un ensemble de sommets (ou encore noeuds) auxquels
sont associés des "valeurs" (les éléments à stocker : entier,
chaîne, structure, ...).
Un arbre binaire est défini récursivement de la manière
suivante : un arbre binaire est composé :

soit d'un seul sommet appelé racine,

soit d'un sommet racine à la gauche duquel est accroché un
sous-arbre binaire gauche

soit d'un sommet racine à la droite duquel est accroché un
sous-arbre binaire droit

soit d'un sommet racine auquel sont accrochés un sous-arbre
binaire droit et un sous-arbre binaire gauche.
DUT MCW1
66
Terminologie

fils gauche de x = le sommet (s'il existe)
accroché à la gauche de x

fils droit de x = le sommet (s'il existe) accroché à
la droite de x

fils de x = le ou les deux sommets accrochés sous
x

père de x = le sommet p tel que x est fils de p

frère de x = un sommet (s'il existe) qui a le même
père

sommet interne = un sommet qui a au moins un
fils ( gauche ou droit ou les deux)
DUT MCW1
67
Terminologie
feuille = un sommet qui n'a pas de fils
 branche = un chemin de fils en fils de la racine
vers une feuille
 branche gauche = la branche de fils gauche en
fils gauche
 branche droite = la branche de fils droit en fils
droit
 hauteur d'un sommet x = la longueur (en nb
d'arcs) du plus long chemin de x à une feuille
 hauteur d'un arbre = la hauteur de la racine
 profondeur d'un sommet x = la longueur (en nb
d'arcs) du chemin de la racine au sommet
Remarque : la racine de l'arbre n'a pas de père et
c'est le seul sommet comme ça.

DUT MCW1
68
Primitives






sommet racine (arbre B) : retourne la
racine de l'arbre
sommet fils_gauche (sommet x,arbre B) :
retourne le sommet fils gauche de x ou VIDE
sommet fils_droit (sommet x,arbre B) :
retourne le sommet fils droit de x ou VIDE
sommet pere (sommet x,arbre B) : retourne
le sommet père de x ou VIDE
element val (sommet x,arbre B) : retourne
l'élément stocké au sommet x
booleen est_vide (arbre B) : retourne vrai
ou faux
DUT MCW1
69
Primitives de construction/modifications






arbre créer_arbre_vide () : création
d'un arbre initialisé comme il faut
sommet créer_sommet(element e, arbre
B) : retourne le sommet
faire_FG(sommet père,sommet fg,arbre
B)
faire_FD(sommet père,sommet fd,arbre
B)
faire_racine (sommet rac, arbre B)
supprimer_feuille (sommet f, arbre B)
...
DUT MCW1
70
Exemple de manipulation d'un arbre binaire :
arbre B sommet s1,s2;
B  creer_arbre_vide();
s1  creer_sommet("chose",B);
faire_racine(s1,B);
s2  creer_sommet("Autre chose",B);
faire_FG(s1,s2,B);
faire_FD(s2,creer_sommet("chose3",B),B);
Peindre_en_Rouge(B);
...
DUT MCW1
71
Parcours
But : passer en revue (pour un traitement quelconque)
chaque sommet une et une seule fois.
//parcours de tous les sommets de l'arbre B
Algorithme Parcours (arbre B)
début
Parcours_Aux(racine(B),B);
Fin
// parcours du sous-arbre de racine r
Algorithme Parcours_Aux (sommet r,arbre B)
début
si r<>VIDE alors
Parcours_Aux(fils_gauche(r,B),B);
Parcours_Aux(fils_droit(r,B),B);
finsi
fin

DUT MCW1
72
Arbres binaires particuliers





On appelle arbre binaire complet un arbre binaire
tel que chaque sommet possède 0 ou 2 fils.
Un arbre binaire complet possède 2P+1 sommets
(nombre impair) dont P sommets internes et P+1
feuilles
On appelle arbre binaire parfait un arbre binaire
(complet) tel que chaque sommet soit père de
deux sous arbres de même hauteur
Un arbre binaire parfait possède 2h+1-1 sommets,
où h est la hauteur de l'arbre.
On appelle arbre binaire quasi-parfait un arbre
binaire parfait éventuellement grignoté d'un étage
en bas à droite.
DUT MCW1
73
Arbre binaire sous forme de tableaux FG et FD
type sommet = entier
type arbre = adresse de {
nbSommets : entier;
FG : tableau [1..MAX] de sommets;
FD : tableau [1..MAX] de sommets;
VAL : tableau [1..MAX] d'éléments;
}
DUT MCW1
74
Exemples de primitives :
Algorithme fils_gauche(sommet x,arbre B):retourne un sommet
debut
retourner (*B).FG[x];
fin
Algorithme pere (sommet x,arbre B):retourne un sommet
locales entier p;
debut
p  1;
tant que (p <= BnbSommets) et (BFG[p] != x) et
(BFD[p] != x) faire
p  p+1;
fintantque
si (p<= BnbSommets)
retourner p;
sinon
retourner VIDE;
finsi
fin
DUT MCW1
75
Exemples de primitives :
Algorithme racine (arbre B) : retourne un sommet
/* la racine est le seul sommet qui n'est fils de personne*/
locales : entier i;
VU : tableau de booleen /*marquage des sommets(vu ou pas)*/
debut
// allouer tableau de marquage
VU= allouer_memoire (B->nbSommets * booleens);
// init de ce tableau
pour i1 a (B->nbSommets) faire
VU[i]  faux; // aucun sommet n'a ete rencontre
finpour
// marquage des sommets rencontres dans FG ou FD
pour i<-1 a (B->nbSommets) faire
VU[B->FG[i]]  vrai;
VU[B->FD[i]]  vrai;
finpour
// chercher celui qui n'est pas marque (qui existe forcemment)
i  1;
tant que VU[i] faire
i  i+1;
fin tant que
retourner i;
fin
DUT MCW1
76
Amélioration
Pour réduire la complexité de cette implémentation, on
peut rajouter dans la structure une place pour stocker la
racine :
type sommet = entier;
type arbre = adresse de {
racine : sommet;
nbSommets : entier
FG : tableau [1..MAX] de sommets;
FD : tableau [1..MAX] de sommets;
VAL : tableau [1..MAX] d'éléments;
}

DUT MCW1
77
Primitives améliorées
// l'arbre est modifie, on precise donc bien E/S !!
Algorithme fait_racine (sommet r, E/S arbre B)
debut
(*B).racine  r;
Fin
Algorithme creer_vide () : retourne un arbre
locales : arbre B; entier i;
debut
B  allouer_memoire(necessaire);
(*B).nbSommets  0;
(*B).racine  VIDE;
retourner B;
Fin
Algorithme fait_FG (sommet p,sommet fg, E/S arbre B)
debut
(*B).FG[p] <- fg;
fin
DUT MCW1
78
Primitives améliorées
Algorithme creer_sommet(E/S arbre B,element e):
retourne un sommet
/* ajout du sommet sans liens
retourne le sommet créé pour ensuite pouvoir faire les
liens */
debut
(*B).nbSommets  (*B).nbSommets +1;
(*B).VAL[(*B).nbSommets]  creer_element(e);
//allouer memoire pour un element si necessaire
(*B).FG[(*B).nbSommets]  VIDE;
(*B).FD[(*B).nbSommets]  VIDE;
//(*B).PERE[(*B).nbSommets]  VIDE si besoin
retourner (*B).nbSommets;
fin
DUT MCW1
79
suppression





Si on effectue des suppressions de sommets, il va falloir
faire des décalages pour éviter les trous dans le tableau.
Pas simple car il ne faut pas seulement décaler, il faut
mettre à jour les bons numéros !!!
Il faut cependant modifier creer_sommet pour trouver
la première place libre dans le tableau (qui n'est pas
forcemment nbSommets).
Sinon on va consommer beaucoup de place mémoire qu'on
n'utilisera pas.
En outre, qu'est ce qu'une "place vide" ?? Il faut un élément
spécial element_vide, ce qui n'est pas toujours possible
suivant la nature des éléments (ex : qu'est ce qu'un nombre
vide ? 0 ? Non, 0 peut être un de ces nombres ! ).
DUT MCW1
80
suppression
Algorithme supprime_feuille (sommet f, E/S arbre B)
// f DOIT être une FEUILLE !
locale : sommet p
début
// supprimer le lien du pere
p  pere(f,B)
si ((*B).FG[p] = f) alors
(*B).FG[p]  VIDE ;
sinon
(*B).FD[p]  VIDE ;
finsi
/* "supprimer" la feuille = ne plus la compter dans
l'arbre et noter la place comme libre */
(*B).VAL[f]  ELEMENT_VIDE ;
(*B).nbSommets  (*B).nbSommets -1 ;
fin
DUT MCW1
81
Arbre binaire sous forme de listes chaînées
DUT MCW1
82
Arbre binaire sous forme de listes chaînées
type sommet = adresse de {
FG : adresse d'un sommet;
FD : adresse d'un sommet;
val : élément;
}
type arbre = sommet;
alias VIDE=adresse NULL;
 Un arbre est donc donné par l'adresse de sa racine. Un
sommet ou un arbre c'est la même chose.
DUT MCW1
83
primitives
Algorithme racine(arbre B) : retourne un sommet
retourner B;
fin
Algorithme fils_gauche(sommet x,arbre B) : retourne un
sommet
retourner (*x).FG;
fin
Algorithme valeur(sommet x,arbre B) : retourne un element
retourner (*x).val;
fin
Algorithme est_vide(arbre B) : retourne un booleen
retourner (B=VIDE);
fin
Algorithme est_feuille(sommet f,arbre B) : retourne un
booleen
retourner ( (*f).FG=VIDE et (*f).FD=VIDE);
fin
DUT MCW1
84
primitives
Algorithme pere(sommet x,arbre B) : retourne un sommet
// recherche RECURSIVE du pere de x dans le sous arbre (de racine) B
// rappel : arbre=sommet dans cette implémentation
locales sommets : tmp,p;
debut
si (est_vide(B)) alors retourner VIDE
sinon
p  B
// racine de B en fait
si (p=x) alors retourner VIDE //cas particulier pere de la racine
sinon
si ((*p).FG=x) ou ( (*p).FD=x) alors
retourner p
sinon
tmp <- pere(x, (*p).FG)
// recherche recursive dans le sous arbre gauche
si (tmp=VIDE) alors
// s'il n'est pas a gauche, c'est qu'il est a droite
tmp <- pere(x, (*p).FD)
finsi
retourner tmp
finsi
finsi
finsi
fin
DUT MCW1
85
Primitives améliorées

Pour réduire la complexité, il est préférable de rajouter
un pointeur vers le père dans la structure
type sommet = adresse de {
FG : adresse d'un sommet;
FD : adresse d'un sommet;
PERE : adresse d'un sommet;
val : élément;
}
type arbre = sommet;
DUT MCW1
86
Primitives améliorées
Algorithme fait_racine (sommet r,E/S arbre B)
// attention je rappelle que B est modifié
B  r;
fin
Algorithme creer_vide () : retourne un arbre
retourner VIDE;
fin
Algorithme fait_FG (sommet p,sommet fg,arbre B)
/*avec cette implémentation B en fait n'est pas
modifie */
Debut
(*p).FG  fg;
// (*fg).pere  p
fin
DUT MCW1
87
Primitives améliorées
Algorithme creer_sommet(element e,arbre B)
:retourne un sommet
//avec cette implémentation B en fait n'est pas modifie
locales
sommet nouv;
début
nouv= allouer_memoire(un sommet);
(*nouv).FG  VIDE;
(*nouv).FD  VIDE;
// (*nouv).pere  VIDE;
// alloc mémoire pour l'élément si besoin
(*nouv).val  creer_element(e);
retourner nouv;
fin
DUT MCW1
88
Primitives améliorées
Algorithme supprime_feuille (sommet f,E/S arbre B)
// larbre peut devenir vide donc etre modifie !!!!
locales : sommet p;
début
si (f=B) alors
// la feuille = la racine de l'arbre
// ==> l'arbre ne contenait que cette feuille
Liberer_memoire(f);
B  VIDE
sinon
p  pere(f);
// p  (*f).pere ?
si (*p).FG=f alors
// feuille a gauche
(*p).FG  VIDE;
sinon // feuille a droite
(*p).FD  VIDE;
finsi
liberer-_memoire(f);
finsi
fin
DUT MCW1
89
Arbre binaire de Recherche
Définitions
Recherche
Ajout
Suppression
Arbres
binaires de recherche équilibrés
Définitions
Rotations
Arbres
DUT MCW1
AVL
90
Définition


Un arbre binaire de recherche est un arbre
binaire tel que la valeur stockée dans
chaque sommet est:
 supérieure à celles stockées dans son
sous-arbre gauche
 inferieure à celles de son sous-arbre
droit.
Remarque : les éléments stockés dans
l'arbre peuvent être compliqués. Quand on
parle de valeur de l'élément il faut alors
comprendre valeur de la clé de l'élément.
DUT MCW1
91
Recherche
Algorithme Recherche (élément e, arbre B)
: retourne un booléen
debut
retourner
RechercheAux(e,racine(B),B);
fin
DUT MCW1
92
Recherche
Algorithme RechercheAux (element e, sommet r, arbre B)
:retourne un booleen
/*vérifie si e appartient au sous-arbre de racine r (qui peut être vide)*/
debut
si r=VIDE Alors
// l'element n'est pas dans l'arbre
retourner FAUX;
sinon
si elements_egaux(val(r,B),e) alors // on l'a trouve
retourner VRAI;
sinon si elements_inferieurs(e,val(r,B)) alors
// s'il existe, c'est dans le sous-arbre gauche
retourner RechercheAux(e,fils_gauche(r,B),B) ;
sinon
// s'il existe, c'est dans le sous-arbre droit
retourner RechercheAux(e,fils_droit(r,B),B);
Finsi
Finsi
Finsi
fin
DUT MCW1
93
Ajout
Algorithme Ajout (element e,E/S arbre B)
// ajoute la element e dans l'arbre
// l'arbre peut etre vide !
// l'arbre est modifie
locales : sommets r,y
début
si est_vide(B) Alors // l'arbre est vide !!!
y  creer_sommet(e,B);
fait_racine(y,B);
sinon
r=racine(B);
si elements_inferieurs(e,val(r,B)) alors
Ajout_aux(e,fils_gauche(r,B),r,gauche,B);
sinon
Ajout_aux(e,fils_droit(r,B),r,droit,B);
finsi
finsi
fin
DUT MCW1
94
Ajout
Algorithme Ajout_aux (element e,sommet r, sommet pere, entier lien,E/S arbre B)
// ajout dans le sous arbre de racine <r> qui est fils de <pere>
// <lien> egal droit ou gauche suivant que <r> est fils gauche ou droit de
son pere
locale : sommet nouv;
debut
si r=VIDE alors
// c'est ici qu'il faut l'ajouter
nouv  creer_sommet(e,B);
si lien=droit alors
faire_FD(pere,nouv,B);
sinon
faire_FG(pere,nouv,B);
finsi
sinon
si elements_inferieurs(e,val(r,B)) alors
Ajout_aux(e,fils_gauche(r,B),r,gauche,B);
sinon
Ajout_aux(e,fils_droit(r,B),r,droit,B);
finsi
finsi
fin
DUT MCW1
95
Ajout

On ajoute le sommet en tant que feuille. On
pourrait aussi ajouter en tant que racine :

couper l'arbre en deux sous-arbres (binaires de
recherche) G et D,

G contenant tous les éléments de valeur plus
petite que celle à ajouter,

et D contenant tous les éléments de valeur plus
grande que celle à ajouter,

créer un nouveau sommet de valeur voulue en
lui mettant G comme sous-arbre gauche et D
comme sous-arbre droit.
DUT MCW1
96
Ajout

Exemple : ajout à la racine du sommet 12
dans un abr
DUT MCW1
97
Suppression

La suppression commence par la recherche
de l'élément. Puis :

si c'est une feuille, on la vire sans problèmes

si c'est un sommet qui n'a qu'un fils, on le
remplace par ce fils

si c'est un sommet qui a deux fils, on a deux
solutions :
- le remplacer par le sommet de plus grande
valeur dans le sous-arbre gauche
- le remplacer par le sommet de plus petite
valeur dans le sous-arbre droit

puis supprimer (récursivement) ce sommet !
DUT MCW1
98
Arbres binaires de recherche équilibrés

La complexité de tous ces algorithmes est
majorée par la hauteur de l'arbre. On a
donc intérêt à avoir des arbres les moins
hauts possible. Ainsi, pour améliorer ces
algos, on pourrait donc faire un
rééquilibrage de temps à autres, c'est à
dire : récupérer tous les éléments par un
parcours symétrique, puis reconstruire un
arbre quasi-parfait à partir de ces éléments
(tout en gardant son critère d'arbre binaire
de recherche).
DUT MCW1
99
Arbres binaires de recherche équilibrés
DUT MCW1
100
Arbres binaires de recherche équilibrés

Mais il y a encore mieux : on peut chercher
à avoir tout le temps un arbre de moindre
hauteur, c'est à dire un arbre tel que les
hauteurs des sous-arbres de chaque
sommet diffèrent d'au plus 1 et donc
rééquilibrer comme il faut après chaque
ajout ou suppression. On pourrait appeler
un tel arbre arbre binaire de recherche
quasi-quasi-parfait, mais il a déjà un nom
: arbre AVL, du nom de leurs inventeurs
Adelson, Velskii et Landis.
DUT MCW1
101
Arbres binaires de recherche équilibrés :Définitions
Définition 1. Un arbre binaire est dit Héquilibré si en tout sommet de l'arbre les
hauteurs des sous-arbres gauche et droit
diffèrent au plus de 1.
 Définition 2. Un arbre AVL est un arbre
binaire de recherche H-équilibré
 Soit la fonction de déséquilibre d'un sommet
deseq(sommet) =
hauteur(sous-arbre gauche)hauteur(sous-arbre droit)
 Un arbre est H-équilibré si et seulement si
les déséquilibres sont égaux à 0, 1 ou 
DUT MCW1
102
Arbres binaires de recherche équilibrés :Définitions

Exemple : l'arbre suivant n'est pas Héquilibré car il y a un déséquilibre de -2.
DUT MCW1
103
Rotations


Après chaque ajout ou suppression, il faut
regarder si cela a entraîné un déséquilibre
et si c'est le cas on rééquilibre par des
rotations.
Rotation droite
DUT MCW1
104
Rotations
Algorithme rd (sommet b, E/S arbre B) // rotation droite du sous arbre de racine b
locales : sommets a,v,pb; coté = droit/gauche;
debut
// garder infos pour raccrocher correctement le sous arbre dans B
pb  pere(b,B);
si pb<>VIDE alors
si b=fils_gauche(pb,B) alors
coté  gauche;
sinon coté  droit
finsi;
finsi
// === la rotation du sous
arbre
a  fils_gauche(b,B);
v  fils_droit(a,B);
fait_FG(b,v,B);
fait_FD(a,b,B);
DUT MCW1
// maintenant on raccroche dans
l'arbre
si pb<>VIDE alors
si coté=gauche alors
fait_FG(pb,a,B)
sinon fait_FD(pb,a,B);
finsi
sinon
fait_racine(a,B);
finsi
fin
105
Rotation gauche
DUT MCW1
106
Rotation gauche droite
DUT MCW1
107
Rotation droite gauche
DUT MCW1
108
Arbres A.V.L.




Après un ajout dans un AVL, soit il n'y a rien à faire (l'arbre est
encore H-équilibré), soit on fait des rotations qui rééquilibrent
l'arbre, et ce qui est très fort, c'est qu'une seule rotation suffit !!!
Théorème
Si B est un AVL et si on ajoute un sommet à B, alors
si cela ne provoque aucun déséquilibre, on a toujours un arbre Héquilibré on est content
sinon soit x le sommet le plus bas déséquilibré (ie de déséquilibre 2
ou -2) :

si x a un déséquilibre de 2 et est tel que son fils gauche

a un déséquilibre de 1, alors une rotation rd(x,B) permet de
rééquilibrer l'arbre

a un déséquilibre de -1, alors une rotation rgd(x,B) permet de
rééquilibrer l'arbre

si x a un déséquilibre de -2 et est tel que son fils droit

a un déséquilibre de -1, alors une rotation rg(x,B) permet de
rééquilibrer l'arbre

a un déséquilibre de 1, alors une rotation rdg(x,B) permet de
rééquilibrer l'arbre

pas d'autres cas possible andouille
DUT MCW1
109
Téléchargement