{
  "chapter": {
    "id": "listes-piles-files",
    "level": "terminale",
    "theme": "Structures de données",
    "title": "Listes, piles, files",
    "description": "Structures de données linéaires : listes chaînées, piles\n(LIFO) et files (FIFO). Opérations de base, complexité,\nimplémentations possibles, applications classiques (parcours,\nparenthésage, parcours en largeur, etc.).",
    "parties": [
      {
        "id": "listes",
        "title": "Listes chaînées et coûts"
      },
      {
        "id": "piles",
        "title": "Piles"
      },
      {
        "id": "files",
        "title": "Files"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "pile",
        "definition"
      ],
      "title": "Définition d'une pile",
      "partie": "piles",
      "statement": "Quel est le principe d'une **pile** (*stack*) ?",
      "options": [
        {
          "text": "Premier entré, premier sorti (FIFO)",
          "correct": false,
          "feedback": "Erreur : c'est le principe d'une **file** (queue),\npas d'une pile. Confusion classique entre les deux.\n"
        },
        {
          "text": "Accès aléatoire à n'importe quel élément",
          "correct": false,
          "feedback": "Erreur : ce serait un tableau classique. Une pile\nimpose un accès uniquement par le sommet.\n"
        },
        {
          "text": "Tri automatique des éléments",
          "correct": false,
          "feedback": "Erreur : aucun tri. Les éléments sont stockés dans\nleur ordre d'arrivée, accessible uniquement par\nle sommet.\n"
        },
        {
          "text": "Dernier entré, premier sorti (LIFO)",
          "correct": true,
          "feedback": "Bonne réponse : LIFO (*Last In, First Out*). On\najoute (push) et on retire (pop) toujours par le\n**sommet**. C'est le comportement d'une pile\nd'assiettes : on prend toujours celle du haut.\n"
        }
      ],
      "explanation": "Les piles sont fondamentales en informatique : pile\nd'appels de fonctions, gestion d'historique (Ctrl+Z),\nanalyse syntaxique de parenthèses, parcours en\nprofondeur d'un graphe."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "file",
        "definition"
      ],
      "title": "Définition d'une file",
      "partie": "files",
      "statement": "Quel est le principe d'une **file** (*queue*) ?",
      "options": [
        {
          "text": "Élimination automatique des doublons",
          "correct": false,
          "feedback": "Erreur : aucune dédoublonnage automatique.\n"
        },
        {
          "text": "Premier entré, premier sorti (FIFO)",
          "correct": true,
          "feedback": "Bonne réponse : FIFO (*First In, First Out*). On\najoute en queue (enfilement) et on retire en tête\n(défilement). C'est le comportement d'une file\nd'attente au supermarché.\n"
        },
        {
          "text": "Dernier entré, premier sorti (LIFO)",
          "correct": false,
          "feedback": "Erreur : c'est le principe d'une **pile**, pas\nd'une file.\n"
        },
        {
          "text": "Accès direct au milieu de la structure",
          "correct": false,
          "feedback": "Erreur : la file impose l'accès uniquement aux\nextrémités.\n"
        }
      ],
      "explanation": "Les files sont utilisées pour : tâches en attente\n(imprimante, ordonnanceur), parcours en largeur d'un graphe,\nbuffers de communication, et tout système où l'on\nveut respecter l'ordre d'arrivée."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "operations"
      ],
      "title": "Opérations sur une pile",
      "partie": "listes",
      "statement": "Quelles sont les **deux opérations principales** d'une\npile ?",
      "options": [
        {
          "text": "`lire(i)` et `ecrire(i, x)`",
          "correct": false,
          "feedback": "Erreur : ce sont des opérations d'accès indexé,\npropres à un tableau ; une pile n'accède qu'à son\nsommet.\n"
        },
        {
          "text": "`add` et `remove`",
          "correct": false,
          "feedback": "Erreur : ces noms sont génériques. Les noms\nstandard sur une pile sont plus spécifiques.\n"
        },
        {
          "text": "`push` et `pop`",
          "correct": true,
          "feedback": "Bonne réponse : `push(x)` (empiler) ajoute `x` au\nsommet ; `pop()` (dépiler) retire et renvoie\nl'élément du sommet. On a aussi souvent `peek()`\nou `top()` (consulter sans retirer) et\n`is_empty()` (tester la vacuité).\n"
        },
        {
          "text": "`enqueue` et `dequeue`",
          "correct": false,
          "feedback": "Erreur : ce sont les opérations d'une **file**,\npas d'une pile.\n"
        }
      ],
      "explanation": "Toutes ces opérations s'effectuent en temps **constant**\n$O(1)$ avec une bonne implémentation. C'est l'avantage\nprincipal des piles et files par rapport aux\nstructures plus complexes."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "operations-file"
      ],
      "title": "Opérations sur une file",
      "partie": "files",
      "statement": "Quelles sont les opérations principales d'une **file** ?",
      "options": [
        {
          "text": "`push` et `pop`",
          "correct": false,
          "feedback": "Erreur : ce sont les opérations d'une **pile**,\npas d'une file.\n"
        },
        {
          "text": "`min` et `max`",
          "correct": false,
          "feedback": "Erreur : ce sont des opérations d'agrégation, pas\nde file.\n"
        },
        {
          "text": "`enqueue` et `dequeue`",
          "correct": true,
          "feedback": "Bonne réponse : `enqueue(x)` ajoute `x` à la queue ;\n`dequeue()` retire et renvoie l'élément en tête.\nComme pour la pile, ces opérations sont en $O(1)$.\n"
        },
        {
          "text": "`sort` et `reverse` (listes)",
          "correct": false,
          "feedback": "Erreur : opérations sur les listes, pas sur les\nfiles.\n"
        }
      ],
      "explanation": "Les noms varient selon les langages et les ouvrages : `enfiler` et `defiler` en français, `enqueue` et `dequeue` en anglais. Le concept reste le même."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "exemple-application"
      ],
      "title": "Application classique des piles",
      "partie": "listes",
      "statement": "Pour vérifier qu'une expression contient des\n**parenthèses bien équilibrées** (par exemple\n`((a + b) * c)`), quelle structure est la plus adaptée ?",
      "options": [
        {
          "text": "Une pile (LIFO)",
          "correct": true,
          "feedback": "Bonne réponse : on empile les parenthèses\nouvrantes ; à chaque parenthèse fermante, on\ndépile et on vérifie la correspondance. À la fin,\nla pile doit être vide. C'est l'algorithme\nstandard.\n"
        },
        {
          "text": "Un tableau trié",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec un tri. C'est l'ordre\nd'apparition qui compte, ce qui est une pile.\n"
        },
        {
          "text": "Une file (FIFO)",
          "correct": false,
          "feedback": "Erreur : la file ne convient pas. Pour\nl'équilibrage, on a besoin d'accéder à la\nparenthèse ouvrante la plus récente, ce qui est\ndu LIFO.\n"
        },
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Erreur : le dictionnaire n'est pas adapté à un\nordre temporel. La pile est la bonne structure.\n"
        }
      ],
      "explanation": "Cet exemple illustre bien le LIFO : la dernière\nparenthèse ouverte est celle qui doit être fermée en\npremier. L'algorithme s'étend aux crochets `[]`,\naccolades `{}`, balises HTML, etc."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "exemple-file"
      ],
      "title": "Application classique des files",
      "partie": "files",
      "statement": "Pour gérer une **file d'impression** (les documents\narrivés en premier sont imprimés en premier), quelle\nstructure utiliser ?",
      "options": [
        {
          "text": "Une pile",
          "correct": false,
          "feedback": "Erreur : avec une pile, le dernier document arrivé\nserait imprimé en premier, ce qui est injuste.\n"
        },
        {
          "text": "Une liste triée par taille",
          "correct": false,
          "feedback": "Erreur : trier par taille fausserait l'ordre\nd'arrivée. Sauf si on veut une politique\nspécifique (le plus petit d'abord), mais ce n'est\npas FIFO.\n"
        },
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Erreur : pas de notion d'ordre temporel naturelle\ndans un dictionnaire.\n"
        },
        {
          "text": "Une file",
          "correct": true,
          "feedback": "Bonne réponse : la file (FIFO) garantit que\nl'ordre d'arrivée est respecté. C'est exactement\nl'usage typique : ordonnanceur du système\nd'exploitation, file d'impression, tampon\nréseau.\n"
        }
      ],
      "explanation": "D'autres situations relèvent de la file : clients servis à un guichet, processus en attente du processeur, paquets en attente dans un routeur."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "pile-python"
      ],
      "title": "Pile en Python",
      "partie": "piles",
      "statement": "En Python, une manière simple d'implémenter une pile\nutilise :",
      "options": [
        {
          "text": "Une liste avec `append()` et `pop()`",
          "correct": true,
          "feedback": "Bonne réponse : `lst.append(x)` ajoute en fin\n(sommet) et `lst.pop()` retire la fin (sommet),\ntous deux en $O(1)$ amorti. Simple et efficace.\n"
        },
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Erreur : le dictionnaire n'a pas de notion d'ordre\nen place pour cet usage.\n"
        },
        {
          "text": "Un simple entier",
          "correct": false,
          "feedback": "Un entier est une valeur\nunique : il ne peut pas\ncontenir une suite\nd'éléments empilés\nsuccessivement.\n"
        },
        {
          "text": "Une simple chaîne de caractères mutable",
          "correct": false,
          "feedback": "Erreur : les chaînes sont immuables en Python.\nEmpiler créerait une nouvelle chaîne à chaque\nopération, c'est inefficace.\n"
        }
      ],
      "explanation": "Pour une pile « propre », on préférera implémenter une\nclasse `Pile` avec méthodes `empiler`, `depiler`,\n`est_vide`, etc. Cela cache la liste sous-jacente et\nempêche les usages aberrants (accès indexé direct)."
    },
    {
      "id": "q08",
      "difficulty": 2,
      "skills": [
        "file-python",
        "implementation",
        "cout"
      ],
      "title": "Implémenter une file rapide en Python",
      "partie": "files",
      "statement": "On veut implémenter en Python une file dont les opérations `enfiler` et `defiler` restent rapides, même avec un très grand nombre d'éléments. Quelle implémentation convient ?",
      "options": [
        {
          "text": "Une seule liste, avec `append` pour enfiler et `pop(0)` pour défiler",
          "correct": false,
          "feedback": "Erreur : `pop(0)` retire le premier élément et oblige à décaler tous les autres d'une case, ce qui coûte un temps proportionnel à la taille de la file à chaque défilement.\n"
        },
        {
          "text": "Deux listes utilisées comme piles : une d'entrée, une de sortie",
          "correct": true,
          "feedback": "Bonne réponse : on empile en entrée avec `append` et on dépile en sortie avec `pop()`. Quand la pile de sortie est vide, on y déverse la pile d'entrée, ce qui remet les éléments dans l'ordre d'arrivée. Chaque élément n'est déplacé qu'une fois, si bien que le coût moyen par opération reste constant.\n"
        },
        {
          "text": "Une seule liste, avec `insert(0, x)` pour enfiler et `pop()` pour défiler",
          "correct": false,
          "feedback": "Erreur : `insert(0, x)` place l'élément au début de la liste et décale tous les autres, ce qui coûte un temps proportionnel à la taille de la file à chaque enfilement.\n"
        },
        {
          "text": "Un p-uplet, reconstruit avec `+` à chaque enfilement et chaque défilement",
          "correct": false,
          "feedback": "Erreur : un p-uplet ne se modifie pas. Chaque opération fabrique un nouveau p-uplet en recopiant tous les éléments, ce qui coûte un temps proportionnel à la taille de la file.\n"
        }
      ],
      "explanation": "Une file s'implémente classiquement avec un tableau ou avec deux piles. Les deux piles évitent tout décalage : chaque élément est empilé une fois, déversé une fois et dépilé une fois."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "pile-appels"
      ],
      "title": "Pile d'appels",
      "partie": "piles",
      "statement": "Quel rôle joue la **pile d'appels** (*call stack*) lors\nde l'exécution d'un programme ?",
      "options": [
        {
          "text": "Stocker les fichiers ouverts",
          "correct": false,
          "feedback": "Erreur : les fichiers sont gérés par le système\nd'exploitation, pas par la pile d'appels.\n"
        },
        {
          "text": "Stocker les données complètes d'une base de données",
          "correct": false,
          "feedback": "Une base de données est\nstockée sur le disque\ndans des structures\ndédiées, sans rapport\navec la pile d'appels\ngérée par\nl'interpréteur.\n"
        },
        {
          "text": "Compter les variables globales",
          "correct": false,
          "feedback": "Erreur : aucun rapport. La pile d'appels gère les\ncontextes locaux.\n"
        },
        {
          "text": "Empiler le contexte de chaque appel de fonction",
          "correct": true,
          "feedback": "Bonne réponse : c'est exactement le rôle d'une\npile au sens LIFO. Quand une fonction A appelle B,\nle contexte de A reste empilé sous celui de B,\njusqu'à ce que B se termine et qu'on dépile pour\nrevenir à A.\n"
        }
      ],
      "explanation": "C'est précisément la pile d'appels qui sature en cas\nde récursion infinie, provoquant le célèbre\n`RecursionError` ou *stack overflow*. Sa taille est\ntypiquement limitée à quelques dizaines de milliers\nd'appels."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "liste-chainee"
      ],
      "title": "Liste chaînée",
      "partie": "listes",
      "statement": "Qu'est-ce qu'une **liste chaînée** ?",
      "options": [
        {
          "text": "Une liste reliée à une base de données",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec une base de données.\n"
        },
        {
          "text": "Une liste qui ne peut pas être modifiée",
          "correct": false,
          "feedback": "Erreur : ce serait une liste immuable. Une liste\nchaînée peut tout à fait être modifiée.\n"
        },
        {
          "text": "Une liste chiffrée",
          "correct": false,
          "feedback": "Erreur : « chaînée » et « chiffrée » sont des\nmots différents. Aucun rapport avec la\ncryptographie.\n"
        },
        {
          "text": "Des maillons reliant chaque valeur au suivant",
          "correct": true,
          "feedback": "Bonne réponse : contrairement à un tableau (mémoire\ncontiguë), une liste chaînée est composée de\nmaillons qui se référencent. Ajouter ou supprimer\nen tête se fait en $O(1)$, mais l'accès indexé\ncoûte $O(n)$.\n"
        }
      ],
      "explanation": "Variantes : listes **simplement chaînées** (un seul\npointeur next), **doublement chaînées** (next et\nprevious), **circulaires** (le dernier pointe vers le\npremier). Chaque variante a ses cas d'usage."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "complexite-pile"
      ],
      "title": "Complexité des opérations sur une pile",
      "partie": "piles",
      "statement": "Quelle est la complexité des opérations `push` et `pop`\nsur une pile, pour une bonne implémentation ?",
      "options": [
        {
          "text": "$O(n)$ pour les deux",
          "correct": false,
          "feedback": "Erreur : c'est trop coûteux. Les piles sont\nréputées efficaces parce que ces opérations sont\nen temps constant.\n"
        },
        {
          "text": "$O(\\log n)$ pour les deux",
          "correct": false,
          "feedback": "Erreur : pas de raison logarithmique. C'est un\naccès direct.\n"
        },
        {
          "text": "$O(1)$ pour les deux",
          "correct": true,
          "feedback": "Bonne réponse : `push` et `pop` agissent toujours\nau sommet, donc en temps constant. C'est ce qui\nrend les piles si utiles dans les algorithmes\ncomme la conversion infixe → postfixe ou le parcours en profondeur.\n"
        },
        {
          "text": "$O(n^2)$ pour les deux",
          "correct": false,
          "feedback": "Erreur : excessif.\n"
        }
      ],
      "explanation": "Avec une liste Python, `append` et `pop()` (sans\nargument) sont en $O(1)$ amorti. Avec une liste\nchaînée et un pointeur sur le sommet, c'est aussi\n$O(1)$. Pas de surprise."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "complexite-acces"
      ],
      "title": "Accès indexé dans une liste chaînée",
      "partie": "listes",
      "statement": "Quelle est la complexité d'accéder au $k$-ième élément\nd'une liste chaînée simplement chaînée ?",
      "options": [
        {
          "text": "$O(k)$",
          "correct": true,
          "feedback": "Bonne réponse : il faut suivre les pointeurs un\npar un depuis la tête. Pour atteindre l'élément\n$k$, il faut $k$ étapes : le coût est linéaire\nen $k$.\n"
        },
        {
          "text": "$O(\\log k)$",
          "correct": false,
          "feedback": "Erreur : pas de division par deux possible. C'est\nplutôt linéaire.\n"
        },
        {
          "text": "$O(1)$",
          "correct": false,
          "feedback": "Erreur : les listes chaînées **n'offrent pas**\nd'accès indexé en temps constant, contrairement\naux tableaux. Il faut parcourir.\n"
        },
        {
          "text": "$O(k^2)$",
          "correct": false,
          "feedback": "Erreur : excessif.\n"
        }
      ],
      "explanation": "C'est le compromis fondamental : une liste chaînée\noffre des insertions/suppressions efficaces ($O(1)$\nen tête, $O(n)$ ailleurs), mais un accès indexé lent.\nPour un accès rapide, utiliser un tableau."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "pile-evaluation"
      ],
      "title": "Évaluation d'une expression postfixée",
      "partie": "piles",
      "statement": "L'évaluation d'une expression en **notation postfixée**\n(par exemple `2 3 + 4 *`) utilise :",
      "options": [
        {
          "text": "Une file",
          "correct": false,
          "feedback": "Erreur : la file ne fonctionne pas pour ce cas. La\nnotation postfixée demande d'accéder aux deux\nderniers opérandes, ce qui est typiquement LIFO.\n"
        },
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Le dictionnaire associe\ndes clés à des valeurs ;\nil ne reflète pas l'ordre\ntemporel d'arrivée des\nopérandes nécessaire à\nl'évaluation d'une\nexpression postfixée.\n"
        },
        {
          "text": "Un arbre binaire de syntaxe",
          "correct": false,
          "feedback": "Erreur : on peut **représenter** une expression par\nun arbre, mais l'évaluation postfixée utilise une\npile.\n"
        },
        {
          "text": "Une pile",
          "correct": true,
          "feedback": "Bonne réponse : on empile chaque opérande et l'on\ndépile à chaque opérateur. Pour `2 3 + 4 *`, on empile $2$,\npuis $3$ ; on rencontre `+`, on dépile $3$ et $2$,\non calcule $5$, on empile $5$ ; on empile $4$ ;\non rencontre `*`, on dépile $4$ et $5$, on calcule\n$20$. Résultat : $20$.\n"
        }
      ],
      "explanation": "Cette technique évite la gestion explicite des parenthèses : l'ordre d'écriture fixe à lui seul l'ordre des calculs."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "interface"
      ],
      "title": "Distinction entre interface et implémentation",
      "partie": "listes",
      "statement": "« Pile » et « file » sont des **interfaces** abstraites.\nCela signifie que :",
      "options": [
        {
          "text": "Elles sont écrites en assembleur",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le langage.\n"
        },
        {
          "text": "Elles définissent le comportement, pas la structure",
          "correct": true,
          "feedback": "Bonne réponse : on peut implémenter une pile avec\nune liste Python, une liste chaînée, un tableau,\netc. Tant que les opérations respectent LIFO,\nc'est une pile. Cette abstraction est essentielle\nen programmation modulaire.\n"
        },
        {
          "text": "Elles sont obsolètes",
          "correct": false,
          "feedback": "Erreur : elles sont au contraire fondamentales et\nomniprésentes.\n"
        },
        {
          "text": "Elles ne peuvent pas du tout être implémentées en Python",
          "correct": false,
          "feedback": "Erreur : Python permet bien sûr d'implémenter ces\nstructures.\n"
        }
      ],
      "explanation": "C'est l'idée des **types abstraits de données**\n(TAD) : on spécifie ce que la structure **doit faire**, pas comment. Une pile peut être implémentée\nde plusieurs façons, l'utilisateur du TAD ne s'en\nsoucie pas."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "pile-vide"
      ],
      "title": "Pile vide",
      "partie": "piles",
      "statement": "Que faut-il faire avant de tenter un `pop()` sur une\npile ?",
      "options": [
        {
          "text": "Recalculer la longueur",
          "correct": false,
          "feedback": "Erreur : pas de calcul à faire avant un pop.\n"
        },
        {
          "text": "Vérifier qu'elle n'est pas vide",
          "correct": true,
          "feedback": "Bonne réponse : un `pop` sur pile vide provoque\nen général une erreur (`IndexError` en Python). Il\nfaut toujours vérifier d'abord, ou capturer\nl'exception.\n"
        },
        {
          "text": "Toujours appeler `peek()` d'abord",
          "correct": false,
          "feedback": "Erreur : `peek()` consulte mais ne protège pas\nd'un pop sur une pile vide.\n"
        },
        {
          "text": "Multiplier la taille par deux",
          "correct": false,
          "feedback": "Erreur : aucun rapport. Aucune opération de\nredimensionnement n'est nécessaire avant un pop.\n"
        }
      ],
      "explanation": "Bonne pratique : dans une classe `Pile`, proposer une méthode `est_vide` que l'utilisateur appelle avant de dépiler, ou vérifier la condition au début de `depiler` avec un `assert` explicite."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "comparaison-tableau"
      ],
      "title": "Liste chaînée vs tableau",
      "partie": "listes",
      "statement": "Quel est l'**avantage principal** d'une liste chaînée\nsur un tableau dynamique ?",
      "options": [
        {
          "text": "L'accès indexé est plus rapide",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse. Le tableau a un accès\n$O(1)$, la liste chaînée $O(n)$.\n"
        },
        {
          "text": "Insertion en tête en temps constant",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'avantage clé. Avec un\ntableau, insérer en tête demande de décaler tous\nles éléments. Avec une liste chaînée, on\nmodifie juste un pointeur.\n"
        },
        {
          "text": "Elle utilise moins de mémoire",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse, en général. Une liste\nchaînée stocke un pointeur supplémentaire par\nélément, ce qui consomme plus de mémoire.\n"
        },
        {
          "text": "Elle est intrinsèquement toujours triée",
          "correct": false,
          "feedback": "Erreur : aucun tri automatique.\n"
        }
      ],
      "explanation": "Compromis classique : tableau pour l'accès indexé\nrapide ; liste chaînée pour les insertions/suppressions\nfréquentes en début ou milieu (avec un pointeur sur\nl'emplacement)."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "implementation"
      ],
      "title": "Implémentation orientée objet d'une pile",
      "partie": "listes",
      "statement": "En Python, dans une classe `Pile` minimale, quelles\nméthodes définit-on typiquement ?",
      "options": [
        {
          "text": "`lire(i)`, `ecrire(i, x)`, `taille`",
          "correct": false,
          "feedback": "Erreur : lire ou écrire à un indice\nquelconque, ce serait un tableau, pas une\npile, qui n'est accessible que par son\nsommet.\n"
        },
        {
          "text": "`add`, `remove`, `find`, `sort`",
          "correct": false,
          "feedback": "Erreur : ces méthodes ne correspondent pas à\nl'API d'une pile. Une pile n'offre pas de `find`\nou `sort`.\n"
        },
        {
          "text": "Une seule méthode `do(action, x)`",
          "correct": false,
          "feedback": "Erreur : trop minimaliste. Une pile expose\nplusieurs opérations distinctes, chacune avec sa\npropre méthode.\n"
        },
        {
          "text": "`empiler`, `depiler`, `est_vide`",
          "correct": true,
          "feedback": "Bonne réponse : ces trois méthodes forment le cœur de l'interface d'une pile. On y ajoute souvent `sommet()`, qui consulte sans dépiler, ou `taille()`.\n"
        }
      ],
      "explanation": "L'utilisateur ne manipule que les méthodes de la pile, jamais la liste qui la stocke : c'est ce qui distingue une pile, accessible par son seul sommet, d'un tableau libre."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "equilibrage-parentheses"
      ],
      "title": "Détection de parenthèses déséquilibrées",
      "partie": "piles",
      "statement": "L'expression `(())` est-elle bien parenthésée ? Et\n`(()` ?",
      "options": [
        {
          "text": "`(())` et `(()` sont toutes deux bien parenthésées",
          "correct": false,
          "feedback": "Erreur : `(()` ne ferme pas la première\nparenthèse. Donc déséquilibrée.\n"
        },
        {
          "text": "La parité du nombre de caractères suffit à juger",
          "correct": false,
          "feedback": "Erreur : `()(` est de longueur paire mais\ndéséquilibré. La parité ne suffit pas.\n"
        },
        {
          "text": "`(())` est correcte, `(()` ne l'est pas",
          "correct": true,
          "feedback": "Bonne réponse : avec une pile, on empile chaque\n`(`, on dépile à chaque `)`. Pour `(())`, la pile\nest vide à la fin. Pour `(()`, la pile contient\nencore la première parenthèse, qui n'est jamais\nfermée : l'expression est déséquilibrée.\n"
        },
        {
          "text": "`(())` et `(()` sont toutes deux mal parenthésées",
          "correct": false,
          "feedback": "Erreur : `(())` est parfaitement équilibrée :\ndeux parenthèses ouvrantes, deux fermantes, dans\nle bon ordre.\n"
        }
      ],
      "explanation": "Règle algorithmique : une expression est bien\nparenthésée si et seulement si la pile est vide à la\nfin, ET qu'aucune fermante n'apparaît sur une pile\nvide pendant le parcours."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "code",
        "analyse"
      ],
      "title": "Analyse d'un code de pile",
      "partie": "listes",
      "statement": "Que renvoie le code Python suivant ?\n\n```python\npile = []\nfor x in [1, 2, 3, 4]:\n    pile.append(x)\nprint(pile.pop())\n```",
      "options": [
        {
          "text": "$0$",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec $0$.\n"
        },
        {
          "text": "La liste entière `[1, 2, 3]`",
          "correct": false,
          "feedback": "Erreur : `pop()` ne retourne qu'un seul élément.\n"
        },
        {
          "text": "$1$",
          "correct": false,
          "feedback": "Erreur : c'est l'élément de **bas** de pile (le\npremier empilé). Mais `pop()` retire le **sommet**\n(le dernier empilé).\n"
        },
        {
          "text": "$4$",
          "correct": true,
          "feedback": "Bonne réponse : on a empilé $1, 2, 3, 4$. Le\nsommet est $4$. `pop()` retire et renvoie $4$.\n"
        }
      ],
      "explanation": "Comportement standard LIFO : dernier entré, premier\nsorti. Si on continuait à `pop()` plusieurs fois, on\nobtiendrait $4, 3, 2, 1$ dans l'ordre."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "implementation",
        "cout"
      ],
      "title": "Pourquoi pas list.pop(0) pour une file",
      "partie": "listes",
      "statement": "Pourquoi `list.pop(0)` est-il **inefficace** pour\nimplémenter une file en Python ?",
      "options": [
        {
          "text": "Parce que les listes Python ne sont pas mutables",
          "correct": false,
          "feedback": "Erreur : les listes sont mutables.\n"
        },
        {
          "text": "Il faut décaler tous les éléments, en $O(n)$",
          "correct": true,
          "feedback": "Bonne réponse : les éléments d'une liste Python sont rangés les uns à la suite des autres en mémoire. Retirer le premier élément force à décaler tous les autres d'une case.\n"
        },
        {
          "text": "Parce que ça lève toujours une erreur",
          "correct": false,
          "feedback": "Erreur : `pop(0)` fonctionne sur une liste non\nvide. Le problème est la performance, pas la\ncorrection.\n"
        },
        {
          "text": "Parce que `pop` ne peut pas prendre d'argument",
          "correct": false,
          "feedback": "Erreur : `pop(i)` accepte un argument optionnel.\n"
        }
      ],
      "explanation": "C'est un piège classique en Python. Pour une file efficace, on peut par exemple utiliser deux piles, l'une pour enfiler et l'autre pour défiler, ou une liste accompagnée d'un indice qui repère la tête de la file."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "pile-recursion"
      ],
      "title": "Récursivité et pile",
      "partie": "piles",
      "statement": "Toute fonction récursive peut être convertie en une\nversion itérative utilisant explicitement :",
      "options": [
        {
          "text": "Une file",
          "correct": false,
          "feedback": "Erreur : la récursivité a une nature LIFO (l'appel\nle plus profond est traité en premier). C'est\ndonc une pile.\n"
        },
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Erreur : un dictionnaire ne préserve pas l'ordre\nd'appels. La structure adaptée est une pile.\n"
        },
        {
          "text": "Une pile (la pile d'appels)",
          "correct": true,
          "feedback": "Bonne réponse : c'est précisément ce que fait le\nprocesseur en interne. En remplaçant les appels\nrécursifs par des opérations sur une pile\nexplicite, on transforme une fonction récursive\nen boucle. Cela peut éviter les dépassements de\npile.\n"
        },
        {
          "text": "Un arbre binaire de recherche",
          "correct": false,
          "feedback": "Erreur : un arbre est une donnée, pas un\nmécanisme d'exécution.\n"
        }
      ],
      "explanation": "Cette technique de **dérécursivation** est utile\nquand la profondeur de récursion dépasse la limite\nPython (souvent $1000$). On remplace les appels par\ndes push/pop sur une pile explicite, et on contrôle\nainsi la mémoire utilisée."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "dictionnaire",
        "recherche",
        "cout"
      ],
      "title": "Chercher par clé ou par valeur dans un dictionnaire",
      "partie": "listes",
      "statement": "Le dictionnaire `annuaire` associe à chaque nom un numéro de téléphone : par exemple, `annuaire[\"Léa\"]` vaut `\"0601020304\"`. Il contient $100000$ entrées. On effectue deux recherches :\n\n- (A) trouver le numéro de Léa ;\n- (B) trouver le nom de la personne dont le numéro est `\"0601020304\"`.\n\nQue peut-on dire de leur coût ?",
      "options": [
        {
          "text": "Les deux sont quasi immédiates, car un dictionnaire est indexé",
          "correct": false,
          "feedback": "Erreur : seul l'accès par clé est rapide. Rien ne permet d'aller directement à la valeur `\"0601020304\"` : il faut parcourir les entrées jusqu'à la trouver.\n"
        },
        {
          "text": "Les deux demandent de parcourir les entrées, comme dans une liste",
          "correct": false,
          "feedback": "Erreur : ce serait le cas si l'annuaire était une liste de couples. Dans un dictionnaire, l'accès par clé `annuaire[\"Léa\"]` ne parcourt pas les entrées et prend un temps constant en moyenne.\n"
        },
        {
          "text": "(B) est quasi immédiate, (A) demande de parcourir les entrées",
          "correct": false,
          "feedback": "Erreur : tu inverses les rôles des clés et des valeurs. Ici, les noms sont les clés, ce sont eux qui donnent un accès direct ; les numéros sont les valeurs.\n"
        },
        {
          "text": "(A) est quasi immédiate, (B) demande de parcourir les entrées",
          "correct": true,
          "feedback": "Bonne réponse : l'accès `annuaire[\"Léa\"]` prend un temps constant en moyenne, quelle que soit la taille du dictionnaire. Retrouver un nom à partir d'un numéro oblige au contraire à examiner les couples un par un, comme dans une liste.\n"
        }
      ],
      "explanation": "Chercher par clé dans un dictionnaire prend un temps constant en moyenne ; chercher une valeur dans une liste, ou parmi les valeurs d'un dictionnaire, demande un parcours de coût linéaire. Pour retrouver rapidement un nom à partir d'un numéro, il faudrait un second dictionnaire dont les numéros sont les clés."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Choisir la bonne structure",
      "partie": "listes",
      "statement": "On souhaite gérer l'historique des actions d'un éditeur\nde texte, avec une fonction « annuler » (Ctrl+Z). Quelle\nstructure est la plus adaptée ?",
      "options": [
        {
          "text": "Une liste triée",
          "correct": false,
          "feedback": "Erreur : pas de tri à faire ; l'ordre est déjà\ndonné par l'arrivée.\n"
        },
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Erreur : pas de notion temporelle adaptée.\n"
        },
        {
          "text": "Une pile",
          "correct": true,
          "feedback": "Bonne réponse : à chaque action, on l'empile.\nCtrl+Z dépile et annule la dernière. C'est le cas\nd'usage typique des piles. Pour le « refaire »\n(Ctrl+Y), on utilise une **deuxième** pile.\n"
        },
        {
          "text": "Une file",
          "correct": false,
          "feedback": "Erreur : la file annulerait la **première** action,\npas la dernière. Or « annuler » concerne la plus\nrécente.\n"
        }
      ],
      "explanation": "Petit défi : implémenter Ctrl+Z **et** Ctrl+Y. Astuce\nclassique : deux piles. Quand on annule, on dépile de\nla pile « historique » et on empile dans la pile\n« refait ». Quand on refait, c'est l'inverse."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "maillon-implementation"
      ],
      "title": "Maillon d'une liste chaînée",
      "partie": "listes",
      "statement": "On souhaite implémenter une liste simplement chaînée\nen Python. Quelle classe représente un **maillon**\ncorrectement ?",
      "options": [
        {
          "text": "```python\nclass Maillon:\n    def __init__(self, valeur):\n        self.valeur = valeur\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque l'attribut `suivant`. Sans\nlui, le maillon ne peut pas pointer vers le\nsuivant et la « chaîne » est cassée. Ce serait\njuste un conteneur de valeur, pas un maillon.\n"
        },
        {
          "text": "```python\nclass Maillon:\n    def __init__(self, valeur, suivant=None):\n        self.valeur = valeur\n        self.suivant = suivant\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : un maillon contient sa propre\nvaleur et une référence vers le maillon suivant\n(ou `None` si c'est le dernier). C'est la\ndéfinition canonique. La construction d'une\nliste chaînée se fait ensuite en chaînant\nplusieurs maillons via leur attribut `suivant`.\n"
        },
        {
          "text": "```python\ndef maillon(valeur):\n    return [valeur]\n```\n",
          "correct": false,
          "feedback": "Erreur : on retourne ici une liste Python avec\nun seul élément, ce qui n'est pas un maillon.\nPour chaîner plusieurs valeurs, il faudrait\nimbriquer des listes, ce qui est lourd et peu\nclair par rapport à une vraie classe.\n"
        },
        {
          "text": "```python\nclass Maillon:\n    def __init__(self, valeur, precedent, suivant):\n        self.valeur = valeur\n        self.precedent = precedent\n        self.suivant = suivant\n```\n",
          "correct": false,
          "feedback": "Erreur : on a ici un maillon **doublement chaîné** (avec un pointeur vers le précédent et\nun vers le suivant). C'est utile pour certaines\nstructures, mais la question portait sur une\nliste **simplement** chaînée.\n"
        }
      ],
      "explanation": "Pour parcourir la liste chaînée, on commence par la\ntête et on suit les pointeurs `suivant` jusqu'à\natteindre `None`. La classe `Maillon` est l'élément\natomique ; la classe `ListeChainee` (ou directement\nune variable « tête ») gère la collection."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "parcours-profondeur"
      ],
      "title": "Parcours en profondeur avec pile explicite",
      "partie": "listes",
      "statement": "Pour effectuer un parcours en profondeur d'un arbre\nou d'un graphe **sans utiliser la récursivité**, quel\nalgorithme et quelle structure utilise-t-on ?",
      "options": [
        {
          "text": "Une pile : dépiler un sommet, le marquer, empiler ses voisins",
          "correct": true,
          "feedback": "Bonne réponse : la pile explicite simule\nexactement ce que ferait la récursion via la\npile d'appels du langage. C'est un cas concret\nde dérécursivation. Avantage : on contrôle la\nmémoire et on évite les `RecursionError` sur\nles graphes profonds.\n"
        },
        {
          "text": "On utilise un dictionnaire pour stocker les\nsommets visités\n",
          "correct": false,
          "feedback": "Réponse partielle : un dictionnaire (ou un\nensemble) sert effectivement à mémoriser les\nsommets déjà visités, mais ce n'est pas la\nstructure qui dirige le parcours. C'est la\npile (ou la file) qui décide de l'ordre de\nvisite.\n"
        },
        {
          "text": "On utilise une file : on enfile le sommet\nde départ et on défile à chaque étape\n",
          "correct": false,
          "feedback": "Erreur : avec une file (FIFO), on obtient un\nparcours en **largeur**, pas en profondeur.\nLe parcours en profondeur explore d'abord\nles descendants avant les frères, ce qui est\nun comportement LIFO.\n"
        },
        {
          "text": "On trie d'abord les sommets puis on les parcourt\ndans l'ordre\n",
          "correct": false,
          "feedback": "Erreur : on parle ici d'un parcours qui\nrespecte la structure du graphe ou de l'arbre,\npas d'un parcours par valeur croissante. Trier\nn'a pas de sens dans ce contexte.\n"
        }
      ],
      "explanation": "Mémoire récurrente :\n- **Pile** + parcours = parcours en **profondeur**.\n- **File** + parcours = parcours en **largeur**.\nCette dualité s'observe sur les arbres comme sur\nles graphes, et illustre la puissance des\nstructures linéaires LIFO/FIFO en algorithmique."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "annuler-refaire"
      ],
      "title": "Annuler et refaire avec deux piles",
      "partie": "piles",
      "statement": "On veut implémenter une fonctionnalité « annuler »\n(Ctrl+Z) et « refaire » (Ctrl+Y) dans un éditeur.\nQuelle structure utiliser ?",
      "options": [
        {
          "text": "Un dictionnaire indexé par l'instant de l'action\n",
          "correct": false,
          "feedback": "Solution surdimensionnée : un dictionnaire\npermet d'aller à n'importe quel instant, ce\nqui n'est pas demandé pour Ctrl+Z/Ctrl+Y. Les\ndeux opérations agissent toujours sur la\ndernière action ou la dernière action\nannulée, ce qui correspond à des piles.\n"
        },
        {
          "text": "Une file pour l'historique, une pile pour le refaire",
          "correct": false,
          "feedback": "Erreur : une file annulerait l'action la plus\nancienne (FIFO), alors qu'on veut annuler la\nplus récente (LIFO). Il faut donc une **pile**\npour l'historique, pas une file.\n"
        },
        {
          "text": "Deux piles, une « historique » et une « refaire »",
          "correct": true,
          "feedback": "Bonne réponse : c'est le schéma standard des\néditeurs de texte. Les deux piles évoluent\nsymétriquement, sauf au moment d'une nouvelle\naction, qui rend impossible de « refaire » les\nactions annulées précédemment (c'est le\ncomportement attendu : on ne peut pas refaire\nce qui n'existe plus dans la branche\nd'historique courante).\n"
        },
        {
          "text": "Une seule pile suffit\n",
          "correct": false,
          "feedback": "Erreur : avec une seule pile, on peut annuler\nmais on perd définitivement les actions\nannulées. Pour pouvoir les **refaire**, il\nfaut les conserver quelque part : c'est le\nrôle de la deuxième pile.\n"
        }
      ],
      "explanation": "Variante avancée : pour gérer un historique\narborescent (où chaque action peut produire une\nbranche), il faut utiliser un **arbre**, pas\nseulement deux piles. C'est le modèle utilisé par\ncertains éditeurs avancés (vim, Emacs en mode\n`undo-tree`)."
    },
    {
      "id": "q29",
      "difficulty": 1,
      "skills": [
        "interface",
        "implementation",
        "file"
      ],
      "title": "Deux implémentations d'une même file",
      "partie": "listes",
      "statement": "Deux programmeurs implémentent une file : l'un avec\nune liste Python, l'autre avec deux piles. Qu'ont en\ncommun leurs deux versions ?",
      "options": [
        {
          "text": "La même interface (`enfiler`, `defiler`, `est_vide`) et le même ordre premier entré, premier sorti",
          "correct": true,
          "feedback": "Un programme qui utilise la file ne voit aucune\ndifférence entre les deux.\n"
        },
        {
          "text": "Le même code, à quelques noms de variables et de fonctions près, puisque le résultat est le même",
          "correct": false,
          "feedback": "Les codes diffèrent entièrement ; seul le\ncomportement observable est identique.\n"
        },
        {
          "text": "Le même coût pour chaque opération, puisque les deux versions produisent exactement le même résultat",
          "correct": false,
          "feedback": "Le coût dépend de l'implémentation : `pop(0)` sur\nune liste décale tous les éléments, les deux\npiles l'évitent.\n"
        },
        {
          "text": "Rien du tout : une file construite avec deux piles n'est pas une vraie file, seule la liste en est une",
          "correct": false,
          "feedback": "Si elle rend les éléments dans l'ordre d'arrivée,\nc'est une file, quelle que soit sa construction.\n"
        }
      ],
      "explanation": "L'interface d'une structure est la liste de ses\nopérations et de ce qu'elles garantissent ;\nl'implémentation est la façon de les programmer.\nPlusieurs implémentations peuvent respecter la même\ninterface, avec des coûts différents."
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "interface",
        "implementation",
        "remplacement"
      ],
      "title": "Remplacer l'implémentation sans toucher au programme",
      "partie": "listes",
      "statement": "Un programme de gestion d'impressions utilise une\nfile uniquement par `enfiler`, `defiler` et\n`est_vide`. On remplace l'implémentation de la file\npar une version plus rapide qui offre les mêmes\nopérations. Que faut-il modifier dans le programme ?",
      "options": [
        {
          "text": "Rien : il n'utilise que l'interface, qui n'a pas changé",
          "correct": true,
          "feedback": "C'est tout l'intérêt de programmer contre une\ninterface : l'implémentation peut changer\nlibrement.\n"
        },
        {
          "text": "Tous les appels à `defiler`, car la nouvelle version rend les éléments dans l'autre sens",
          "correct": false,
          "feedback": "Une file reste une file : `defiler` rend toujours\nle plus ancien élément, sinon l'interface ne\nserait pas respectée.\n"
        },
        {
          "text": "Les noms des opérations, qui dépendent de l'implémentation choisie",
          "correct": false,
          "feedback": "Les noms font partie de l'interface, justement\nconservée.\n"
        },
        {
          "text": "Le programme entier, une structure de données ne se remplaçant pas en cours de projet",
          "correct": false,
          "feedback": "Séparer interface et implémentation sert\nprécisément à rendre ce remplacement indolore.\n"
        }
      ],
      "explanation": "Le programme ne connaît la file que par ses trois\nopérations. Tant que la nouvelle implémentation les\nfournit avec le même comportement, aucun appel ne\nchange. C'est la distinction entre interface et\nimplémentation."
    },
    {
      "id": "q31",
      "difficulty": 3,
      "skills": [
        "interface",
        "implementation",
        "file",
        "analyse-code"
      ],
      "title": "Deux codes, une même file ?",
      "partie": "listes",
      "statement": "Deux implémentations d'une file à partir d'une liste\nPython `f`, initialement vide :\n\n- version A : `enfiler(x)` fait `f.append(x)` et\n  `defiler()` renvoie `f.pop(0)` ;\n- version B : `enfiler(x)` fait `f.insert(0, x)` et\n  `defiler()` renvoie `f.pop()`.\n\nOn enfile $1$, $2$ puis $3$, et on défile trois\nfois. Laquelle se comporte comme une file ?",
      "options": [
        {
          "text": "Les deux : chacune rend $1$, puis $2$, puis $3$",
          "correct": true,
          "feedback": "A range à droite et retire à gauche ; B range à\ngauche et retire à droite. Dans les deux cas, le\nplus ancien sort en premier.\n"
        },
        {
          "text": "Seule la version A, la version B rendant $3$, $2$, $1$",
          "correct": false,
          "feedback": "Dans B, après trois enfilements la liste vaut\n`[3, 2, 1]` et `pop()` retire $1$, le plus\nancien.\n"
        },
        {
          "text": "Seule la version B, la version A rendant $3$, $2$, $1$",
          "correct": false,
          "feedback": "Dans A, la liste vaut `[1, 2, 3]` et `pop(0)`\nretire $1$ : l'ordre d'arrivée est respecté.\n"
        },
        {
          "text": "Aucune des deux, une file ne pouvant pas se construire avec une seule liste",
          "correct": false,
          "feedback": "Une liste suffit ; les deux versions respectent\nl'interface, avec des coûts différents.\n"
        }
      ],
      "explanation": "Les deux versions respectent le comportement premier\nentré, premier sorti : elles implémentent la même\ninterface. Elles diffèrent par le coût : chacune\ncontient une opération qui décale tous les éléments\n(`pop(0)` dans A, `insert(0, x)` dans B)."
    },
    {
      "id": "q32",
      "difficulty": 2,
      "skills": [
        "interface",
        "implementation",
        "pile",
        "analyse-code"
      ],
      "title": "Une pile qui empile en tête",
      "partie": "listes",
      "statement": "```python\nclass Pile:\n    def __init__(self):\n        self.elements = []\n\n    def empiler(self, x):\n        self.elements.insert(0, x)\n\n    def depiler(self):\n        return self.elements.pop(0)\n\n    def est_vide(self):\n        return len(self.elements) == 0\n```\n\nCette classe est-elle une implémentation correcte\nd'une pile ?",
      "options": [
        {
          "text": "Oui : le dernier empilé est le premier dépilé ; seul le coût diffère de la version `append` et `pop()`",
          "correct": true,
          "feedback": "Après `empiler(1)`, `empiler(2)`, `empiler(3)`, la\nliste vaut `[3, 2, 1]` et `depiler()` renvoie\n$3$.\n"
        },
        {
          "text": "Non : `depiler` renvoie le premier élément empilé, ce qui en fait une file et non une pile",
          "correct": false,
          "feedback": "`pop(0)` retire l'élément en tête, et c'est le\ndernier empilé qui s'y trouve.\n"
        },
        {
          "text": "Non : une pile doit obligatoirement s'écrire avec `append` pour empiler et `pop()` pour dépiler",
          "correct": false,
          "feedback": "L'interface impose un comportement, pas des\nméthodes de liste précises.\n"
        },
        {
          "text": "Oui, mais seulement pour des entiers, la méthode `insert` refusant les autres types de valeurs",
          "correct": false,
          "feedback": "`insert` accepte n'importe quel objet ; le type\ndes éléments n'a aucune importance.\n"
        }
      ],
      "explanation": "La classe respecte l'interface d'une pile : dernier\nentré, premier sorti. Elle est simplement moins\nefficace que la version qui empile en fin de liste,\ncar `insert(0, x)` et `pop(0)` décalent tous les\néléments à chaque opération."
    },
    {
      "id": "q33",
      "difficulty": 2,
      "skills": [
        "files",
        "trace",
        "enfiler",
        "defiler"
      ],
      "title": "Trace d'une suite d'opérations sur une file",
      "partie": "files",
      "statement": "Sur une file vide, on enfile $3$, on enfile $7$, on défile, on\nenfile $2$, on défile, puis on enfile $9$. Que contient la\nfile, de la tête vers la queue ?",
      "options": [
        {
          "text": "$2$ puis $9$",
          "correct": true,
          "feedback": "Bonne réponse : le premier défilement retire $3$, le second\nretire $7$ ; restent $2$ puis $9$, dans l'ordre d'arrivée.\n"
        },
        {
          "text": "$3$ puis $9$, comme dans une pile",
          "correct": false,
          "feedback": "Erreur : ce serait le contenu d'une pile, où l'on retire le\ndernier arrivé.\n"
        },
        {
          "text": "$7$ puis $2$",
          "correct": false,
          "feedback": "Erreur : $7$ a été retiré par le second défilement.\n"
        },
        {
          "text": "$9$ puis $2$",
          "correct": false,
          "feedback": "Erreur : $2$ est arrivé avant $9$, il est donc en tête.\n"
        }
      ],
      "explanation": "Dans une file, défiler retire toujours l'élément le plus ancien ;\nsuivre une trace, c'est tenir la liste des arrivées non encore\nservies.",
      "variants": [
        {
          "statement": "Sur une file vide, on enfile $3$, on enfile $7$, on défile, on\nenfile $2$, on défile, puis on enfile $9$. Que contient la\nfile, de la tête vers la queue ?",
          "options": [
            {
              "text": "$2$ puis $9$",
              "correct": true,
              "feedback": "Bonne réponse : le premier défilement retire $3$, le second\nretire $7$ ; restent $2$ puis $9$, dans l'ordre d'arrivée.\n"
            },
            {
              "text": "$3$ puis $9$, comme dans une pile",
              "correct": false,
              "feedback": "Erreur : ce serait le contenu d'une pile, où l'on retire le\ndernier arrivé.\n"
            },
            {
              "text": "$7$ puis $2$",
              "correct": false,
              "feedback": "Erreur : $7$ a été retiré par le second défilement.\n"
            },
            {
              "text": "$9$ puis $2$",
              "correct": false,
              "feedback": "Erreur : $2$ est arrivé avant $9$, il est donc en tête.\n"
            }
          ],
          "explanation": "Dans une file, défiler retire toujours l'élément le plus ancien ;\nsuivre une trace, c'est tenir la liste des arrivées non encore\nservies."
        },
        {
          "statement": "Sur une file vide, on enfile $5$, on enfile $1$, on défile, on\nenfile $8$, on défile, puis on enfile $4$. Que contient la\nfile, de la tête vers la queue ?",
          "options": [
            {
              "text": "$8$ puis $4$",
              "correct": true,
              "feedback": "Bonne réponse : le premier défilement retire $5$, le second\nretire $1$ ; restent $8$ puis $4$, dans l'ordre d'arrivée.\n"
            },
            {
              "text": "$5$ puis $4$, comme dans une pile",
              "correct": false,
              "feedback": "Erreur : ce serait le contenu d'une pile, où l'on retire le\ndernier arrivé.\n"
            },
            {
              "text": "$1$ puis $8$",
              "correct": false,
              "feedback": "Erreur : $1$ a été retiré par le second défilement.\n"
            },
            {
              "text": "$4$ puis $8$",
              "correct": false,
              "feedback": "Erreur : $8$ est arrivé avant $4$, il est donc en tête.\n"
            }
          ],
          "explanation": "Dans une file, défiler retire toujours l'élément le plus ancien ;\nsuivre une trace, c'est tenir la liste des arrivées non encore\nservies."
        },
        {
          "statement": "Sur une file vide, on enfile $6$, on enfile $2$, on défile, on\nenfile $3$, on défile, puis on enfile $7$. Que contient la\nfile, de la tête vers la queue ?",
          "options": [
            {
              "text": "$3$ puis $7$",
              "correct": true,
              "feedback": "Bonne réponse : le premier défilement retire $6$, le second\nretire $2$ ; restent $3$ puis $7$, dans l'ordre d'arrivée.\n"
            },
            {
              "text": "$6$ puis $7$, comme dans une pile",
              "correct": false,
              "feedback": "Erreur : ce serait le contenu d'une pile, où l'on retire le\ndernier arrivé.\n"
            },
            {
              "text": "$2$ puis $3$",
              "correct": false,
              "feedback": "Erreur : $2$ a été retiré par le second défilement.\n"
            },
            {
              "text": "$7$ puis $3$",
              "correct": false,
              "feedback": "Erreur : $3$ est arrivé avant $7$, il est donc en tête.\n"
            }
          ],
          "explanation": "Dans une file, défiler retire toujours l'élément le plus ancien ;\nsuivre une trace, c'est tenir la liste des arrivées non encore\nservies."
        }
      ]
    },
    {
      "id": "q34",
      "difficulty": 3,
      "skills": [
        "files",
        "piles",
        "implementation"
      ],
      "title": "Une file construite avec deux piles",
      "partie": "files",
      "statement": "Une file est réalisée avec deux piles, `entree` et `sortie` :\nenfiler empile sur `entree`. Pour défiler, que faire quand\n`sortie` est vide ?",
      "options": [
        {
          "text": "Vider `entree` dans `sortie` un par un, puis dépiler `sortie`",
          "correct": true,
          "feedback": "Bonne réponse : en passant d'une pile à l'autre, l'ordre\ns'inverse, le plus ancien se retrouve au sommet de `sortie`.\nChaque élément n'est déplacé qu'une fois, le coût moyen reste\nconstant.\n"
        },
        {
          "text": "Dépiler directement `entree`, qui contient tous les éléments",
          "correct": false,
          "feedback": "Erreur : le sommet de `entree` est le dernier arrivé, on\nobtiendrait une pile.\n"
        },
        {
          "text": "Renvoyer une erreur, la file étant forcément vide elle aussi",
          "correct": false,
          "feedback": "Erreur : la file peut contenir des éléments encore dans\n`entree`.\n"
        },
        {
          "text": "Échanger les noms des deux piles sans déplacer aucun élément",
          "correct": false,
          "feedback": "Erreur : échanger ne renverse pas l'ordre, le sommet resterait\nle dernier arrivé.\n"
        }
      ],
      "explanation": "Deux inversions successives rétablissent l'ordre d'arrivée : c'est\nce qui permet de faire une file avec deux piles."
    },
    {
      "id": "q35",
      "difficulty": 1,
      "skills": [
        "files",
        "fifo",
        "defiler"
      ],
      "title": "Le premier élément servi",
      "partie": "files",
      "statement": "Une file contient, de la tête vers la queue, $4$, $8$ et\n$1$. On enfile $6$ puis on défile. Quelle valeur est\nretirée ?",
      "options": [
        {
          "text": "$4$, la tête de la file",
          "correct": true,
          "feedback": "Bonne réponse : enfiler ajoute en queue et ne change pas la\ntête ; défiler retire la tête, $4$.\n"
        },
        {
          "text": "$6$, le dernier enfilé",
          "correct": false,
          "feedback": "Erreur : retirer le dernier arrivé serait le comportement d'une\npile.\n"
        },
        {
          "text": "$1$, le dernier avant l'ajout",
          "correct": false,
          "feedback": "Erreur : $1$ est en queue, loin de la tête.\n"
        },
        {
          "text": "$8$, l'élément du milieu",
          "correct": false,
          "feedback": "Erreur : $8$ ne sort qu'après $4$.\n"
        }
      ],
      "explanation": "Premier arrivé, premier servi : enfiler et défiler agissent aux\ndeux extrémités opposées.",
      "variants": [
        {
          "statement": "Une file contient, de la tête vers la queue, $4$, $8$ et\n$1$. On enfile $6$ puis on défile. Quelle valeur est\nretirée ?",
          "options": [
            {
              "text": "$4$, la tête de la file",
              "correct": true,
              "feedback": "Bonne réponse : enfiler ajoute en queue et ne change pas la\ntête ; défiler retire la tête, $4$.\n"
            },
            {
              "text": "$6$, le dernier enfilé",
              "correct": false,
              "feedback": "Erreur : retirer le dernier arrivé serait le comportement d'une\npile.\n"
            },
            {
              "text": "$1$, le dernier avant l'ajout",
              "correct": false,
              "feedback": "Erreur : $1$ est en queue, loin de la tête.\n"
            },
            {
              "text": "$8$, l'élément du milieu",
              "correct": false,
              "feedback": "Erreur : $8$ ne sort qu'après $4$.\n"
            }
          ],
          "explanation": "Premier arrivé, premier servi : enfiler et défiler agissent aux\ndeux extrémités opposées."
        },
        {
          "statement": "Une file contient, de la tête vers la queue, $9$, $2$ et\n$5$. On enfile $3$ puis on défile. Quelle valeur est\nretirée ?",
          "options": [
            {
              "text": "$9$, la tête de la file",
              "correct": true,
              "feedback": "Bonne réponse : enfiler ajoute en queue et ne change pas la\ntête ; défiler retire la tête, $9$.\n"
            },
            {
              "text": "$3$, le dernier enfilé",
              "correct": false,
              "feedback": "Erreur : retirer le dernier arrivé serait le comportement d'une\npile.\n"
            },
            {
              "text": "$5$, le dernier avant l'ajout",
              "correct": false,
              "feedback": "Erreur : $5$ est en queue, loin de la tête.\n"
            },
            {
              "text": "$2$, l'élément du milieu",
              "correct": false,
              "feedback": "Erreur : $2$ ne sort qu'après $9$.\n"
            }
          ],
          "explanation": "Premier arrivé, premier servi : enfiler et défiler agissent aux\ndeux extrémités opposées."
        },
        {
          "statement": "Une file contient, de la tête vers la queue, $7$, $3$ et\n$0$. On enfile $5$ puis on défile. Quelle valeur est\nretirée ?",
          "options": [
            {
              "text": "$7$, la tête de la file",
              "correct": true,
              "feedback": "Bonne réponse : enfiler ajoute en queue et ne change pas la\ntête ; défiler retire la tête, $7$.\n"
            },
            {
              "text": "$5$, le dernier enfilé",
              "correct": false,
              "feedback": "Erreur : retirer le dernier arrivé serait le comportement d'une\npile.\n"
            },
            {
              "text": "$0$, le dernier avant l'ajout",
              "correct": false,
              "feedback": "Erreur : $0$ est en queue, loin de la tête.\n"
            },
            {
              "text": "$3$, l'élément du milieu",
              "correct": false,
              "feedback": "Erreur : $3$ ne sort qu'après $7$.\n"
            }
          ],
          "explanation": "Premier arrivé, premier servi : enfiler et défiler agissent aux\ndeux extrémités opposées."
        }
      ]
    },
    {
      "id": "q36",
      "difficulty": 3,
      "skills": [
        "files",
        "tableau",
        "circulaire",
        "implementation"
      ],
      "title": "Une file dans un tableau de taille fixe",
      "partie": "files",
      "statement": "Une file est rangée dans un tableau de taille fixe avec deux\nindices, `tete` et `queue`, qui avancent à chaque défilement et à\nchaque enfilement. Que faire quand `queue` atteint la fin du\ntableau alors que des cases du début ont été libérées ?",
      "options": [
        {
          "text": "Reprendre à l'indice $0$, le tableau étant parcouru en cercle",
          "correct": true,
          "feedback": "Bonne réponse : les indices avancent modulo la taille du\ntableau. Les cases libérées en tête sont réutilisées, sans\njamais décaler les éléments.\n"
        },
        {
          "text": "Décaler tous les éléments vers le début du tableau à chaque fois",
          "correct": false,
          "feedback": "Erreur : décaler coûte un temps proportionnel au nombre\nd'éléments, ce que l'on cherche à éviter.\n"
        },
        {
          "text": "Refuser l'enfilement, la file devant être considérée comme pleine",
          "correct": false,
          "feedback": "Erreur : des cases sont libres, la file n'est pas pleine.\n"
        },
        {
          "text": "Remettre `tete` à $0$ sans toucher à `queue`, qui reste en place",
          "correct": false,
          "feedback": "Erreur : `tete` désigne le plus ancien élément, le déplacer\nperdrait des valeurs.\n"
        }
      ],
      "explanation": "Le tableau circulaire donne des enfilements et défilements en temps\nconstant avec une mémoire fixe."
    },
    {
      "id": "q37",
      "difficulty": 2,
      "skills": [
        "piles",
        "inversion",
        "trace"
      ],
      "title": "Transvaser une liste par une pile",
      "partie": "piles",
      "statement": "Que contient `sortie` à la fin ?\n```python\npile = []\nfor x in [1, 2, 3]:\n    pile.append(x)\nsortie = []\nwhile pile:\n    sortie.append(pile.pop())\n```",
      "options": [
        {
          "text": "`[3, 2, 1]`",
          "correct": true,
          "feedback": "Bonne réponse : les éléments sortent de la pile dans l'ordre\ninverse de leur entrée ; `sortie` est la liste renversée.\n"
        },
        {
          "text": "`[1, 2, 3]`",
          "correct": false,
          "feedback": "Erreur : passer par une pile inverse l'ordre.\n"
        },
        {
          "text": "`[]`",
          "correct": false,
          "feedback": "Erreur : `sortie` reçoit chaque élément dépilé.\n"
        },
        {
          "text": "Une erreur, `pop` sur une pile vide",
          "correct": false,
          "feedback": "Erreur : la boucle `while pile` s'arrête dès que la pile est\nvide, avant tout `pop` fautif.\n"
        }
      ],
      "explanation": "Empiler puis dépiler tout renverse une séquence : c'est le\ncomportement dernier entré, premier sorti.",
      "variants": [
        {
          "statement": "Que contient `sortie` à la fin ?\n```python\npile = []\nfor x in [1, 2, 3]:\n    pile.append(x)\nsortie = []\nwhile pile:\n    sortie.append(pile.pop())\n```",
          "options": [
            {
              "text": "`[3, 2, 1]`",
              "correct": true,
              "feedback": "Bonne réponse : les éléments sortent de la pile dans l'ordre\ninverse de leur entrée ; `sortie` est la liste renversée.\n"
            },
            {
              "text": "`[1, 2, 3]`",
              "correct": false,
              "feedback": "Erreur : passer par une pile inverse l'ordre.\n"
            },
            {
              "text": "`[]`",
              "correct": false,
              "feedback": "Erreur : `sortie` reçoit chaque élément dépilé.\n"
            },
            {
              "text": "Une erreur, `pop` sur une pile vide",
              "correct": false,
              "feedback": "Erreur : la boucle `while pile` s'arrête dès que la pile est\nvide, avant tout `pop` fautif.\n"
            }
          ],
          "explanation": "Empiler puis dépiler tout renverse une séquence : c'est le\ncomportement dernier entré, premier sorti."
        },
        {
          "statement": "Que contient `sortie` à la fin ?\n```python\npile = []\nfor x in [4, 9, 2]:\n    pile.append(x)\nsortie = []\nwhile pile:\n    sortie.append(pile.pop())\n```",
          "options": [
            {
              "text": "`[2, 9, 4]`",
              "correct": true,
              "feedback": "Bonne réponse : les éléments sortent de la pile dans l'ordre\ninverse de leur entrée ; `sortie` est la liste renversée.\n"
            },
            {
              "text": "`[4, 9, 2]`",
              "correct": false,
              "feedback": "Erreur : passer par une pile inverse l'ordre.\n"
            },
            {
              "text": "`[]`",
              "correct": false,
              "feedback": "Erreur : `sortie` reçoit chaque élément dépilé.\n"
            },
            {
              "text": "Une erreur, `pop` sur une pile vide",
              "correct": false,
              "feedback": "Erreur : la boucle `while pile` s'arrête dès que la pile est\nvide, avant tout `pop` fautif.\n"
            }
          ],
          "explanation": "Empiler puis dépiler tout renverse une séquence : c'est le\ncomportement dernier entré, premier sorti."
        },
        {
          "statement": "Que contient `sortie` à la fin ?\n```python\npile = []\nfor x in [5, 6, 7, 8]:\n    pile.append(x)\nsortie = []\nwhile pile:\n    sortie.append(pile.pop())\n```",
          "options": [
            {
              "text": "`[8, 7, 6, 5]`",
              "correct": true,
              "feedback": "Bonne réponse : les éléments sortent de la pile dans l'ordre\ninverse de leur entrée ; `sortie` est la liste renversée.\n"
            },
            {
              "text": "`[5, 6, 7, 8]`",
              "correct": false,
              "feedback": "Erreur : passer par une pile inverse l'ordre.\n"
            },
            {
              "text": "`[]`",
              "correct": false,
              "feedback": "Erreur : `sortie` reçoit chaque élément dépilé.\n"
            },
            {
              "text": "Une erreur, `pop` sur une pile vide",
              "correct": false,
              "feedback": "Erreur : la boucle `while pile` s'arrête dès que la pile est\nvide, avant tout `pop` fautif.\n"
            }
          ],
          "explanation": "Empiler puis dépiler tout renverse une séquence : c'est le\ncomportement dernier entré, premier sorti."
        }
      ]
    },
    {
      "id": "q38",
      "difficulty": 2,
      "skills": [
        "piles",
        "parentheses",
        "hauteur"
      ],
      "title": "La hauteur maximale d'une pile de parenthèses",
      "partie": "piles",
      "statement": "On lit `((())())` caractère par caractère, en empilant chaque\nparenthèse ouvrante et en dépilant à chaque fermante. Quelle\nhauteur maximale la pile atteint-elle ?",
      "options": [
        {
          "text": "$3$, au plus profond de l'imbrication",
          "correct": true,
          "feedback": "Bonne réponse : la hauteur de la pile est le nombre de\nparenthèses ouvertes non encore fermées ; son maximum est la\nprofondeur d'imbrication, $3$ ici.\n"
        },
        {
          "text": "$4$, le nombre de parenthèses ouvrantes",
          "correct": false,
          "feedback": "Erreur : les fermantes dépilent en cours de route, la pile ne\ncontient jamais toutes les ouvrantes à la fois.\n"
        },
        {
          "text": "$8$, la longueur de la chaîne",
          "correct": false,
          "feedback": "Erreur : seules les ouvrantes sont empilées, et pas toutes en\nmême temps.\n"
        },
        {
          "text": "$0$, la pile finissant vide",
          "correct": false,
          "feedback": "Erreur : la pile finit vide, mais elle a été remplie entre\ntemps.\n"
        }
      ],
      "explanation": "La hauteur de la pile mesure l'imbrication courante ; son maximum\nest la profondeur de l'expression.",
      "variants": [
        {
          "statement": "On lit `((())())` caractère par caractère, en empilant chaque\nparenthèse ouvrante et en dépilant à chaque fermante. Quelle\nhauteur maximale la pile atteint-elle ?",
          "options": [
            {
              "text": "$3$, au plus profond de l'imbrication",
              "correct": true,
              "feedback": "Bonne réponse : la hauteur de la pile est le nombre de\nparenthèses ouvertes non encore fermées ; son maximum est la\nprofondeur d'imbrication, $3$ ici.\n"
            },
            {
              "text": "$4$, le nombre de parenthèses ouvrantes",
              "correct": false,
              "feedback": "Erreur : les fermantes dépilent en cours de route, la pile ne\ncontient jamais toutes les ouvrantes à la fois.\n"
            },
            {
              "text": "$8$, la longueur de la chaîne",
              "correct": false,
              "feedback": "Erreur : seules les ouvrantes sont empilées, et pas toutes en\nmême temps.\n"
            },
            {
              "text": "$0$, la pile finissant vide",
              "correct": false,
              "feedback": "Erreur : la pile finit vide, mais elle a été remplie entre\ntemps.\n"
            }
          ],
          "explanation": "La hauteur de la pile mesure l'imbrication courante ; son maximum\nest la profondeur de l'expression."
        },
        {
          "statement": "On lit `()()()` caractère par caractère, en empilant chaque\nparenthèse ouvrante et en dépilant à chaque fermante. Quelle\nhauteur maximale la pile atteint-elle ?",
          "options": [
            {
              "text": "$1$, au plus profond de l'imbrication",
              "correct": true,
              "feedback": "Bonne réponse : la hauteur de la pile est le nombre de\nparenthèses ouvertes non encore fermées ; son maximum est la\nprofondeur d'imbrication, $1$ ici.\n"
            },
            {
              "text": "$3$, le nombre de parenthèses ouvrantes",
              "correct": false,
              "feedback": "Erreur : les fermantes dépilent en cours de route, la pile ne\ncontient jamais toutes les ouvrantes à la fois.\n"
            },
            {
              "text": "$6$, la longueur de la chaîne",
              "correct": false,
              "feedback": "Erreur : seules les ouvrantes sont empilées, et pas toutes en\nmême temps.\n"
            },
            {
              "text": "$0$, la pile finissant vide",
              "correct": false,
              "feedback": "Erreur : la pile finit vide, mais elle a été remplie entre\ntemps.\n"
            }
          ],
          "explanation": "La hauteur de la pile mesure l'imbrication courante ; son maximum\nest la profondeur de l'expression."
        },
        {
          "statement": "On lit `(())(())` caractère par caractère, en empilant chaque\nparenthèse ouvrante et en dépilant à chaque fermante. Quelle\nhauteur maximale la pile atteint-elle ?",
          "options": [
            {
              "text": "$2$, au plus profond de l'imbrication",
              "correct": true,
              "feedback": "Bonne réponse : la hauteur de la pile est le nombre de\nparenthèses ouvertes non encore fermées ; son maximum est la\nprofondeur d'imbrication, $2$ ici.\n"
            },
            {
              "text": "$4$, le nombre de parenthèses ouvrantes",
              "correct": false,
              "feedback": "Erreur : les fermantes dépilent en cours de route, la pile ne\ncontient jamais toutes les ouvrantes à la fois.\n"
            },
            {
              "text": "$8$, la longueur de la chaîne",
              "correct": false,
              "feedback": "Erreur : seules les ouvrantes sont empilées, et pas toutes en\nmême temps.\n"
            },
            {
              "text": "$0$, la pile finissant vide",
              "correct": false,
              "feedback": "Erreur : la pile finit vide, mais elle a été remplie entre\ntemps.\n"
            }
          ],
          "explanation": "La hauteur de la pile mesure l'imbrication courante ; son maximum\nest la profondeur de l'expression."
        }
      ]
    },
    {
      "id": "q39",
      "difficulty": 2,
      "skills": [
        "files",
        "parcours-largeur",
        "usage"
      ],
      "title": "Une file pour visiter niveau par niveau",
      "partie": "files",
      "statement": "Pour visiter un arbre ou un graphe niveau par niveau, on range les\nsommets découverts dans une file. Pourquoi une file et non une\npile ?",
      "options": [
        {
          "text": "Les sommets sont traités dans l'ordre de leur découverte",
          "correct": true,
          "feedback": "Bonne réponse : la file rend les sommets dans l'ordre où ils y\nsont entrés ; un niveau entier est traité avant le suivant.\n"
        },
        {
          "text": "Une pile ne peut pas contenir des sommets, seulement des nombres",
          "correct": false,
          "feedback": "Erreur : une pile le peut, mais elle rendrait les derniers\ndécouverts en premier, ce qui donne le parcours en profondeur.\n"
        },
        {
          "text": "Une file est plus rapide qu'une pile pour ajouter des sommets",
          "correct": false,
          "feedback": "Erreur : les deux structures ont des opérations en temps\nconstant.\n"
        },
        {
          "text": "Une file évite de visiter deux fois le même sommet",
          "correct": false,
          "feedback": "Erreur : c'est le marquage des sommets visités qui l'évite,\navec l'une ou l'autre structure.\n"
        }
      ],
      "explanation": "File ou pile, le même algorithme de parcours change d'ordre de\nvisite : largeur avec une file, profondeur avec une pile."
    },
    {
      "id": "q40",
      "difficulty": 3,
      "skills": [
        "files",
        "liste-chainee",
        "implementation",
        "cout"
      ],
      "title": "Une file en liste chaînée à coût constant",
      "partie": "files",
      "statement": "Comment réaliser une file avec une liste chaînée pour que\nenfiler et défiler soient tous deux en temps constant ?",
      "options": [
        {
          "text": "Garder un lien vers la fin, ajouter en queue, retirer en tête",
          "correct": true,
          "feedback": "Bonne réponse : garder un lien vers la tête et un vers le\ndernier maillon permet d'ajouter à la fin et de retirer au\ndébut sans parcourir la liste.\n"
        },
        {
          "text": "Ajouter en tête et retirer en tête, sans lien vers la fin",
          "correct": false,
          "feedback": "Erreur : ce serait une pile, le dernier arrivé sortant le\npremier.\n"
        },
        {
          "text": "Ajouter en tête et retirer en queue, en parcourant la liste",
          "correct": false,
          "feedback": "Erreur : atteindre le dernier maillon demande de parcourir\ntoute la liste, le défilement coûte alors un temps linéaire.\n"
        },
        {
          "text": "Ajouter en queue et retirer en queue, avec un lien vers la fin",
          "correct": false,
          "feedback": "Erreur : ajouter et retirer du même côté donne une pile.\n"
        }
      ],
      "explanation": "Les deux extrémités d'une liste chaînée doivent être accessibles\ndirectement pour obtenir une file en temps constant."
    }
  ]
}