Fictif en tête
Énoncé
Dans cet exercice, nous allons étudier une technique très classique pour simplifier les codes manipulant des listes chaînées : la technique de l'élément fictif en tête.
Un élément fictif est tout simplement une Cellule
dont le champ val
n'est pas significatif et ne fait pas formellement partie de la liste chaînée.
Comme il n'est pas possible de mettre "rien" comme valeur d'une cellule, on pourra par exemple utiliser le caractère ?
, ce qui permettra de détecter visuellement d'éventuelles erreurs (si une trace affiche le caractère ?
à l'écran).
En effet, on ne devra jamais accéder au champ val
d'une cellule fictive, car ce champ est considéré comme « non initialisé ».
On créera donc un élément fictif en tête en allouant une cellule qu'on chaînera simplement en tête de la liste : fictif = Cellule('?', liste)
.
Cette technique est utile quand on doit parcourir une liste chaînée avec un coup de retard : par exemple quand on veut supprimer la cellule contenant la première occurrence d'une valeur et qu'on doit s'arrêter sur la cellule précédant celle contenant cette valeur, pour pouvoir modifier le chaînage.
Dans une telle fonction, le cas de la liste chaînée vide et celui de la liste contenant la valeur cherchée dans sa première cellule sont des cas particuliers qu'on doit traiter séparément du cas général.
L'utilisation de l'élément fictif en tête permet souvent d'homogénéiser le code en ramenant les cas particuliers au cas général.
Pour vous en convaincre, ajoutez la fonction supprime
suivante à votre programme manipulant les listes basiques de l'exercice précédent :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | def supprime(lsc, val):
"""
Supprime la premiere occurrence de val dans lsc (version naive sans fictif).
La fonction renvoie la liste chaînée (éventuellement) modifiée et un booléen supp qui vaut True
ssi il y a bien eu une suppression (c'est à dire si la liste chaînée initiale contenait au
moins une occurrence de val).
"""
supp = False
if est_vide(lsc):
pass
elif lsc.val == val:
lsc = lsc.suiv
supp = True
else:
prec = lsc
while prec.suiv is not None and prec.suiv.val != val:
prec = prec.suiv
if prec.suiv is not None:
prec.suiv = prec.suiv.suiv
supp = True
return lsc, supp
|
ainsi que le bout de code de test ci-dessous à la fin de la fonction main
:
| # Ajouter ce bout de code à la fin de la fonction main de l'exercice précédent
print("\nListe initiale : ", end="")
affiche(lsc)
while not est_vide(lsc):
val = randint(0, 5)
print(f"Suppression de {val} : ", end="")
lsc, supp = supprime(lsc, val)
if supp:
affiche(lsc)
else:
print("valeur absente")
|
Testez cette fonction (volontairement non commentée !) et essayez de comprendre son fonctionnement, en faisant particulièrement attention aux deux cas particuliers au début de la fonction.
Créez une fonction supprimer_fictif
qui utilise la technique de l'élément fictif en tête pour écrire un code équivalent à celui de supprimer
, mais sans avoir à dissocier les cas particuliers.
Autrement dit, la fonction supprimer_fictif
ajoutera un élément fictif en tête de liste avant de faire la suppression.
Il suffira de changer l'appel à supprimer
en appel à supprimer_fictif
dans le code de test pour vérifier votre fonction.
Correction
Cliquez ici pour révéler la correction.
fictif_en_tete.py
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 | #!/usr/bin/env python3
"""
Programme pour tester des listes simplement chaînées avec technique du fictif.
"""
from random import randint
class Cellule:
"""
Une cellule est composée d'une valeur et d'une référence vers la
cellule suivante (ou None s'il n'y a pas de suivant)
"""
# pylint: disable=too-few-public-methods
def __init__(self, val, suiv):
"""
Constructeur
"""
self.val = val
self.suiv = suiv
def __str__(self):
"""
Afficheur
"""
return f"{self.val} -> "
def est_vide(lsc):
"""
Teste si la liste chaînée est vide
"""
return lsc is None
def insere_tete(lsc, val):
"""
Insère une cellule de valeur val en tête de la liste chaînée.
Renvoie la nouvelle cellule insérée en tête.
"""
return Cellule(val, lsc)
def insere_queue(lsc, val):
"""
Insère une cellule de valeur val en queue de la liste chaînée
"""
cell = Cellule(val, None)
if est_vide(lsc):
return cell
cour = lsc
while cour.suiv is not None:
cour = cour.suiv
cour.suiv = cell
return lsc
def affiche(lsc):
"""
Affiche la liste chaînée
"""
cour = lsc
while cour is not None:
print(cour, end="")
cour = cour.suiv
print("FIN")
def supprime(lsc, val):
"""
Supprime la premiere occurrence de val dans lsc (version naive sans fictif).
La fonction renvoie la liste chaînée (éventuellement) modifiée et un booléen supp qui vaut True
ssi il y a bien eu une suppression (c'est à dire si la liste chaînée initiale contenait au
moins une occurrence de val).
"""
supp = False
if est_vide(lsc):
pass
elif lsc.val == val:
lsc = lsc.suiv
supp = True
else:
prec = lsc
while prec.suiv is not None and prec.suiv.val != val:
prec = prec.suiv
if prec.suiv is not None:
prec.suiv = prec.suiv.suiv
supp = True
return lsc, supp
def supprime_fictif(lsc, val):
"""
Supprime la premiere occurrence de val (version avec fictif)
"""
supp = False
fictif = Cellule("?", lsc)
prec = fictif
while prec.suiv is not None and prec.suiv.val != val:
prec = prec.suiv
if prec.suiv is not None:
prec.suiv = prec.suiv.suiv
lsc = fictif.suiv
supp = True
return lsc, supp
def main():
"""
Fonction principale
"""
lsc = None # creation d'une liste chaînée vide
print("Liste initiale vide : ", end="")
affiche(lsc)
for _ in range(10):
ins_en_tete = randint(0, 1) == 1
val = randint(0, 5)
if ins_en_tete:
print(f"Insertion en tête de {val} : ", end="")
lsc = insere_tete(lsc, val)
else:
print(f"Insertion en queue de {val} : ", end="")
lsc = insere_queue(lsc, val)
affiche(lsc)
# Ajouter ce bout de code à la fin de la fonction main de l'exercice précédent
print("\nListe initiale : ", end="")
affiche(lsc)
while not est_vide(lsc):
val = randint(0, 5)
print(f"Suppression de {val} : ", end="")
lsc, supp = supprime(lsc, val)
if supp:
affiche(lsc)
else:
print("valeur absente")
main()
|