Liste chaînée

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
notion image
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 :
  1. Insérer un nombre au début de la liste.
  1. 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.

Exemples

Entrée
Sortie
6 insert 8 print insert 10 insert -2 insert -6 print
8 -6 -2 10 8
 

Constraints

Time limit: 2 seconds

Memory limit: 512 MB

Output limit: 1 MB

To check your solution you need to sign in
Sign in to continue