Académique Documents
Professionnel Documents
Culture Documents
Chapitre3 Partie 02
Chapitre3 Partie 02
Données
– Cours –
Chapitre 03 : Structures de données linéaires : Liste, Pile et
File
Partie02: Type Abstrait d'une Liste et ses implémentations possibles
Staff pédagogique
Nom Grade Faculté/Institut Adresse e-mail
BELALA Faiza Professeur Nouvelles Technologies Faiza.belala@univ-constantine2.dz
HAMMOUD Djamila MCB Nouvelles Technologies Djamila.hammoud@univ-
constantine2.dz
Etudiants concernés
Faculté/Institut Département Année Spécialité
Nouvelles Technologies MI Licence 2
Objectifs du cours
Objectif: Cette partie du cours permet d'une part, d'introduire les Listes à partir de leur TAD,
d'étudier de manière générique et réutisable les opérations qui les manipulent, et d'autre part de
considérer l'implémentation de cette structure en utilisant les tableaux et les pointeurs.
Une liste linéaire est une suite d’un nombre variable d’éléments de même type. L’ordre des
éléments dans une liste est fondamental. Ce n’est pas un ordre sur les éléments, mais un ordre sur
les places des éléments. Les places sont totalement ordonnées. Chaque place a un contenu qui est un
élément. Le nombre n d’éléments (donc de places) est appelé la longueur de liste. Si n = 0, la liste
est vide. On peut donc avoir une liste de valeurs entières, une liste d’étudiants, une liste de produits,
etc.
Opérations sur les listes : Plusieurs opérations peuvent être définies pour manipuler la structure
de donnée liste, on peut citer sans être exhaustif :
Création d’une liste vide
Insertion d’un élément (qui a une place donnée),
Suppression d’un élément (qui a une place donnée),
Accéder à un élément quelconque de la liste
Calcul de la longueur d’une liste,
Vérifier si une liste est vide,
Eclatement d’une liste en deux listes,
Concaténation de deux listes,
Tri d’une liste,
etc.
...
Remarques:
1. Nous remarquons que le nombre d’opérations constructeurs dans un TAD, comme c’est le
cas des opérations liste-vide et insérer dans le cas d’une liste, est bien défini (il ne changera
pas d’un utilisateur à un autre).
2. Nous pouvons toujours enrichir un TAD par d’autres opérations non constructeurs, le but est
d’avoir un TAD le plus complet possible. Ainsi à la définition d’unTAD, pensez à recencer
toutes les opérations de base.
3. Nous simplifions dans la suite en considérant le TAD importé: POSITION défini par celui
ENTIER pour donner les axiomes correspondants au TAD LISTE (POSITION = ENTIER).
Exemple d'axiomes:
Préconditions:
accés(l,i) est définie ssi 1≤ i ≤ longueur(l)
supprimer(l,i) est définie ssi 1≤i≤ longueur(l)
insérer(l,i,e) est définie ssi 1≤i≤ longueur(l)+1
Axiomes:
taille(liste_vide) ≡ o
taille(insérer(l,i,e)) ≡ longueur(l) + 1
Est-vide(liste_vide) ≡ vrai
Est-vide(insérer(l,i,e)) ≡ faux
ou bien sans utiliser les opérations constructeurs pour représenter les
arguments de l'opération longueur et Est-vide
Il peut exister plusieurs manières de représentation du type liste défini ci dessus, au niveau de la
machine. Nous présentons dans ce cours seulement, deux implémentations possibles l’une
(contigue) à base des tableaux et l’autre (chaînée) à base des pointeurs.
1.2.1. Représentation contiguë
La liste est représentée par un tableau dont la ième case est la ième place de la liste. Elle est donc
désignée par le couple: (tableau, longueur-liste). L’implémentation se fait par un enregistrement ou
deux variables séparées avec les opérations précédentes manipulant un tableau classique.
Liste= <e1, e2, ..., en> est représentée par:
longueur-liste
L’implémentation du TAD associé à la structure de données liste, dans n’importe quel langage,
nécessite la déclaration des types utilisés pour déclarer les valeurs ayant la structure liste (tableau,
enregistrement et entier dans notre cas) et les fonctions implémentant les opérations manipulant la
structure de données en question (liste-vide, insérer, ..., etc.).
Naturellement, pour écrire une fonction, son entête est déduite du profil de l’opération
correspondante (nombre et type des arguments, type du résultat), son corps est défini, rappelons le,
comme suit:
Si l’opération est de type interne constructeur dans le TAD, alors l’écriture du corps de la
fonction l’implémentant dépend des types utilisés, telles que les fonctions de base: liste-vide
et insérer (voir tableau ci dessous).
Si l’opération est observateur ou interne non constructeur ayant des propriétés sous forme
d’axiomes dans son TAD, le corps de la fonction l’implémentant est écrit généralement en
utilisant les fonctions de base déclarées auparavant, comme c’est le cas de la fonction
récursive longueur ou est-vide déclarées dans ce qui suit.
Certaines opérations internes non constructeurs (comme supprimer et accés) malgrè qu'elles
possèdent des propriétés sous formes d'axiomes dans le TAD, leur implémentation dépend
toujours des types utilisés. Les fonctions correspondantes font partie de l'ensemble des
fonctions de base manipulant la structure de donnée en question.
insérer: Liste, position, élément → liste Fonction insérer (D L: liste, D p: Entier, D e:élement): liste
Déclaration
j: Entier
Début
Si L.longueur < Lmax
Alors
Si L.longueur ≠0 et p≠ L.longueur +1 /* On peut omettre ce test et le
mettre dans le programme principal (précondition dans le TAD
correspondant
Alors Pour j = L.longueur à p , pas = -1 faire
L.Tab[j+1] = L.Tab[j]
Finpour
Finsi
L.Tab[p] = e
L.longueur = L.longueur +1
Sinon écrire ( “l’insertion est impossible, la liste est saturée”)
Finsi
retourner(L)
Fin
supprimer: liste, position → liste Fonction supprimer( D L: liste, Donnée p: Entier): liste
Déclaration
j: Entier
Début
Si L.longueur < >1 /*suppresion avec un seul élément dans L
Alors Pour j = p à L.longueur -1 faire
L.Tab[j ] = L.Tab[j+1]
Finpour
finsi
L.longueur = L.longueur - 1
finsi
retourner(L)
Fin
accés: liste, position→ élément Fonction accés ( Donnée L: liste, Donnée p:entier): élément
Début
Retourner L.Tab[p]
fin
Fonction récursive
Fonction Taille ( Donnée L: liste): entier
Début
Si L==liste-vide ( ) alors retourner 0
Sinon retourner 1+ taille (supprimer(L,1))
Fsi
fin
Remarques:
1. Nous avons implémenté toutes les opérations du TAD par des fonctions rien n'empêche de
considérer des procédures. Les fonctions favorisent leur réutilisation dans d’autres
fonctions (en effet une fonction peut être paramètre d’une autre fonction)
2. Notons que la première fonction itérative taille dépend de la manière dont est implémentée
la structure de donnée liste. Ce n’est pas le cas de l'autre fonction taille proposée, elle est
indépendante du type liste choisi dans cette représentation, elle utilise les fonctions de base
liste-vide et supprimer et reste valable quelque soit l’implémentation de la liste considérée.
Cette deuxième solution, favorise la réutilisation des fonctions (avantage d’un TAD).
3. Dans le module (ou fonction) principal(e) qui va appeler (utiliser) les fonctions insérer,
supprimer et accés possédant des préconditions, les tests traduisant ces préconditions
doivent être ajoutés avant d'appeler ces fonctions.
4. Les déclarations ci dessus des types de valeurs et fonctions considérées peuvent être
traduites dans n’importe quel langage de programmation en particulier JAVA, dans ce cas
les objets liste doivent avoir la structure définie et les fonctions sont alors remplacées par
des méthodes JAVA manipulant ce type d’objets.
supprimer: liste, position → liste Fonction supprimer( D L: liste, Donnée p: position): liste
Déclaration
Pt1, Pt2: liste
Début
Si p=L alors retourner (L.suiv)
Sinon
Pt1L
Pt2L.suiv
tanque Pt2 p faire
Pt1Pt1.suiv
Pt2Pt2.suiv
Fintq
Pt1.suivPt2.suiv
retourner(L)
Fsi
Fin
accés: liste, position→ élément Fonction accés ( D L: liste, donnée p:position): élément
Début
Retourner(P.info)
fin
Fonction récursive
inchangée: il faut juste adapter supprimer(L,1) par supprimer(L,L) ou bien
suivant(L)
Fonction taille ( Donnée L: liste): entier
Début
Si L==liste-vide ( ) alors retourner 0
Sinon retourner 1+ taille (supprimer(L, L))
Fsi
Fin
Est-vide: liste → bool Fonction est-vide (D L: liste): bool
Début
retourner (L==liste-vide( ))
Fin
Remarques:
1. Nous remarquons que la fonction récursive taille et est-vide restent inchangées, elles ne
dépendent d’aucune implémentation (réutilisation).
2. Nous remarquons que l'opération Suivant a été identifiée avec sa précondition au niveau du
TAD LISTE pour rappeler sa fonctionnalité: l'ordre établi entre les positions des éléments
d'une liste, surtout dans ce cas où la position est implémentée par une adresse (pointeur).
Dans le cas où position est implémentée par les entiers, cet ordre est implicite: 1, 2, 3, ..., on
n'a pas besoin de cette opération.
3. Le chaînage des cellules d’une liste peut être simple et on parle de liste simplement chaînée.
Il peut être double, c’est-à-dire, chaque cellule de la liste pointe sur son successeur et sur son
prédécesseur, et on parle de liste doublement chaînée.
4. En langage JAVA, on peut donc déclarer ce type de liste et les opérations qui le gèrent sous
forme de classe JAVA (spécifiant les objets liste ayant cette structure) et ses méthodes
respectives.
5. L'implémentation contigüe d'une liste (par tableau) est peu efficace. En effet, l’espace
mémoire est mal géré, aussi la suppression et l’insertion sont coûteuses en temps (opération
de décalage). Une liste implémentée de manière chaînée (par pointeur) permet une insertion