Menu
Coddy logo textTech

Liste doublement chaînée

Dernière mise à jour

Une liste doublement chaînée est une liste chaînée où chaque nœud contient deux pointeurs : next (vers le nœud suivant) et prev (vers le précédent). Ce lien arrière supplémentaire permet de parcourir dans les deux sens et de supprimer un nœud en O(1) lorsque vous en avez déjà une référence, car vous pouvez atteindre directement son prédécesseur au lieu de parcourir depuis la tête. Appuyez sur lecture ci-dessus pour voir les nœuds se lier et se relier dans les deux sens.

Le coût est un pointeur supplémentaire par nœud et davantage de gestion : chaque insertion et suppression doit mettre à jour à la fois next et prev chez les voisins. C'est la structure derrière de nombreuses files doubles (deques) et caches LRU, où la suppression rapide aux deux extrémités compte.

Complexité temporelle

OpérationComplexitéNotes
Insérer en tête/queueO(1)Avec des pointeurs de tête et de queue
Supprimer un nœud connuO(1)Atteint prev directement, sans parcours
RechercheO(n)Toujours un balayage linéaire
Accès par indiceO(n)Pas d'accès aléatoire

Liste simplement vs doublement chaînée

AspectSimpleDouble
Pointeurs par nœud1 (next)2 (next + prev)
Parcourir en arrièreNonOui
Supprimer un nœud connuO(n) (trouver prev)O(1)
Surcoût mémoirePlus faiblePlus élevé

Exemple résolu

Construction de [10, 20, 30] par ajout en fin, puis suppression du nœud 20 :

ÉtapeStructureAction
DébutnullListe vide : head et tail valent tous deux null
Ajouter 1010Premier nœud ; head et tail pointent dessus, prev = next = null
Ajouter 2010 <-> 20On pose 10.next = 20 et 20.prev = 10 ; tail = 20
Ajouter 3010 <-> 20 <-> 30On pose 20.next = 30 et 30.prev = 20 ; tail = 30
Supprimer 2010 <-> 30En utilisant 20.prev et 20.next : on pose 10.next = 30 et 30.prev = 10 en O(1)

Quand utiliser une liste doublement chaînée

À utiliser quandÀ éviter quand
Vous devez parcourir ou raccorder dans les deux sensVous n'avancez que vers l'avant : une liste simple est plus légère
Vous supprimez des nœuds arbitraires dont vous avez déjà une référence (O(1))Vous indexez surtout par position : utilisez un tableau pour un accès O(1)
Vous implémentez un deque, un cache LRU ou un historique d'annulationLa mémoire est limitée : le pointeur prev supplémentaire double le surcoût des liens
Les insertions et suppressions se font souvent aux deux extrémitésLes données sont petites et changent peu : copier des tableaux coûte moins cher

Code de Doubly Linked List

Une implémentation propre et exécutable de Doubly Linked List en Python, JavaScript, Java, C++, C. Choisissez un langage, copiez le code ou ouvrez-le préchargé dans le Playground Coddy.

Code de Doubly Linked List en Python

Python
1class Node:2    def __init__(self, value):3        self.value = value4        self.prev = None5        self.next = None6
7
8class DoublyLinkedList:9    def __init__(self):10        self.head = None11        self.tail = None12
13    def append(self, value):14        node = Node(value)15        if self.tail is None:16            self.head = self.tail = node17            return18        # Wire both directions: old tail <-> new node19        node.prev = self.tail20        self.tail.next = node21        self.tail = node22
23    def prepend(self, value):24        node = Node(value)25        if self.head is None:26            self.head = self.tail = node27            return28        node.next = self.head29        self.head.prev = node30        self.head = node31
32    def forward(self):33        values, current = [], self.head34        while current:35            values.append(current.value)36            current = current.next37        return values38
39    def backward(self):40        values, current = [], self.tail41        while current:42            values.append(current.value)43            current = current.prev44        return values45
46
47lst = DoublyLinkedList()48for value in [2, 3, 4]:49    lst.append(value)50lst.prepend(1)51
52print("Forward: ", lst.forward())53print("Backward:", lst.backward())
Exécutez ce code dans le Playground Python

FAQ sur les listes doublement chaînées

Quelle est la différence entre une liste simplement et doublement chaînée ?
Une liste simplement chaînée a un pointeur par nœud (next), vous ne pouvez donc avancer que vers l'avant. Une liste doublement chaînée ajoute un pointeur prev, ce qui permet de parcourir en arrière et de supprimer en O(1) un nœud dont vous avez une référence, au prix d'un pointeur supplémentaire par nœud et de la mise à jour de deux liens à chaque changement.
Quand une liste doublement chaînée vaut-elle la mémoire supplémentaire ?
Lorsque vous avez besoin d'un parcours en arrière ou d'une suppression en O(1) de nœuds arbitraires que vous référencez déjà : les cas classiques sont les files doubles (deques) et les caches LRU, où les entrées sont déplacées et évincées fréquemment aux deux extrémités.
Pourquoi une liste doublement chaînée peut-elle supprimer un nœud en O(1) ?
Pour retirer un nœud, il faut relier son prédécesseur à son successeur. Dans une liste simplement chaînée, il faudrait parcourir depuis la tête pour trouver le prédécesseur (O(n)) ; dans une liste doublement chaînée, le pointeur prev du nœud donne directement le prédécesseur, donc la reconnexion est en O(1).
Liste doublement chaînée ou tableau : lequel choisir ?
Utilisez un tableau lorsque vous avez besoin d'un accès O(1) par indice et d'une itération efficace en cache, et lorsque les insertions se font surtout à la fin. Utilisez une liste doublement chaînée lorsque vous insérez ou supprimez souvent au milieu ou aux deux extrémités avec une référence de nœud, car c'est O(1) contre le décalage O(n) d'un tableau. Les tableaux gagnent en mémoire et en localité ; la liste gagne sur les modifications structurelles.
Qu'est-ce qu'un nœud sentinelle dans une liste doublement chaînée ?
Un nœud sentinelle (ou factice) est un marqueur placé avant la tête et/ou après la queue afin que la liste ne soit jamais réellement vide. Il supprime les vérifications de null pour head, tail et les insertions/suppressions aux bornes, permettant à chaque opération de suivre le même chemin de reconnexion. De nombreuses implémentations de caches LRU en production utilisent deux sentinelles pour éviter de traiter les extrémités comme des cas particuliers.
Quel est le bug le plus courant lors d'une suppression dans une liste doublement chaînée ?
Oublier de mettre à jour head ou tail lors de la suppression du premier ou du dernier nœud, ce qui laisse un pointeur pendant vers un nœud libéré. Vérifiez toujours si node.prev vaut null (mettez à jour head) ou si node.next vaut null (mettez à jour tail) avant de reconnecter les voisins.
Coddy programming languages illustration

Maîtrisez les algorithmes avec Coddy

COMMENCER