Blog français

Linked lists en Python : 5 patterns d'entretien

19 mai 202621 min de lecture
Linked lists en Python : 5 patterns d'entretien

Maîtrisez les linked lists en Python avec les 5 patterns clés, les pointeurs et les pièges à éviter pour réussir vos entretiens. À lire maintenant.

Most candidates who struggle with linked lists in an interview aren't struggling because they don't know the concept. Ils savent qu’une linked list est une chaîne de nœuds où chaque nœud contient une valeur et un pointeur vers le suivant. Le problème, c’est qu’un entretien Python sur les linked lists vous demande de faire quelque chose avec ce concept sous la pression du temps — écrire une classe `ListNode` de zéro, inverser la liste sans perdre aucune référence, détecter un cycle avant que la patience de l’intervieweur ne s’épuise. C’est une compétence différente de la simple connaissance de la définition, et la plupart des ressources de préparation traitent les deux comme s’il s’agissait de la même chose.

Ce guide est le chemin le plus court entre la compréhension de la structure d’un nœud et l’exécution des patterns que les intervieweurs réutilisent réellement. Il ne couvrira pas tous les problèmes possibles sur les linked lists. Il couvrira ceux qui comptent, le template qui tient sous pression, et les erreurs qui brisent discrètement des solutions pourtant correctes.

Ce qu’est réellement une linked list en Python, et pourquoi les intervieweurs continuent d’en parler

Arrêtez de penser en indices et commencez à penser en nœuds

Les tableaux vous donnent des positions. Vous demandez l’index 3 et vous obtenez la valeur à l’index 3 — en temps constant, sans navigation. Les linked lists vous donnent des références. Vous commencez à la tête et vous suivez les pointeurs jusqu’à atteindre ce dont vous avez besoin. Cette distinction n’a rien d’anecdotique. C’est précisément la raison pour laquelle les intervieweurs évoquent les linked lists.

Quand un intervieweur vous demande de comparer les deux structures, il teste si vous comprenez pourquoi ce compromis existe. Les tableaux sont stockés en mémoire contiguë, ce qui rend l’accès aléatoire rapide et le comportement du cache prévisible. Les linked lists répartissent les nœuds dans la mémoire, ce qui rend la traversée plus lente pour l’accès aléatoire, mais permet une insertion et une suppression à des positions arbitraires en O(1) une fois que vous avez le bon pointeur — puisque vous ne faites que réorganiser des références, et non décaler des éléments. Selon les notes sur les structures de données du MIT OpenCourseWare, ces différences de complexité temporelle découlent directement du modèle mémoire sous-jacent, et non de choix d’implémentation au niveau du langage.

La question d’entretien « quand utiliseriez-vous une linked list plutôt qu’un tableau ? » a une vraie réponse : lorsque vous faites des insertions et suppressions fréquentes au milieu d’une séquence et que vous n’avez pas besoin d’un accès aléatoire. La suite honnête est que, dans la plupart des codebases Python en production, `collections.deque` gère cela mieux qu’une linked list écrite à la main — mais l’entretien teste votre compréhension du compromis, pas votre choix de structure en production.

Ce que cela donne en pratique

Parcourir un tableau de trois éléments en Python tient en une ligne : `arr[2]`. Parcourir une linked list de trois éléments demande trois sauts de pointeurs — `head`, puis `head.next`, puis `head.next.next`. Cela semble plus lent parce que cela l’est, pour l’accès aléatoire. Mais imaginez maintenant que vous vouliez insérer un nœud entre les positions 1 et 2. Dans le tableau, vous décalez chaque élément après la position 1 d’une case vers la droite — O(n). Dans la linked list, vous modifiez deux pointeurs — O(1) une fois que vous êtes sur le bon nœud.

Le moment où cette distinction a fait sens pour moi, c’est lorsque j’ai cessé de demander « quel est cet index ? » pour commencer à demander « vers quoi ce nœud pointe-t-il ? ». La liste a cessé de ressembler à un tableau cassé et a commencé à ressembler à une chaîne dont la forme est la structure. Une fois que vous la voyez ainsi, la manipulation des pointeurs ne paraît plus arbitraire.

Écrivez un template ListNode vraiment utilisable sous pression en entretien

Le constructeur minimal qui fait le travail sans se compliquer la vie

La classe `ListNode` que vous écrivez en entretien doit être immédiatement lisible et prendre moins de trente secondes à taper. Voici la version qui fonctionne :

C’est tout. Les valeurs par défaut `val=0` et `next=None` vous permettent de créer un nœud avec une simple valeur (`ListNode(5)`) ou de les chaîner en ligne (`ListNode(1, ListNode(2, ListNode(3)))`). Ces deux formes reviennent constamment. Les valeurs par défaut rendent le constructeur flexible sans ajouter de charge mentale.

Certains candidats ajoutent un pointeur `prev` pour les variantes de doubly linked list. Ne l’ajoutez pas sauf si l’énoncé le demande explicitement — des attributs en plus non nécessaires signalent du bruit, pas de la rigueur.

slots est un bon détail, pas l’essentiel

Ajouter `__slots__ = ('val', 'next')` à la déclaration de la classe indique à Python d’utiliser une disposition mémoire de taille fixe plutôt qu’un `__dict__` par instance. Pour une `ListNode` instanciée des milliers de fois, cela réduit sensiblement la consommation mémoire. La documentation Python sur `__slots__` détaille précisément le mécanisme.

En entretien, mentionner `__slots__` est un signal raisonnable que vous comprenez le modèle objet de Python. L’intégrer à votre template sans qu’on vous le demande est une autre affaire — cela ajoute une ligne que l’intervieweur vous demandera probablement d’expliquer, et si vous ne pouvez pas le faire clairement, cela vous dessert davantage que de l’omettre. Connaissez-le. Utilisez-le si l’intervieweur parle d’optimisation mémoire. Ne commencez pas par là.

Ce que cela donne en pratique

Construire et parcourir une linked list de trois nœuds avec ce template :

Le schéma de parcours — `current = head`, avancer avec `current = current.next`, s’arrêter quand `current` vaut `None` — est la base de presque toutes les solutions sur les linked lists que vous écrirez. Mémorisez cette boucle avant de toucher à quoi que ce soit d’autre.

Apprenez à parcourir, insérer, supprimer et inverser avant d’attaquer les patterns plus avancés

La traversée est la partie ennuyeuse qui sauve toutes les autres solutions

Une préparation d’entretien sur les linked lists qui saute directement aux fast/slow pointers ou à la détection de cycle repose sur du sable. La traversée n’est pas un échauffement — c’est l’opération fondamentale dont dépendent tous les autres patterns. Si vous perdez `current` au milieu d’une traversée, vous perdez la liste. Il n’existe aucun index de secours.

La boucle de traversée ci-dessus est le pattern complet. La seule variation vraiment utile à connaître est la traversée à deux pointeurs où vous maintenez `prev` et `current` simultanément, ce dont vous aurez besoin pour la suppression et l’inversion. À l’aise avec les deux avant de passer à la suite.

L’erreur d’un seul pointeur qui ruine les suppressions

Le bug classique de suppression : un candidat écrit `current.next = current.next.next`, puis essaie de réutiliser `current.next` sans réaliser que cette référence a déjà disparu. La correction est toujours la même — sauvegarder ce dont vous avez besoin avant de réorganiser quoi que ce soit.

Ce n’est pas un bug subtil. Il apparaît dans presque toutes les sessions sur les linked lists où un candidat va trop vite et cesse de décrire l’état de ses pointeurs. La solution consiste à le verbaliser : « je sauvegarde `next` avant de réaffecter ».

Ce que cela donne en pratique

L’inversion itérative est l’opération unique la plus importante à internaliser. Parcourez-la pas à pas :

À chaque étape, `next_node` est sauvegardé avant que `current.next` ne soit écrasé. Cette seule ligne — la sauvegarde de la référence vers l’avant — fait la différence entre une inversion propre et une liste qui disparaît dans `None`. Dès que vous comprenez pourquoi cette sauvegarde est nécessaire, l’insertion et la suppression commencent à suivre la même logique.

Les cinq patterns de linked list que les intervieweurs testent encore et encore

Inversion, fusion, fast/slow, dummy node, cycle — tout le jeu en cinq mouvements

À première vue, les questions d’entretien sur les linked lists en Python semblent variées. Elles ne le sont pas. Presque chaque question que vous rencontrerez en entretien junior ou intermédiaire est une variante de cinq patterns fondamentaux : inversion, fusion de deux listes triées, fast/slow pointers, dummy node et détection de cycle. Une fois que vous voyez le pattern sous l’énoncé, vous choisissez un outil dans une petite boîte à outils plutôt que de repartir de zéro.

Ce n’est pas une exagération. Une revue des problèmes sur linked lists les plus fréquents sur les principales plateformes de code confirme que ces cinq catégories représentent l’immense majorité des questions posées. Les ensembles de problèmes curatés de LeetCode et la roadmap de NeetCode font remonter le même groupe de problèmes lorsque vous filtrez par fréquence sur les linked lists.

Ce que cela donne en pratique

Chaque pattern correspond directement à un problème représentatif :

  • Reverse → Reverse Linked List (LeetCode 206). L’approche itérative à trois pointeurs ci-dessus est le template.
  • Merge → Merge Two Sorted Lists (LeetCode 21). Deux pointeurs parcourent les deux listes, avec un dummy node au début.
  • Fast/slow pointers → Middle of the Linked List (LeetCode 876), plus la détection de cycle (LeetCode 141).
  • Dummy node → Remove Nth Node From End (LeetCode 19), Merge Two Sorted Lists.
  • Cycle detection → Linked List Cycle (LeetCode 141), Linked List Cycle II (LeetCode 142).

Quand un nouveau problème se présente, la première question n’est pas « comment le résoudre ? ». C’est « auquel de ces cinq patterns cela ressemble-t-il ? ». Ce simple changement de perspective réduit déjà le temps passé devant un éditeur vide.

Pourquoi la reconnaissance de pattern vaut mieux que la mémorisation des réponses

Dans la plupart des entreprises, les intervieweurs ne vérifient pas si vous avez mémorisé une solution précise. Ils observent si vous pouvez identifier la structure du problème assez vite pour prendre de bonnes décisions. Un candidat qui dit « cela ressemble à un problème de fast/slow pointer, parce que nous devons trouver une position par rapport à la fin sans connaître la longueur » paraît mieux préparé qu’un autre qui produit silencieusement le bon code sans expliquer son choix. La reconnaissance du pattern, c’est le signal.

Utilisez un dummy node quand les cas sur la tête vous feraient autrement perdre du temps

L’idée est d’arrêter de traiter la tête comme un cas spécial

Les dummy nodes existent pour une seule raison : la tête de la liste est un cas particulier dans un nombre surprenant de problèmes, et la gérer avec des branchements explicites (`if head is None`, `if we're deleting the head`, `if the first node is the merge target`) ajoute une complexité qui n’a rien à voir avec la logique principale.

Un dummy node est un `ListNode(0)` jetable dont le pointeur `next` commence sur `head`. Votre algorithme n’a alors plus jamais à traiter le premier vrai nœud différemment des autres, puisqu’il y a toujours un nœud avant lui.

Ce que cela donne en pratique

Fusionner deux listes triées sans dummy node nécessite une vérification séparée pour déterminer quelle liste fournit la tête initiale. Avec un dummy node :

Le `dummy.next` à la fin est la vraie tête de la liste fusionnée. Sans le dummy, vous auriez besoin d’une condition avant la boucle pour déterminer quel nœud démarre le résultat. Avec lui, la boucle traite tous les cas de la même manière. Dans une vraie résolution, cela enlève trois vérifications séparées de cas limites — le genre de vérifications qui ressemblent à de la sécurité mais signalent en réalité que la solution manque de netteté.

Les fast et slow pointers résolvent bien plus que les questions sur le nœud central

La même astuce trouve le milieu, détecte les cycles et prépare Remove Nth From End

Les fast et slow pointers fonctionnent parce que deux pointeurs avançant à des vitesses différentes dans la même liste créent une relation de distance prévisible. Faites avancer `fast` de deux pas pour chaque pas de `slow`, et lorsque `fast` atteint la fin, `slow` est au milieu. Faites-les avancer dans un cycle, et ils finiront par se rencontrer. Placez `fast` n pas devant `slow`, et quand `fast` atteint `None`, `slow` se trouve à n nœuds de la fin.

C’est une seule intuition structurelle, avec trois applications directes. Les mathématiques sous-jacentes sont les mêmes à chaque fois.

Ce que cela donne en pratique

Nœud du milieu :

Détection de cycle (algorithme de Floyd) :

Supprimer le n-ième depuis la fin : Commencez avec `fast` à `n+1` pas devant `slow` (tous deux partant d’un dummy node), puis avancez les deux d’un pas à la fois. Quand `fast` vaut `None`, `slow.next` est le nœud à supprimer.

La condition de boucle `while fast and fast.next` est la même dans les trois cas. C’est bien le but — une fois que vous l’avez écrite une fois, vous l’avez écrite à chaque fois.

Pourquoi les candidats compliquent trop ce pattern

L’erreur la plus fréquente consiste à reconstituer la solution de mémoire au lieu de suivre pas à pas l’écart entre les pointeurs. Les candidats qui ont mémorisé le code de détection de cycle sans intérioriser pourquoi les pointeurs se rencontrent bloqueront si l’intervieweur demande « que se passe-t-il si le cycle commence au troisième nœud ? ». Travaillez un exemple de cinq nœuds sur papier, en indiquant la position de chaque pointeur à chaque étape. Le pattern devient immédiatement évident et le reste sous pression.

Selon CLRS (Introduction to Algorithms), la preuve de correction de la détection de cycle de Floyd découle directement de l’arithmétique modulaire de deux pointeurs dans une boucle — comprendre cela rend l’algorithme mémorable plutôt qu’arbitraire.

Choisissez les bons problèmes d’entraînement au lieu d’enchaîner les problèmes au hasard

Commencez par des victoires faciles, puis passez aux problèmes qui combinent réellement des patterns

S’entraîner au hasard produit des résultats aléatoires. Une progression structurée produit une reconnaissance de pattern transférable. Pour un candidat junior qui se prépare à un entretien sur les linked lists, l’ordre compte plus que le volume.

Commencez par la traversée et la manipulation de base : inverser une liste de façon itérative, puis récursivement. Passez ensuite à des problèmes qui introduisent un second pattern : fusionner deux listes triées (merge + dummy node), trouver le milieu (fast/slow), détecter un cycle (fast/slow avec test d’égalité). Une fois que cela devient automatique, passez à des problèmes qui combinent deux patterns : supprimer le n-ième depuis la fin (dummy node + écart entre pointeurs), palindrome linked list (fast/slow pour trouver le milieu + inversion de la seconde moitié), reorder list (milieu + inversion + fusion).

Ce n’est qu’après cette couche de combinaison que vous devriez tenter reverse nodes in k-group ou LRU cache — ces problèmes supposent que vous avez complètement intériorisé les patterns plus simples.

Ce que cela donne en pratique

Une progression d’étude compacte pour un candidat junior, dans l’ordre :

  • Reverse Linked List (LeetCode 206) — itératif et récursif
  • Merge Two Sorted Lists (LeetCode 21) — base avec dummy node
  • Middle of the Linked List (LeetCode 876) — base fast/slow
  • Linked List Cycle (LeetCode 141) — détection de cycle
  • Remove Nth Node From End (LeetCode 19) — dummy node + écart entre pointeurs
  • Palindrome Linked List (LeetCode 234) — combinaison milieu + inversion
  • Reorder List (LeetCode 143) — milieu + inversion + fusion, le tout en une fois

Sept problèmes, dans cet ordre, avec une attention volontaire au pattern enseigné par chacun. C’est une semaine de préparation bien plus utile que trente problèmes choisis au hasard et résolus dans un ordre aléatoire. La roadmap Blind 75 de NeetCode classe ces problèmes dans une séquence similaire pour exactement cette raison.

Évitez les erreurs Python qui cassent de bonnes solutions sur les linked lists

Perdre une référence est la petite erreur la plus coûteuse que vous puissiez faire

Les problèmes de linked lists en Python punissent plus qu’aucun autre une erreur : réaffecter un pointeur avant d’avoir sauvegardé ce qu’il pointait. La liste ne lève pas d’erreur. Elle se coupe simplement en silence, et le résultat est faux d’une manière difficile à retracer, sauf si vous décrivez à voix haute l’état des pointeurs.

La règle est mécanique : avant de modifier `current.next`, sauvegardez `current.next` dans une variable. Avant de déplacer `current`, sauvegardez sa destination. Ce n’est pas de la programmation défensive — c’est l’ordre d’exécution correct pour manipuler des pointeurs.

Les vérifications de None ne sont pas une décoration optionnelle

Une liste vide (`head is None`) et une liste à un seul nœud (`head.next is None`) sont les deux cas limites qui font échouer le plus de solutions. Une traversée qui suppose au moins deux nœuds lèvera un `AttributeError` sur une entrée à un seul nœud. Une suppression qui suppose que la cible n’est pas la tête renverra une liste corrompue. Mentionner ces cas à voix haute avant d’écrire la solution montre à l’intervieweur que vous avez réfléchi au problème — ce n’est pas du pinaillage, c’est l’art de l’entretien.

Ce que cela donne en pratique

Voici une suppression cassée, puis la version corrigée :

Le dummy node supprime le cas limite de suppression de la tête. Le retour anticipé après la suppression empêche de doubler l’avance. Ce ne sont pas des astuces brillantes — ce sont les patterns standards qui empêchent les solutions Python sur linked lists de casser sur les entrées que les intervieweurs testent toujours en premier.

Comment Verve AI peut vous aider à réussir votre entretien de code en Python sur les linked lists

Le problème structurel que ce guide a souligné — connaître le pattern mais perdre le fil lorsqu’il faut l’expliquer en direct — ne se résout pas en lisant. Il se résout en le faisant à voix haute, sous une pression qui ressemble à la réalité, jusqu’à ce que la narration devienne automatique.

Verve AI Coding Copilot est conçu précisément pour cet écart. Il lit votre écran pendant que vous travaillez sur un problème LeetCode, HackerRank ou CodeSignal, et réagit à ce que vous écrivez réellement — pas à une invite générique. Lorsque votre logique de pointeurs dérive ou que la gestion des cas limites est incomplète, Verve AI Coding Copilot met en évidence le problème précis dans son contexte, et non un indice passe-partout. La fonctionnalité Secondary Copilot vous aide à rester concentré sur un seul problème sans perdre votre place lorsque vous bloquez et devez réfléchir pas à pas à l’état des pointeurs. Et comme il reste invisible pendant les entretiens techniques en direct, l’assistance ne s’arrête pas quand l’entretien commence. Pour les candidats qui suivent la progression d’entraînement de ce guide, Verve AI Coding Copilot transforme le travail solitaire en quelque chose qui ressemble davantage à une répétition accompagnée — ce qui est précisément le cadre dans lequel la reconnaissance de pattern se construit.

FAQ

Q: Qu’est-ce qu’une linked list en Python, et en quoi diffère-t-elle d’un tableau dans un entretien ?

Une linked list est une séquence de nœuds où chaque nœud contient une valeur et une référence vers le nœud suivant. Contrairement à un tableau, il n’y a ni bloc mémoire contigu ni accès par index — vous naviguez en suivant les pointeurs depuis la tête. En entretien, la différence clé est la complexité temporelle : les tableaux offrent un accès aléatoire en O(1) mais une insertion/suppression au milieu en O(n) ; les linked lists offrent une insertion/suppression en O(1) une fois le pointeur obtenu, mais une traversée en O(n) pour trouver une position.

Q: Quels sont les patterns de linked list les plus souvent testés par les intervieweurs ?

Les cinq patterns qui couvrent l’immense majorité des questions d’entretien sont : inversion (itérative à trois pointeurs), fusion de deux listes triées (deux pointeurs + dummy node), fast/slow pointers (milieu, cycle, n-ième depuis la fin), dummy node (supprime les cas limites sur la tête) et détection de cycle (algorithme de Floyd). La plupart des problèmes sont de nouvelles variantes de l’un de ces cinq mécanismes.

Q: Comment écrire une classe `ListNode` de base et parcourir, insérer, supprimer et inverser une liste en Python ?

La classe minimale est `class ListNode: def __init__(self, val=0, next=None)`. La traversée utilise `current = head; while current: current = current.next`. La suppression sauvegarde `current.next` avant de réorganiser les liens. L’inversion utilise trois pointeurs — `prev`, `current`, `next_node` — en avançant dans le bon ordre pour ne perdre aucune référence. Le pattern d’inversion itérative de la section 3 couvre précisément la séquence.

Q: Quand faut-il utiliser un dummy node, et comment cela simplifie-t-il la suppression de la tête et les problèmes de fusion ?

Utilisez un dummy node chaque fois que la tête de la liste peut changer ou disparaître — suppression de la tête, fusion, suppression du n-ième depuis la fin. Le dummy se place avant la vraie tête, de sorte que votre algorithme traite tous les nœuds de manière uniforme. Vous retournez `dummy.next` à la fin au lieu de suivre quel nœud est devenu la nouvelle tête. Cela supprime les branchements conditionnels pour le premier nœud sans ajouter de complexité logique.

Q: Comment résoudre les problèmes de nœud du milieu, de détection de cycle et de suppression du n-ième depuis la fin avec fast/slow pointers ?

Pour le nœud du milieu, faites avancer `slow` d’un pas et `fast` de deux pas jusqu’à ce que `fast` ou `fast.next` soit `None` — `slow` est alors au milieu. Pour la détection de cycle, même mouvement ; si `slow == fast` à un moment donné, il y a un cycle. Pour supprimer le n-ième depuis la fin, placez `fast` à `n+1` pas devant `slow` (tous deux au départ d’un dummy node), puis avancez les deux d’un pas à la fois — quand `fast` vaut `None`, `slow.next` est le nœud cible.

Q: Quels cas limites faut-il toujours mentionner dans une réponse d’entretien sur les linked lists ?

Mentionnez toujours : liste vide (`head is None`), liste à un seul nœud, liste à deux nœuds (surtout pour l’inversion et la fusion), et le cas où le nœud cible est la tête. Les mentionner avant d’écrire le code montre que vous avez réfléchi au problème. Les oublier et laisser l’intervieweur trouver un cas qui échoue sur l’un de ces inputs est la façon la plus courante pour une bonne solution de perdre des points.

Q: Quels problèmes de linked list un candidat junior devrait-il pratiquer en premier pour prendre confiance avant les entretiens ?

Dans cet ordre : Reverse Linked List, Merge Two Sorted Lists, Middle of the Linked List, Linked List Cycle, Remove Nth Node From End, Palindrome Linked List, Reorder List. Chaque problème de cette séquence enseigne un pattern ou une combinaison de patterns précise. Les résoudre dans l’ordre construit les bases avant d’introduire les problèmes qui combinent plusieurs techniques.

Q: Comment expliquer clairement à un intervieweur les compromis des linked lists et la manipulation des pointeurs ?

Commencez par le modèle mémoire : les tableaux utilisent une mémoire contiguë pour un accès en O(1), les linked lists utilisent des nœuds dispersés pour une insertion/suppression en O(1) à une position connue. Puis soyez honnête sur la limite pratique — la traversée est en O(n), et en Python, `deque` gère mieux la plupart des cas d’usage en production. Pour la manipulation des pointeurs, décrivez à voix haute l’état de vos pointeurs à chaque étape : « je sauvegarde `next` avant de réorganiser `current.next` ». C’est cette narration qui distingue un candidat qui comprend la mécanique d’un candidat qui a mémorisé le code.

---

Vous n’aviez pas besoin d’une encyclopédie sur les linked lists. Vous aviez besoin d’un guide Python-first qui garde les pointeurs bien ordonnés, du premier constructeur de nœud jusqu’au dernier cas limite. Les patterns de ce guide — inversion, fusion, fast/slow, dummy node, détection de cycle — sont les mêmes que ceux qui reviennent sur toutes les grandes plateformes d’entretien, sous toutes les variantes, à tous les niveaux. Suivez la progression d’entraînement dans l’ordre, verbalisez l’état de vos pointeurs à chaque fois, et vérifiez les cas limites avant d’écrire la moindre ligne de code. La confiance en entretien sur les linked lists ne vient pas du fait d’avoir vu tous les problèmes possibles. Elle vient du fait d’avoir intériorisé un petit ensemble de mécanismes au point que les nouveaux problèmes paraissent familiers avant même que vous ayez fini de les lire.

CW

Cameron Wu

Archives