Lorsque nous manipulons des données séquentielles, nous utilisons généralement des listes ou des tableaux pour stocker et accéder à ces informations. Cependant, dans certains cas, il peut être plus rapide d’employer une liste chaînée. Cela est particulièrement vrai lorsque le programme doit insérer ou supprimer un élément au début de la liste à de multiples reprises, opérations qui peuvent s’avérer plus efficaces avec une liste chaînée.
Une liste chaînée est l’une des structures de données les plus fondamentales. Elle contient une liste d’éléments, peut mettre à jour cette liste en y ajoutant de nouveaux éléments et peut accéder à ces éléments, de la même façon qu’un tableau ou une liste ordinaire. Lorsqu’on travaille avec des listes chaînées, on appelle chaque élément un Node, qui contient les données et un lien vers le Node suivant. Dans une liste simplement chaînée, chaque élément (le Node) a une référence vers le nœud suivant, et le dernier nœud fait référence à rien – null. Dans la littérature, on appelle le premier élément d’une liste simplement chaînée le head, tandis que le dernier élément s’appelle le tail. Une des implémentations possibles d’une liste chaînée peut prendre la forme suivante :
class Node:
def __init__(self, data):
self.data = data # Conserver la donnée
self.after = None # Stocker un lien vers l'élément suivant
class LinkedList:
def __init__(self):
self.head = None # Au départ, il n'y a aucun élément
def append(self, data): # Ajouter un nouvel élément à la liste
new_node = Node(data) # Créer un nœud qui contient data
if self.head is None: # S'il n'y a aucun élément dans la liste
self.head = new_node # Définir le head comme le nouveau nœud
return
current = self.head # Commencer à parcourir la liste depuis le head
while current.after: # Tant que nous n'avons pas atteint le tail
current = current.after # Passer au nœud suivant
current.after = new_node # Mettre à jour le tail et le remplacer par le nouveau nœud
def print(self): # Afficher tous les éléments de la liste
current = self.head # Partir du head
while current: # Tant que nous ne sommes pas arrivés à la fin
print(current.data, end='\n' if current.after is None else ' --> ')
current = current.after # Passer au nœud suivant
names = LinkedList() # Créer une liste chaînée vide
names.append('Anna') # Ajouter Anna à la liste
names.append('Bob') # Ajouter Bob à la liste
names.append('Sona') # Ajouter Sona à la liste
names.print() # Anna --> Bob --> Sona
Dans cette implémentation, nous avons deux classes : Node et LinkedList. La classe Node possède deux attributs : data et after. L’attribut data contient la valeur du nœud, tandis que l’attribut after conserve la référence vers le nœud suivant dans la liste. La classe LinkedList a un attribut unique : head, qui référence le premier nœud de la liste. La méthode append() sert à ajouter un nouveau nœud à la fin de la liste, et la méthode print() permet d’afficher les éléments de la liste.
Liste doublement chaînée
En outre, il est possible d’avoir des listes doublement chaînées, dans lesquelles chaque élément possède une référence vers l’élément suivant et l’élément précédent. Cela permet de parcourir la liste dans les deux sens.
Analyse de la liste chaînée
Quelques points importants à garder en tête lorsque vous travaillez avec des listes chaînées :
Accéder à un élément dans une liste chaînée prend un temps de , car il faut parcourir la liste depuis le head pour trouver l’élément.
Si l’on connaît le Node après lequel insérer un nouvel élément, l’insertion s’effectue en , puisqu’il suffit de mettre à jour les références des nœuds voisins.
Si l’on connaît le Node à supprimer, la suppression s’effectue en , car il suffit de mettre à jour les références des nœuds voisins.
Les listes chaînées consomment plus de mémoire que les tableaux en raison de la référence supplémentaire stockée dans chaque élément.
Défi
Étant donné n requêtes, vous devez les exécuter. Il existe 2 types de requêtes :
Insérer un nombre au début de la liste.
Afficher la liste entière.
Entrée
La première ligne de l’entrée contient un entier n (1 ≤ n ≤ 1000) indiquant le nombre de requêtes.
Les n lignes suivantes contiennent les requêtes – print si vous devez afficher la liste entière, et insert x si vous devez insérer le nombre x au début de la liste ( ≤ x ≤ ).
Sortie
Le programme doit exécuter correctement toutes les requêtes et afficher la liste en séparant les éléments par des espaces.