{
  "chapter": {
    "id": "arbres-binaires",
    "level": "terminale",
    "theme": "Structures de données",
    "title": "Arbres binaires",
    "description": "Définition d'un arbre binaire (nœuds, racine, feuilles,\nsous-arbres), terminologie (hauteur, profondeur, taille),\nparcours en profondeur (préfixe, infixe, postfixe) et en\nlargeur, propriétés des arbres équilibrés et complets,\napplications.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Définition d'un arbre binaire",
      "statement": "Qu'est-ce qu'un **arbre binaire** ?",
      "options": [
        {
          "text": "Un arbre dont les nœuds contiennent uniquement deux valeurs",
          "correct": false,
          "feedback": "Erreur : « binaire » fait référence au nombre\nd'enfants, pas au nombre de valeurs par nœud.\n"
        },
        {
          "text": "Une structure linéaire avec deux pointeurs par élément",
          "correct": false,
          "feedback": "Erreur : c'est plutôt une liste doublement\nchaînée. Un arbre n'est pas linéaire mais\nhiérarchique.\n"
        },
        {
          "text": "Un arbre généalogique",
          "correct": false,
          "feedback": "Erreur : un arbre généalogique n'est pas\nforcément binaire (chaque personne a au plus\ndeux parents directs, mais peut avoir plus de\ndeux enfants).\n"
        },
        {
          "text": "Une structure hiérarchique où chaque nœud a au plus deux enfants (gauche et droit)",
          "correct": true,
          "feedback": "Bonne réponse : un arbre binaire est un cas\nparticulier d'arbre où chaque nœud a $0$, $1$ ou\n$2$ enfants. La structure est récursive : chaque\nenfant est lui-même la racine d'un arbre binaire.\n"
        }
      ],
      "explanation": "Vocabulaire : **racine** (sommet), **nœud interne**\n(au moins un enfant), **feuille** (aucun enfant),\n**sous-arbre** (arbre formé par un nœud et ses\ndescendants). Cette définition est fondamentale."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "vocabulaire",
        "racine"
      ],
      "title": "Racine",
      "statement": "Combien de **racines** un arbre binaire possède-t-il ?",
      "options": [
        {
          "text": "Aucune",
          "correct": false,
          "feedback": "Erreur : un arbre non vide a toujours exactement\nune racine. Seul l'arbre vide n'a pas de racine.\n"
        },
        {
          "text": "Exactement une",
          "correct": true,
          "feedback": "Bonne réponse : la racine est le sommet de\nl'arbre, le nœud sans parent. Tout autre nœud\ndescend de la racine via une chaîne unique.\n"
        },
        {
          "text": "Cela dépend de la profondeur",
          "correct": false,
          "feedback": "Erreur : non, peu importe la profondeur, il y a\ntoujours une seule racine.\n"
        },
        {
          "text": "Plusieurs, autant que de feuilles",
          "correct": false,
          "feedback": "Erreur : confusion entre racine et feuille. Un\narbre a une racine et plusieurs feuilles\n(typiquement).\n"
        }
      ],
      "explanation": "L'unicité de la racine définit l'arbre. Si on a\nplusieurs « racines », on parle plutôt d'une\n**forêt** (ensemble disjoint d'arbres)."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "vocabulaire",
        "feuille"
      ],
      "title": "Feuille",
      "statement": "Qu'est-ce qu'une **feuille** dans un arbre binaire ?",
      "options": [
        {
          "text": "La racine de l'arbre",
          "correct": false,
          "feedback": "Erreur : la racine est au sommet, les feuilles\nsont les nœuds **terminaux**.\n"
        },
        {
          "text": "Un nœud à mi-hauteur de l'arbre",
          "correct": false,
          "feedback": "Erreur : aucune définition de feuille basée sur\nla hauteur.\n"
        },
        {
          "text": "Un nœud qui a exactement deux enfants",
          "correct": false,
          "feedback": "Erreur : ça, c'est un nœud **interne complet**.\nUne feuille n'a aucun enfant.\n"
        },
        {
          "text": "Un nœud qui n'a aucun enfant",
          "correct": true,
          "feedback": "Bonne réponse : une feuille est un nœud sans\ndescendant. Elle marque la fin d'une branche.\n"
        }
      ],
      "explanation": "Les feuilles sont importantes car elles marquent les\ncas de base des algorithmes récursifs sur les arbres.\nUn parcours s'arrête typiquement à chaque feuille."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "hauteur"
      ],
      "title": "Hauteur d'un arbre",
      "statement": "Qu'appelle-t-on **hauteur** d'un arbre binaire ?",
      "options": [
        {
          "text": "La distance entre deux feuilles",
          "correct": false,
          "feedback": "Erreur : c'est le diamètre, pas la hauteur.\n"
        },
        {
          "text": "Le nombre de feuilles",
          "correct": false,
          "feedback": "Le nombre de feuilles\nrenseigne sur la\ncapacité finale d'un\narbre, mais ne mesure\npas sa hauteur.\n"
        },
        {
          "text": "La longueur du plus long chemin de la racine à une feuille (compté en nombre d'arêtes)",
          "correct": true,
          "feedback": "Bonne réponse : convention courante. Un arbre\nréduit à une racine a une hauteur de $0$. Un\narbre vide a souvent la convention de\nhauteur $-1$.\n"
        },
        {
          "text": "Le nombre total de nœuds",
          "correct": false,
          "feedback": "Erreur : c'est la **taille**, pas la hauteur.\n"
        }
      ],
      "explanation": "Variante : certaines conventions comptent en\n**nombre de nœuds** plutôt que d'arêtes (la racine\nseule a alors une hauteur de $1$). Préciser la\nconvention dans tout exercice."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "profondeur"
      ],
      "title": "Profondeur d'un nœud",
      "statement": "La **profondeur** d'un nœud dans un arbre est :",
      "options": [
        {
          "text": "Le nombre de feuilles sous ce nœud",
          "correct": false,
          "feedback": "Erreur : c'est encore une autre quantité.\n"
        },
        {
          "text": "La longueur du chemin de la racine jusqu'à ce nœud",
          "correct": true,
          "feedback": "Bonne réponse : la racine a une profondeur de\n$0$, ses enfants $1$, etc. La profondeur est une\npropriété du nœud, alors que la hauteur est une\npropriété de l'arbre.\n"
        },
        {
          "text": "Le nombre de ses descendants",
          "correct": false,
          "feedback": "Erreur : c'est plutôt la **taille du sous-arbre**\nenraciné en ce nœud.\n"
        },
        {
          "text": "La distance jusqu'à sa feuille la plus proche",
          "correct": false,
          "feedback": "Erreur : pas la définition standard de\nprofondeur.\n"
        }
      ],
      "explanation": "La hauteur de l'arbre est égale à la profondeur\nmaximale (parmi tous les nœuds). Les deux notions\nsont liées mais distinctes."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "parcours",
        "prefixe"
      ],
      "title": "Parcours préfixe",
      "statement": "Dans un parcours **préfixe** (ou *pre-order*), dans\nquel ordre visite-t-on les nœuds ?",
      "options": [
        {
          "text": "Nœud, sous-arbre gauche, sous-arbre droit",
          "correct": true,
          "feedback": "Bonne réponse : « préfixe » car on visite le nœud\n**avant** ses enfants (préfixe = devant).\nUtilisation typique : copier un arbre.\n"
        },
        {
          "text": "Sous-arbre droit, nœud, sous-arbre gauche",
          "correct": false,
          "feedback": "Erreur : ordre inhabituel, ce n'est aucun des\nparcours classiques.\n"
        },
        {
          "text": "Sous-arbre gauche, nœud, sous-arbre droit",
          "correct": false,
          "feedback": "Erreur : c'est le parcours **infixe** (in-order).\n"
        },
        {
          "text": "Sous-arbre gauche, sous-arbre droit, nœud",
          "correct": false,
          "feedback": "Erreur : c'est le parcours **postfixe**\n(post-order).\n"
        }
      ],
      "explanation": "Mnémotechnique : **pré**fixe = nœud d'**abord** ;\n**in**fixe = nœud **au milieu** ; **post**fixe = nœud\n**après**. Le « préfixe », « infixe », « postfixe »\nréfère à la position du nœud dans la séquence des\nvisites."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "parcours",
        "infixe"
      ],
      "title": "Parcours infixe",
      "statement": "Dans quel ordre s'effectue un parcours **infixe** d'un\narbre binaire ?",
      "options": [
        {
          "text": "Nœud, sous-arbre gauche, sous-arbre droit",
          "correct": false,
          "feedback": "Erreur : c'est le parcours **préfixe**.\n"
        },
        {
          "text": "Sous-arbre gauche, sous-arbre droit, nœud",
          "correct": false,
          "feedback": "Erreur : c'est le parcours **postfixe**.\n"
        },
        {
          "text": "Sous-arbre gauche, nœud, sous-arbre droit",
          "correct": true,
          "feedback": "Bonne réponse : on visite d'abord tout le\nsous-arbre gauche, puis le nœud lui-même, puis le\nsous-arbre droit. Ce parcours est très utile sur\nun arbre binaire de recherche : il donne les\nvaleurs **dans l'ordre\ncroissant**.\n"
        },
        {
          "text": "Toutes les feuilles d'abord, puis tous les nœuds internes",
          "correct": false,
          "feedback": "Erreur : aucun parcours classique ne sépare\nainsi.\n"
        }
      ],
      "explanation": "Sur un arbre binaire de recherche, le parcours\ninfixe parcourt les valeurs dans l'ordre. C'est la\nraison principale d'utiliser cette structure pour\nmaintenir un ensemble trié dynamiquement."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "parcours",
        "postfixe"
      ],
      "title": "Parcours postfixe",
      "statement": "Le parcours **postfixe** visite :",
      "options": [
        {
          "text": "D'abord le sous-arbre gauche, puis le sous-arbre droit, et enfin le nœud",
          "correct": true,
          "feedback": "Bonne réponse : on visite le nœud **après** ses\nenfants. Très utile pour libérer la mémoire d'un\narbre (ses feuilles d'abord) ou évaluer une\nexpression arithmétique en notation postfixée.\n"
        },
        {
          "text": "Uniquement les feuilles",
          "correct": false,
          "feedback": "Erreur : tous les nœuds sont visités, pas\nseulement les feuilles.\n"
        },
        {
          "text": "Les nœuds dans un ordre aléatoire",
          "correct": false,
          "feedback": "Erreur : un parcours est déterministe.\n"
        },
        {
          "text": "D'abord le nœud, puis les enfants",
          "correct": false,
          "feedback": "Erreur : c'est le parcours préfixe.\n"
        }
      ],
      "explanation": "Application : pour calculer la **taille** d'un arbre\nou sa **hauteur** récursivement, on a besoin des\nrésultats des sous-arbres avant le nœud courant,\nc'est typiquement un parcours postfixe."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "parcours-largeur"
      ],
      "title": "Parcours en largeur",
      "statement": "Le parcours en **largeur** d'un arbre :",
      "options": [
        {
          "text": "Visite la branche gauche en entier avant la droite",
          "correct": false,
          "feedback": "Erreur : c'est plutôt un parcours en\n**profondeur** orienté vers la gauche.\n"
        },
        {
          "text": "Visite tous les nœuds à profondeur $0$, puis $1$, puis $2$, etc.",
          "correct": true,
          "feedback": "Bonne réponse : on parcourt niveau par niveau,\nde la racine vers les feuilles. Implémentation\nstandard : avec une **file**.\n"
        },
        {
          "text": "Visite uniquement les nœuds internes",
          "correct": false,
          "feedback": "Erreur : tous les nœuds sont visités.\n"
        },
        {
          "text": "Évalue une expression arithmétique",
          "correct": false,
          "feedback": "Erreur : ce serait plutôt un parcours postfixe.\n"
        }
      ],
      "explanation": "Mémo : **largeur = file**, **profondeur = pile**\n(ou récursivité). Cette dualité est centrale dans\nles algorithmes sur les graphes, dont les\nparcours en largeur et en profondeur s'appuient\nsur ces deux structures."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "taille"
      ],
      "title": "Taille d'un arbre",
      "statement": "La **taille** d'un arbre binaire est :",
      "options": [
        {
          "text": "Le nombre d'arêtes",
          "correct": false,
          "feedback": "Erreur : un arbre à $n$ nœuds a exactement\n$n - 1$ arêtes, donc ce n'est pas une mesure\nindépendante.\n"
        },
        {
          "text": "La hauteur du plus long chemin",
          "correct": false,
          "feedback": "Erreur : c'est la **hauteur**, pas la taille.\n"
        },
        {
          "text": "Le nombre total de nœuds",
          "correct": true,
          "feedback": "Bonne réponse : la taille compte tous les nœuds\n(internes et feuilles). Pour la calculer, un\nparcours récursif suffit : $T(\\text{vide}) = 0$ ;\n$T(\\text{nœud}) = 1 + T(\\text{gauche}) + T(\\text{droit})$.\n"
        },
        {
          "text": "Le nombre de feuilles",
          "correct": false,
          "feedback": "Erreur : c'est seulement une partie des nœuds.\n"
        }
      ],
      "explanation": "Relation utile : un arbre binaire de hauteur $h$ a\nau plus $2^{h+1} - 1$ nœuds. C'est le nombre maximum,\natteint quand l'arbre est **parfaitement équilibré**."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "implementation",
        "code"
      ],
      "title": "Implémentation Python",
      "statement": "Une implémentation simple d'un nœud d'arbre binaire en\nPython utilise :",
      "options": [
        {
          "text": "Une chaîne de caractères",
          "correct": false,
          "feedback": "Erreur : pas adapté à une structure récursive.\n"
        },
        {
          "text": "Un dictionnaire avec une clé entière",
          "correct": false,
          "feedback": "Erreur : possible mais pas idiomatique. Une\nclasse dédiée est plus claire.\n"
        },
        {
          "text": "Une liste de longueur fixe `[valeur, gauche, droite]`",
          "correct": false,
          "feedback": "Erreur : possible, mais peu lisible. Une classe\ndédiée est plus claire.\n"
        },
        {
          "text": "Une classe avec trois attributs (`valeur`, `gauche`, `droit`), où `gauche` et `droit` sont soit `None` soit un autre nœud",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'approche orientée objet\nstandard. Exemple :\n\n```\nclass Noeud:\n    def __init__(self, v, g=None, d=None):\n        self.valeur = v\n        self.gauche = g\n        self.droit = d\n```\n"
        }
      ],
      "explanation": "Convention : un sous-arbre vide est représenté par\n`None`. Cela simplifie la récursivité, le cas de\nbase est `if noeud is None: return ...`."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "calcul-hauteur"
      ],
      "title": "Calcul récursif de la hauteur",
      "statement": "Quelle fonction Python calcule correctement la hauteur\nd'un arbre binaire (en nombre d'arêtes, $-1$ pour l'arbre\nvide) ?",
      "options": [
        {
          "text": "```\ndef h(n):\n    return n.gauche + n.droit\n```\n",
          "correct": false,
          "feedback": "Erreur : on additionne des nœuds, ça n'a pas de\nsens.\n"
        },
        {
          "text": "```\ndef h(n):\n    if n is None: return 0\n    return 1 + h(n.gauche) + h(n.droit)\n```\n",
          "correct": false,
          "feedback": "Erreur : on **somme** les hauteurs, ce qui est\nincorrect. Il faut prendre le **maximum**.\n"
        },
        {
          "text": "```\ndef h(n):\n    return len(n)\n```\n",
          "correct": false,
          "feedback": "Erreur : `len` ne s'applique pas à un objet\narbitraire.\n"
        },
        {
          "text": "```\ndef h(n):\n    if n is None: return -1\n    return 1 + max(h(n.gauche), h(n.droit))\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on prend la profondeur maximale\ndes deux sous-arbres et on ajoute $1$ (pour\nl'arête vers ce nœud). Convention : arbre vide\nde hauteur $-1$, racine seule de hauteur $0$.\n"
        }
      ],
      "explanation": "Convention alternative : compter en **nombre de\nnœuds** sur le chemin (cas vide → $0$). Adapter\nl'arithmétique en conséquence ($\\max + 1$ devient\n$\\max$ ou différent)."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "arbre-equilibre"
      ],
      "title": "Arbre équilibré",
      "statement": "Qu'est-ce qu'un arbre binaire **équilibré** ?",
      "options": [
        {
          "text": "Un arbre où tous les nœuds ont exactement deux enfants",
          "correct": false,
          "feedback": "Erreur : c'est un arbre **complet** ou **plein**,\npas équilibré.\n"
        },
        {
          "text": "Un arbre symétrique (gauche et droite identiques)",
          "correct": false,
          "feedback": "Erreur : un arbre équilibré n'est pas\nforcément symétrique. La symétrie est une\ncontrainte plus forte.\n"
        },
        {
          "text": "Un arbre où, pour chaque nœud, les hauteurs des sous-arbres gauche et droit diffèrent au plus de $1$",
          "correct": true,
          "feedback": "Bonne réponse : c'est la définition de\nl'équilibre AVL. Un arbre équilibré garantit une\nhauteur $O(\\log n)$, ce qui rend les opérations\nde recherche, insertion, suppression rapides.\n"
        },
        {
          "text": "Un arbre où la racine est au centre",
          "correct": false,
          "feedback": "Erreur : la racine est au sommet, pas au centre.\nLa notion d'« équilibre » est plus précise.\n"
        }
      ],
      "explanation": "Sans équilibrage, un arbre peut dégénérer en\n« peigne » (chaque nœud a un seul enfant) avec\nhauteur $n$ et opérations en $O(n)$. Les arbres\nAVL et rouge-noir sont des structures\nauto-équilibrantes."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "parcours",
        "code"
      ],
      "title": "Code de parcours préfixe",
      "statement": "Quelle fonction Python affiche les valeurs d'un arbre\nbinaire en parcours **préfixe** ?",
      "options": [
        {
          "text": "```\ndef parcours(n):\n    if n is None: return\n    print(n.valeur)\n    parcours(n.gauche)\n    parcours(n.droit)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on imprime le nœud **avant** ses\nenfants : c'est la définition même du parcours\npréfixe.\n"
        },
        {
          "text": "```\ndef parcours(n):\n    print(n.valeur)\n```\n",
          "correct": false,
          "feedback": "Erreur : ne descend pas dans les sous-arbres.\n"
        },
        {
          "text": "```\ndef parcours(n):\n    if n is None: return\n    parcours(n.gauche)\n    parcours(n.droit)\n    print(n.valeur)\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est un parcours **postfixe** (nœud\naprès les enfants).\n"
        },
        {
          "text": "```\ndef parcours(n):\n    if n is None: return\n    parcours(n.gauche)\n    print(n.valeur)\n    parcours(n.droit)\n```\n",
          "correct": false,
          "feedback": "Erreur : c'est le parcours **infixe**.\n"
        }
      ],
      "explanation": "Le seul changement entre les trois parcours en\nprofondeur est la **position** du `print` par rapport\naux deux appels récursifs. Très instructif à\nobserver."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "arbre-complet"
      ],
      "title": "Arbre complet",
      "statement": "Un arbre binaire est dit **complet** quand :",
      "options": [
        {
          "text": "Il contient au moins une feuille",
          "correct": false,
          "feedback": "Erreur : trivialement vrai pour tout arbre non\nvide. Pas une définition utile.\n"
        },
        {
          "text": "Toutes les feuilles sont à la même profondeur",
          "correct": false,
          "feedback": "Erreur : c'est plutôt un arbre **parfait**, plus\nstrict.\n"
        },
        {
          "text": "Tous les niveaux sont entièrement remplis, sauf éventuellement le dernier qui est rempli de gauche à droite",
          "correct": true,
          "feedback": "Bonne réponse : c'est la propriété qui permet\nd'implémenter un arbre complet par un **tableau**\n(sans pointeurs), avec la convention que pour le\nnœud à l'index $i$, ses enfants sont aux indices\n$2i+1$ et $2i+2$.\n"
        },
        {
          "text": "La racine a un enfant gauche et un enfant droit",
          "correct": false,
          "feedback": "Erreur : trop faible. Un arbre complet exige bien\nplus.\n"
        }
      ],
      "explanation": "Cette structure est utilisée dans les **tas binaires**\n(priority queues), où l'arbre complet est représenté\nen tableau pour économiser la mémoire et garantir\nune complexité $O(\\log n)$ des opérations."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "recursion",
        "propriete"
      ],
      "title": "Nombre de feuilles",
      "statement": "Pour un arbre binaire **plein** (chaque nœud interne a\nexactement deux enfants), si $f$ est le nombre de\nfeuilles et $i$ le nombre de nœuds internes, quelle\nrelation a-t-on ?",
      "options": [
        {
          "text": "Aucune relation déterministe",
          "correct": false,
          "feedback": "Erreur : il existe bien une relation pour les\narbres pleins.\n"
        },
        {
          "text": "$f = i + 1$",
          "correct": true,
          "feedback": "Bonne réponse : par récurrence sur la taille.\nCas de base : un seul nœud (racine = feuille),\n$f = 1, i = 0$. Pour un arbre plus grand, chaque\nnouveau nœud interne ajoute une feuille (en\nremplaçant un fils null par un sous-arbre).\n"
        },
        {
          "text": "$f = i$",
          "correct": false,
          "feedback": "Erreur : la relation est différente. On peut\nmontrer $f = i + 1$ par récurrence.\n"
        },
        {
          "text": "$f = 2 \\cdot i$",
          "correct": false,
          "feedback": "Erreur : ne se vérifie pas.\n"
        }
      ],
      "explanation": "Cette relation s'utilise pour des comptages\nd'arbres dans les exercices. Elle vient d'une\nsymétrie structurelle : chaque nœud interne « remplace\n» une feuille par lui-même plus deux nouvelles\nfeuilles (delta de $+1$ feuille, $+1$ interne)."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "parcours-largeur-implementation"
      ],
      "title": "Implémenter un parcours en largeur",
      "statement": "Pour implémenter un parcours en largeur d'un arbre\nbinaire en Python, on utilise généralement :",
      "options": [
        {
          "text": "Une file (`collections.deque` avec `append` et `popleft`)",
          "correct": true,
          "feedback": "Bonne réponse : on enfile la racine, puis tant\nque la file n'est pas vide on défile le nœud\ncourant et on enfile ses deux enfants. Le file\nassure le traitement niveau par niveau.\n"
        },
        {
          "text": "Une variable simple",
          "correct": false,
          "feedback": "Erreur : insuffisant pour stocker plusieurs\nnœuds en attente.\n"
        },
        {
          "text": "Une pile (`list` avec `append` et `pop`)",
          "correct": false,
          "feedback": "Erreur : la pile correspond au parcours en\n**profondeur**. Pour la largeur, c'est différent.\n"
        },
        {
          "text": "Un dictionnaire trié",
          "correct": false,
          "feedback": "Erreur : pas adapté.\n"
        }
      ],
      "explanation": "Code typique :\n\n```\nfrom collections import deque\ndef bfs(racine):\n    if racine is None: return\n    f = deque([racine])\n    while f:\n        n = f.popleft()\n        print(n.valeur)\n        if n.gauche: f.append(n.gauche)\n        if n.droit:  f.append(n.droit)\n```"
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "taille-borne"
      ],
      "title": "Borne du nombre de nœuds",
      "statement": "Quel est le nombre **maximum** de nœuds d'un arbre\nbinaire de hauteur $h$ ?",
      "options": [
        {
          "text": "$h + 1$",
          "correct": false,
          "feedback": "Erreur : c'est le nombre **minimum** (arbre en\npeigne).\n"
        },
        {
          "text": "$h^2$",
          "correct": false,
          "feedback": "Erreur : la croissance n'est pas polynomiale\nmais exponentielle (en hauteur).\n"
        },
        {
          "text": "$h$",
          "correct": false,
          "feedback": "Erreur : un arbre dégénéré (peigne) a déjà $h+1$\nnœuds. Et un arbre équilibré en a beaucoup plus.\n"
        },
        {
          "text": "$2^{h+1} - 1$",
          "correct": true,
          "feedback": "Bonne réponse : un arbre **parfait** de hauteur\n$h$ a $1 + 2 + 4 + \\ldots + 2^h = 2^{h+1} - 1$\nnœuds. C'est le maximum atteignable.\n"
        }
      ],
      "explanation": "Conséquence : pour avoir $n$ nœuds dans un arbre\néquilibré, la hauteur est de l'ordre de $\\log_2 n$.\nC'est ce qui rend les arbres de recherche si\nefficaces : opérations en $O(\\log n)$."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "parcours-evaluation"
      ],
      "title": "Évaluation d'expression arithmétique",
      "statement": "Pour évaluer une expression arithmétique représentée\npar un arbre (les feuilles sont des nombres, les nœuds\ninternes des opérateurs), on utilise un parcours :",
      "options": [
        {
          "text": "Postfixe",
          "correct": true,
          "feedback": "Bonne réponse : on évalue d'abord les sous-arbres\ngauche et droit (récursivement), puis on\napplique l'opérateur du nœud courant. C'est\nexactement la sémantique de l'arithmétique\nmathématique.\n"
        },
        {
          "text": "Infixe",
          "correct": false,
          "feedback": "Erreur : l'infixe affiche dans l'ordre lisible\nhumain, mais on a besoin d'évaluer les\nsous-arbres avant le nœud, ce qui est postfixe.\n"
        },
        {
          "text": "Aléatoire",
          "correct": false,
          "feedback": "Erreur : l'évaluation doit suivre un ordre\nprécis.\n"
        },
        {
          "text": "Préfixe",
          "correct": false,
          "feedback": "Erreur : un parcours préfixe affiche d'abord\nl'opérateur, donc on n'a pas encore les valeurs\nau moment du calcul.\n"
        }
      ],
      "explanation": "L'arbre d'expression de `(2 + 3) * 4` a `*` à la\nracine, avec sous-arbre gauche `+` (de fils $2$ et\n$3$) et sous-arbre droit feuille $4$. Évaluation\npostfixe : $2 + 3 = 5$, puis $5 \\times 4 = 20$."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "recursion-symetrie"
      ],
      "title": "Arbre miroir",
      "statement": "Pour calculer le **miroir** d'un arbre binaire\n(échanger gauche et droite à chaque nœud), une approche\nrécursive est :",
      "options": [
        {
          "text": "Échanger uniquement la racine avec la dernière feuille",
          "correct": false,
          "feedback": "Erreur : pas suffisant. Il faut le faire à\n**chaque** nœud, récursivement.\n"
        },
        {
          "text": "Pour chaque nœud, échanger ses deux sous-arbres puis appliquer récursivement le miroir aux sous-arbres",
          "correct": true,
          "feedback": "Bonne réponse : on inverse à tous les niveaux.\nEn Python :\n\n```\ndef miroir(n):\n    if n is None: return\n    n.gauche, n.droit = n.droit, n.gauche\n    miroir(n.gauche)\n    miroir(n.droit)\n```\n"
        },
        {
          "text": "Ne rien faire",
          "correct": false,
          "feedback": "Erreur : ne donne pas le miroir.\n"
        },
        {
          "text": "Trier les éléments par ordre décroissant",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le tri.\n"
        }
      ],
      "explanation": "C'est un exemple typique d'algorithme récursif\nsimple sur arbre. La récursion est naturelle car la\nstructure est elle-même récursive."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "analyse-complexite"
      ],
      "title": "Complexité de la recherche",
      "statement": "Dans un arbre binaire **non ordonné** de $n$ nœuds, la\nrecherche d'une valeur a une complexité au pire de :",
      "options": [
        {
          "text": "$O(\\log n)$",
          "correct": false,
          "feedback": "Erreur : ce serait dans un arbre **ordonné**\néquilibré (arbre binaire de recherche\néquilibré). Sans ordre, on doit\nparcourir.\n"
        },
        {
          "text": "$O(n)$",
          "correct": true,
          "feedback": "Bonne réponse : sans propriété d'ordre, on doit\npotentiellement visiter tous les nœuds. Aucun\ngain par rapport à une liste.\n"
        },
        {
          "text": "$O(n^2)$",
          "correct": false,
          "feedback": "Erreur : excessif. Une simple parcours visite\nchaque nœud une fois, donc $O(n)$.\n"
        },
        {
          "text": "$O(\\sqrt{n})$",
          "correct": false,
          "feedback": "Erreur : aucune raison particulière.\n"
        }
      ],
      "explanation": "C'est précisément la motivation des **arbres\nbinaires de recherche**, qui en ajoutant la\npropriété d'ordre, ramènent la recherche à\n$O(\\log n)$ en moyenne (et $O(\\log n)$ garanti si\néquilibré)."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "reconstruction"
      ],
      "title": "Reconstruction d'arbre",
      "statement": "Connaissant à la fois le parcours **préfixe** et le\nparcours **infixe** d'un arbre binaire (sans valeurs\ndupliquées), peut-on reconstruire l'arbre de manière\nunique ?",
      "options": [
        {
          "text": "Seulement si l'arbre est équilibré",
          "correct": false,
          "feedback": "Erreur : aucune contrainte d'équilibrage\nrequise.\n"
        },
        {
          "text": "Non, il y a toujours plusieurs arbres possibles",
          "correct": false,
          "feedback": "Erreur : ces deux parcours combinés permettent\nen réalité une reconstruction unique.\n"
        },
        {
          "text": "Oui, c'est un théorème classique",
          "correct": true,
          "feedback": "Bonne réponse : le préfixe donne la racine en\npremier, l'infixe sépare les éléments à gauche\net à droite de la racine. On reconstruit\nrécursivement. Le préfixe seul ne suffit pas\n(deux arbres peuvent avoir le même préfixe).\n"
        },
        {
          "text": "Cela dépend de la profondeur",
          "correct": false,
          "feedback": "Erreur : non, le théorème vaut pour toute\nprofondeur.\n"
        }
      ],
      "explanation": "Question classique d'examens. Le couple\n(préfixe, infixe) ou (postfixe, infixe) suffit. Le\ncouple (préfixe, postfixe) ne suffit pas en général."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "diametre"
      ],
      "title": "Diamètre d'un arbre",
      "statement": "Le **diamètre** d'un arbre binaire est :",
      "options": [
        {
          "text": "La hauteur de l'arbre",
          "correct": false,
          "feedback": "Erreur : la hauteur part de la racine. Le\ndiamètre peut ne pas la passer.\n"
        },
        {
          "text": "Le nombre total de nœuds",
          "correct": false,
          "feedback": "Erreur : c'est la **taille**.\n"
        },
        {
          "text": "Le nombre total de feuilles",
          "correct": false,
          "feedback": "Le nombre de feuilles\nn'est pas une distance\ndans l'arbre. Le diamètre\nmesure la longueur d'un\nchemin, pas un comptage\nde nœuds.\n"
        },
        {
          "text": "La longueur du plus long chemin entre deux nœuds (pas forcément passant par la racine)",
          "correct": true,
          "feedback": "Bonne réponse : c'est une notion plus subtile\nque la hauteur. Le diamètre peut traverser\nl'arbre via la racine ou rester dans un\nsous-arbre. Calcul en $O(n)$ par parcours\nrécursif intelligent.\n"
        }
      ],
      "explanation": "Calcul classique : pour chaque nœud, calculer la\nprofondeur de ses sous-arbres gauche et droit. Le\ndiamètre passant par ce nœud est leur somme + $2$\n(les deux arêtes vers les sous-arbres). Le diamètre\nglobal est le maximum sur tous les nœuds."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "arbre-tableau"
      ],
      "title": "Représentation par tableau",
      "statement": "Quand peut-on représenter efficacement un arbre binaire\ndans un **tableau** (sans pointeurs) ?",
      "options": [
        {
          "text": "Quand l'arbre est complet (tous les niveaux remplis sauf éventuellement le dernier rempli de gauche à droite)",
          "correct": true,
          "feedback": "Bonne réponse : la représentation par tableau est\ncompacte uniquement si l'arbre est complet. À la\nracine on stocke en index $0$ (ou $1$), pour le\nnœud à l'index $i$ les enfants sont en $2i+1$ et\n$2i+2$, et le parent en $(i-1)/2$.\n"
        },
        {
          "text": "Quand l'arbre a moins de $10$ nœuds",
          "correct": false,
          "feedback": "Erreur : pas de seuil de taille, mais une\ncondition structurelle.\n"
        },
        {
          "text": "Toujours, c'est universel",
          "correct": false,
          "feedback": "Erreur : pour un arbre quelconque (avec des\n« trous »), on gaspillerait beaucoup de mémoire.\n"
        },
        {
          "text": "Jamais, c'est inefficace",
          "correct": false,
          "feedback": "Erreur : c'est au contraire très efficace pour\nles arbres complets (tas binaires).\n"
        }
      ],
      "explanation": "Cette représentation est utilisée dans les **tas\nbinaires** (heaps), qui sont des arbres complets\nordonnés par priorité. Avantages : pas de\npointeurs, données contiguës, cache CPU efficace."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Cas d'usage des arbres binaires",
      "statement": "Lequel des cas suivants utilise typiquement un arbre\nbinaire ?",
      "options": [
        {
          "text": "Stocker une liste de courses ordinaire",
          "correct": false,
          "feedback": "Erreur : une liste plate suffit. Pas besoin\nd'arbre.\n"
        },
        {
          "text": "Échanger deux variables",
          "correct": false,
          "feedback": "L'échange de deux variables\nse fait avec une variable\ntemporaire (ou une affectation\nmultiple en Python). Cela n'a\naucun lien avec une structure\narborescente.\n"
        },
        {
          "text": "Compter le nombre de mots dans un texte",
          "correct": false,
          "feedback": "Pour compter des mots,\nun simple compteur entier\nsuffit. Un arbre serait\ntotalement disproportionné\npour cette tâche.\n"
        },
        {
          "text": "Représenter un arbre de décision, une expression arithmétique, ou un index ordonné dynamique (arbre binaire de recherche)",
          "correct": true,
          "feedback": "Bonne réponse : ces cas exploitent la nature\nhiérarchique et/ou récursive de l'arbre.\nParticulièrement utile quand on veut maintenir\nun ensemble trié avec insertion/suppression\ndynamiques.\n"
        }
      ],
      "explanation": "Autres usages : analyse syntaxique (arbres de\nsyntaxe abstraite), systèmes de fichiers (chaque\ndossier est un nœud), arbres de jeux (échecs, go),\nstructures de regroupement hiérarchique. Les arbres\nsont omniprésents en informatique."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "taille-recursive"
      ],
      "title": "Calcul récursif de la taille",
      "statement": "Quelle fonction Python calcule correctement la\n**taille** (nombre total de nœuds) d'un arbre\nbinaire ?",
      "options": [
        {
          "text": "```\ndef taille(n):\n    if n is None:\n        return 0\n    return 1 + max(taille(n.gauche), taille(n.droit))\n```\n",
          "correct": false,
          "feedback": "Erreur : on prend ici le **maximum** au lieu de\nla **somme**. Le résultat correspond à la\nhauteur (en nombre de nœuds), pas à la taille.\n"
        },
        {
          "text": "```\ndef taille(n):\n    if n is None:\n        return 0\n    return 1 + taille(n.gauche) + taille(n.droit)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on additionne $1$ (pour le nœud\ncourant) à la somme des tailles des deux\nsous-arbres. Le cas de base est l'arbre vide,\nde taille $0$. Récursion classique sur la\nstructure d'arbre, en $O(n)$.\n"
        },
        {
          "text": "```\ndef taille(n):\n    if n is None:\n        return 1\n    return taille(n.gauche) + taille(n.droit)\n```\n",
          "correct": false,
          "feedback": "Erreur : on compte les **feuilles vides**\n(`None`) au lieu des nœuds. La taille d'un\narbre à un seul nœud serait $2$ au lieu de\n$1$, ce qui est faux.\n"
        },
        {
          "text": "```\ndef taille(n):\n    return 1\n```\n",
          "correct": false,
          "feedback": "Erreur : on renvoie toujours $1$, sans\nparcourir l'arbre. Pour tout arbre, la\nfonction renverrait $1$, ce qui n'est pas la\ntaille (sauf cas trivial d'un seul nœud).\n"
        }
      ],
      "explanation": "Cette structure de récursion (cas de base\n`None` → $0$, cas récursif $1 + $ somme des\nsous-arbres) est l'archétype de l'algorithme\nrécursif sur arbre binaire. Elle s'adapte à\nd'autres mesures (somme des valeurs, produit,\nmaximum, etc.) en changeant la combinaison."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "compter-feuilles"
      ],
      "title": "Compter les feuilles",
      "statement": "Quelle fonction Python compte correctement le\nnombre de **feuilles** d'un arbre binaire ?",
      "options": [
        {
          "text": "```\ndef feuilles(n):\n    if n is None:\n        return 1\n    return feuilles(n.gauche) + feuilles(n.droit)\n```\n",
          "correct": false,
          "feedback": "Erreur : on compte les positions vides\n(`None`), ce qui donne le double du nombre de\nfeuilles plus quelque chose pour les nœuds à\nun seul enfant. Pas la bonne mesure.\n"
        },
        {
          "text": "```\ndef feuilles(n):\n    if n is None:\n        return 0\n    return feuilles(n.gauche) + feuilles(n.droit)\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque le cas où le nœud est une\nfeuille. Cette fonction renverrait toujours\n$0$ car on ne compte jamais. Récursion\nstérile.\n"
        },
        {
          "text": "```\ndef feuilles(n):\n    if n is None:\n        return 0\n    return 1 + feuilles(n.gauche) + feuilles(n.droit)\n```\n",
          "correct": false,
          "feedback": "Erreur : on ajoute $1$ pour **chaque** nœud,\nce qui compte la **taille totale** de l'arbre,\npas seulement les feuilles. Il faut distinguer\nles feuilles (sans enfant) des nœuds internes\n(au moins un enfant).\n"
        },
        {
          "text": "```\ndef feuilles(n):\n    if n is None:\n        return 0\n    if n.gauche is None and n.droit is None:\n        return 1\n    return feuilles(n.gauche) + feuilles(n.droit)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : trois cas. (1) Arbre vide :\naucune feuille. (2) Nœud sans enfant : une\nfeuille. (3) Nœud interne : on additionne les\nfeuilles des deux sous-arbres. C'est le\npatron classique de comptage récursif.\n"
        }
      ],
      "explanation": "Cas particulier dans le code : on doit reconnaître\nexplicitement les feuilles avec\n`n.gauche is None and n.droit is None`. C'est une\ndifférence importante par rapport à la fonction\n`taille`, qui se contente d'additionner sans\ndistinguer les feuilles."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "profondeur-recherche"
      ],
      "title": "Profondeur d'un nœud",
      "statement": "Comment calculer la **profondeur** d'un nœud\ncontenant la valeur `cible` dans un arbre binaire\n(ou `-1` si la valeur n'est pas présente) ?",
      "options": [
        {
          "text": "```\ndef profondeur(n, cible):\n    return n.valeur - cible\n```\n",
          "correct": false,
          "feedback": "Erreur grossière : on calcule une différence\nde valeurs, ce qui n'a aucun rapport avec une\nprofondeur dans un arbre.\n"
        },
        {
          "text": "```\ndef profondeur(n, cible):\n    if n.valeur == cible:\n        return 0\n    return 1 + profondeur(n.gauche, cible)\n```\n",
          "correct": false,
          "feedback": "Erreur : on ne descend que dans le sous-arbre\n**gauche**, et on plante si on atteint un\nnœud `None` sans avoir trouvé la cible.\nL'arbre ne se réduit pas à sa branche gauche.\n"
        },
        {
          "text": "```\ndef profondeur(n, cible):\n    if n is None:\n        return 0\n    if n.valeur == cible:\n        return 1\n    return profondeur(n.gauche, cible) + profondeur(n.droit, cible)\n```\n",
          "correct": false,
          "feedback": "Erreur : on **additionne** les résultats des\ndeux sous-arbres, ce qui n'a pas de sens pour\nune profondeur. De plus, on ne propage pas\nle compteur de profondeur, donc on ne peut\npas savoir à quelle distance se trouve la\ncible.\n"
        },
        {
          "text": "```\ndef profondeur(n, cible, p=0):\n    if n is None:\n        return -1\n    if n.valeur == cible:\n        return p\n    g = profondeur(n.gauche, cible, p + 1)\n    if g != -1:\n        return g\n    return profondeur(n.droit, cible, p + 1)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on transmet la profondeur\ncourante via le paramètre `p`, qu'on incrémente\nà chaque descente récursive. On retourne `p`\ndès que la cible est trouvée. Si la recherche\ndans le sous-arbre gauche échoue (`-1`), on\nessaie le droit. Sinon on remonte `-1` (cible\nabsente).\n"
        }
      ],
      "explanation": "Variante intéressante : utiliser un parcours en\nlargeur avec une file qui contient les couples\n`(nœud, profondeur)`. Permet d'éviter la\nrécursion. Complexité dans les deux cas :\n`O(n)` au pire, où `n` est le nombre de nœuds."
    }
  ]
}