{
  "chapter": {
    "id": "arbres-binaires-de-recherche",
    "level": "terminale",
    "theme": "Structures de données",
    "title": "Arbres binaires de recherche",
    "description": "Propriété d'ordre dans un arbre binaire de\nrecherche, opérations de recherche, d'insertion et\nde suppression, complexité dans le meilleur et\ndans le pire cas, équilibrage, applications aux\nensembles ordonnés dynamiques et aux dictionnaires\ntriés.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Propriété fondamentale",
      "statement": "Quelle est la **propriété fondamentale** d'un\narbre binaire de recherche ?",
      "options": [
        {
          "text": "Toutes les feuilles se trouvent au même niveau",
          "correct": false,
          "feedback": "Cette description correspond à un arbre\nparfait, et non à un arbre binaire de\nrecherche. Un arbre binaire de recherche\nse définit par une propriété d'ordre, pas\npar la position de ses feuilles.\n"
        },
        {
          "text": "Chaque nœud possède exactement deux enfants",
          "correct": false,
          "feedback": "Cette description correspond à un arbre\nplein, qui n'a aucun rapport direct avec\nl'ordre des valeurs.\n"
        },
        {
          "text": "Pour chaque nœud, toutes les valeurs du sous-arbre gauche sont strictement inférieures à la valeur du nœud, et toutes celles du sous-arbre droit lui sont strictement supérieures",
          "correct": true,
          "feedback": "C'est la propriété d'ordre qui rend la\nrecherche efficace en $O(\\log n)$ dans un\narbre équilibré. Cette propriété doit être\nvérifiée pour **chaque** nœud de l'arbre, et\nnon uniquement pour la racine.\n"
        },
        {
          "text": "La racine contient la plus grande valeur de l'arbre",
          "correct": false,
          "feedback": "Cette propriété correspond à la définition\nd'un tas (de type maximum), pas à celle\nd'un arbre binaire de recherche. Dans un\narbre binaire de recherche, la racine peut\nêtre n'importe quelle valeur.\n"
        }
      ],
      "explanation": "Les arbres binaires de recherche permettent de\nmaintenir un ensemble ordonné dynamique, en\noffrant des opérations de recherche,\nd'insertion et de suppression efficaces lorsque\nl'arbre reste équilibré."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "recherche",
        "complexite"
      ],
      "title": "Complexité de la recherche",
      "statement": "Dans un arbre binaire de recherche **équilibré**\ncontenant $n$ éléments, quelle est la complexité\nde la recherche d'une valeur ?",
      "options": [
        {
          "text": "$O(n^2)$",
          "correct": false,
          "feedback": "Aucune raison d'avoir une complexité\nquadratique pour une simple recherche dans\nun arbre.\n"
        },
        {
          "text": "$O(1)$",
          "correct": false,
          "feedback": "On ne peut pas accéder à un élément\nquelconque en temps constant dans un arbre\nbinaire de recherche. La complexité dépend\nau moins de la hauteur de l'arbre.\n"
        },
        {
          "text": "$O(n)$",
          "correct": false,
          "feedback": "Cette complexité correspond au pire cas\nd'un arbre dégénéré en peigne. Pour un\narbre équilibré, on fait nettement mieux.\n"
        },
        {
          "text": "$O(\\log n)$",
          "correct": true,
          "feedback": "À chaque comparaison, on élimine la moitié\ndes nœuds (on descend dans le sous-arbre\ngauche ou dans le sous-arbre droit). Le\nnombre d'étapes est donc proportionnel à\nla hauteur de l'arbre, qui vaut\n$O(\\log n)$ lorsque l'arbre est équilibré.\n"
        }
      ],
      "explanation": "Cette efficacité explique pourquoi les arbres\nbinaires de recherche sont utilisés pour\nimplémenter des ensembles et dictionnaires\ntriés. Cette complexité dépend néanmoins\ncrucialement du fait que l'arbre reste\néquilibré."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "degenerescence"
      ],
      "title": "Cas pire",
      "statement": "Quelle est la complexité de la recherche dans\nun arbre binaire de recherche dans le **pire\ncas**, sans contrainte d'équilibrage ?",
      "options": [
        {
          "text": "$O(\\sqrt{n})$",
          "correct": false,
          "feedback": "Aucune raison particulière n'amène à une\ncomplexité de cette forme dans un arbre\nbinaire de recherche.\n"
        },
        {
          "text": "$O(\\log n)$ dans tous les cas",
          "correct": false,
          "feedback": "Sans équilibrage, l'arbre peut dégénérer.\nLa complexité $O(\\log n)$ n'est garantie\nque si l'arbre reste équilibré.\n"
        },
        {
          "text": "$O(n^2)$",
          "correct": false,
          "feedback": "Même dans le pire cas, la recherche reste\nen $O(n)$. La hauteur de l'arbre, qui peut\natteindre $n$, est en cause, mais la\ncomplexité ne devient pas quadratique.\n"
        },
        {
          "text": "$O(n)$, lorsque l'arbre a dégénéré en peigne et que chaque nœud n'a qu'un enfant",
          "correct": true,
          "feedback": "Si l'on insère par exemple les valeurs\n$1, 2, 3, \\ldots, n$ dans l'ordre, l'arbre\ndevient une liste chaînée par la droite.\nLa recherche y est alors en $O(n)$, comme\ndans un parcours linéaire.\n"
        }
      ],
      "explanation": "Ce phénomène de dégénérescence motive\nl'utilisation d'arbres binaires de recherche\nauto-équilibrés (de type AVL ou rouge-noir),\nqui maintiennent une hauteur en $O(\\log n)$\ngrâce à des opérations de rééquilibrage à\nchaque insertion ou suppression."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "parcours-infixe"
      ],
      "title": "Parcours infixe",
      "statement": "Que donne un parcours **infixe** sur un arbre\nbinaire de recherche ?",
      "options": [
        {
          "text": "Les valeurs dans un ordre aléatoire",
          "correct": false,
          "feedback": "Un parcours d'arbre est strictement\ndéterministe : il ne fait jamais appel au\nhasard.\n"
        },
        {
          "text": "Les valeurs dans l'ordre croissant",
          "correct": true,
          "feedback": "C'est précisément la propriété remarquable\ndes arbres binaires de recherche. Le\nparcours infixe (sous-arbre gauche, racine,\nsous-arbre droit) visite naturellement les\nvaleurs dans l'ordre croissant.\n"
        },
        {
          "text": "Les valeurs dans l'ordre décroissant",
          "correct": false,
          "feedback": "Pour obtenir l'ordre décroissant, il\nfaudrait inverser le parcours infixe en\nvisitant d'abord le sous-arbre droit, puis\nla racine, puis le sous-arbre gauche.\n"
        },
        {
          "text": "Aucune valeur particulière n'est visitée",
          "correct": false,
          "feedback": "Un parcours visite chaque nœud exactement\nune fois. Toutes les valeurs sont donc\nparcourues.\n"
        }
      ],
      "explanation": "Cette propriété permet de transformer un\narbre binaire de recherche en un tableau\ntrié en $O(n)$. C'est aussi pour cette raison\nque cette structure est naturelle pour\nmaintenir un ensemble ordonné."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "insertion"
      ],
      "title": "Insertion",
      "statement": "Pour insérer une valeur $v$ dans un arbre\nbinaire de recherche, on procède de la manière\nsuivante :",
      "options": [
        {
          "text": "On descend dans l'arbre en comparant la valeur avec chaque nœud rencontré, puis on l'insère comme nouvelle feuille à la place vide trouvée",
          "correct": true,
          "feedback": "Si la valeur $v$ est plus petite que celle\ndu nœud courant, on descend dans le\nsous-arbre gauche ; sinon dans le\nsous-arbre droit. On répète jusqu'à\nrencontrer une place vide, où l'on crée\nune nouvelle feuille.\n"
        },
        {
          "text": "On trie les valeurs, puis on la place",
          "correct": false,
          "feedback": "L'insertion est une opération locale qui\npréserve l'invariant d'ordre. Aucun tri\nglobal n'est nécessaire.\n"
        },
        {
          "text": "On la place toujours à la racine",
          "correct": false,
          "feedback": "Insérer la nouvelle valeur à la racine\nbriserait la propriété d'ordre. L'insertion\ndoit au contraire respecter la structure\nexistante.\n"
        },
        {
          "text": "On la place dans la première feuille rencontrée, sans comparaison",
          "correct": false,
          "feedback": "Sans comparaison avec la valeur des nœuds,\nla propriété d'ordre serait violée et\nl'arbre cesserait d'être un arbre binaire\nde recherche.\n"
        }
      ],
      "explanation": "L'insertion s'effectue en $O(h)$, où $h$ est\nla hauteur de l'arbre. Lorsque l'arbre est\néquilibré, on a $h = O(\\log n)$. Sinon, dans\nle pire cas, l'insertion peut atteindre\n$O(n)$."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "suppression"
      ],
      "title": "Suppression",
      "statement": "La **suppression** d'un nœud dans un arbre\nbinaire de recherche est plus délicate que\nl'insertion. Pourquoi ?",
      "options": [
        {
          "text": "Parce qu'il faut détruire l'arbre puis le reconstruire entièrement",
          "correct": false,
          "feedback": "Cette opération serait inutilement\ncoûteuse. La suppression peut se faire de\nmanière purement locale.\n"
        },
        {
          "text": "Parce que selon que le nœud à supprimer a $0$, $1$ ou $2$ enfants, le traitement est différent, et le cas à deux enfants demande de remplacer le nœud par son successeur (ou son prédécesseur)",
          "correct": true,
          "feedback": "On distingue trois cas. Si le nœud est une\nfeuille, on le supprime simplement. S'il a\nun seul enfant, on remplace le nœud par\ncet enfant. S'il a deux enfants, on le\nremplace par le **successeur infixe**\n(plus petit du sous-arbre droit) ou par le\n**prédécesseur infixe** (plus grand du\nsous-arbre gauche), puis on supprime ce\nsuccesseur (qui a au plus un enfant).\n"
        },
        {
          "text": "Parce qu'il faut entièrement re-trier l'arbre après chaque suppression",
          "correct": false,
          "feedback": "Aucun tri global n'est nécessaire.\nL'opération reste locale, mais doit\ndistinguer plusieurs cas selon la\nstructure du nœud supprimé.\n"
        },
        {
          "text": "Parce qu'il est impossible de supprimer un nœud dans un arbre binaire de recherche",
          "correct": false,
          "feedback": "La suppression est tout à fait possible.\nElle est simplement plus complexe que\nl'insertion, en raison de la distinction\nde cas.\n"
        }
      ],
      "explanation": "Le cas le plus délicat est celui d'un nœud à\ndeux enfants. Le successeur infixe se trouve\nfacilement : il s'agit du nœud le plus à\ngauche dans le sous-arbre droit du nœud à\nsupprimer."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "comparaison-tableau"
      ],
      "title": "Comparaison avec un tableau trié",
      "statement": "Quel est l'**avantage** d'un arbre binaire de\nrecherche équilibré par rapport à un tableau\ntrié ?",
      "options": [
        {
          "text": "La recherche y est plus rapide",
          "correct": false,
          "feedback": "La recherche dichotomique dans un tableau\ntrié est elle aussi en $O(\\log n)$. Les\ndeux structures sont équivalentes pour la\nrecherche.\n"
        },
        {
          "text": "Aucune différence n'existe entre les deux structures",
          "correct": false,
          "feedback": "Les deux structures se distinguent\nprécisément par leur comportement face\naux opérations dynamiques d'ajout et de\nsuppression.\n"
        },
        {
          "text": "L'insertion et la suppression y sont en $O(\\log n)$, alors qu'elles coûtent $O(n)$ dans un tableau trié, à cause du décalage des éléments",
          "correct": true,
          "feedback": "C'est le principal atout d'un arbre\nbinaire de recherche équilibré. On peut y\nmaintenir un ensemble trié dynamique avec\ntoutes les opérations en $O(\\log n)$,\nalors qu'une insertion ou une suppression\nau milieu d'un tableau trié coûte $O(n)$.\n"
        },
        {
          "text": "Il consomme moins de mémoire qu'un tableau",
          "correct": false,
          "feedback": "Au contraire, un arbre binaire de\nrecherche consomme en général plus de\nmémoire qu'un tableau, en raison des\nréférences entre nœuds.\n"
        }
      ],
      "explanation": "Le compromis est clair. Si l'on a beaucoup de\nrecherches mais peu d'insertions, le tableau\ntrié peut suffire. Pour un mélange dynamique\nd'opérations, l'arbre binaire de recherche\néquilibré est mieux adapté."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "exemple-construction"
      ],
      "title": "Construire l'arbre",
      "statement": "On insère successivement les valeurs $5$, $3$,\n$7$, $1$, $4$ dans un arbre binaire de\nrecherche initialement vide. Que devient la\nracine ?",
      "options": [
        {
          "text": "$5$",
          "correct": true,
          "feedback": "La première valeur insérée devient la\nracine. Toutes les insertions suivantes\ndeviennent des descendants de cette\nracine.\n"
        },
        {
          "text": "$7$",
          "correct": false,
          "feedback": "La valeur $7$ est plus grande que $5$,\ndonc elle est insérée à droite de la\nracine, mais elle ne devient pas la\nracine elle-même.\n"
        },
        {
          "text": "$1$",
          "correct": false,
          "feedback": "La valeur $1$ est insérée en dernier et\nfinit en feuille à gauche. La racine est\ntoujours la **première** valeur insérée.\n"
        },
        {
          "text": "$4$",
          "correct": false,
          "feedback": "La valeur $4$ est plus petite que $5$\nmais plus grande que $3$. Elle finit\ncomme enfant droit de $3$, et n'occupe\npas la racine.\n"
        }
      ],
      "explanation": "L'ordre d'insertion détermine la forme de\nl'arbre. Insérer dans l'ordre $1, 3, 4, 5, 7$\ndonnerait un peigne à droite (cas dégénéré).\nC'est précisément pour cette raison que les\nstructures auto-équilibrantes sont\nimportantes en pratique."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "usage"
      ],
      "title": "Cas d'usage",
      "statement": "Lequel des cas suivants est un usage typique\nd'un arbre binaire de recherche ?",
      "options": [
        {
          "text": "Maintenir dynamiquement un ensemble trié de valeurs avec des ajouts, des suppressions et des recherches fréquentes",
          "correct": true,
          "feedback": "C'est l'usage emblématique. On peut par\nexemple gérer une liste de scores triés\nen temps réel, ou un dictionnaire dont\nles clés doivent rester ordonnées.\n"
        },
        {
          "text": "Compter le nombre de mots dans un texte",
          "correct": false,
          "feedback": "Un compteur simple suffit pour cette\ntâche. L'utilisation d'un arbre binaire\nde recherche serait disproportionnée.\n"
        },
        {
          "text": "Afficher une image",
          "correct": false,
          "feedback": "L'affichage d'une image n'a aucun rapport\navec la structure d'arbre binaire de\nrecherche.\n"
        },
        {
          "text": "Calculer la somme des éléments d'une liste",
          "correct": false,
          "feedback": "Un parcours linéaire de la liste suffit.\nAucune structure ordonnée n'est\nnécessaire.\n"
        }
      ],
      "explanation": "En pratique, de nombreuses bibliothèques\nutilisent des arbres auto-équilibrés pour\nimplémenter des ensembles et dictionnaires\ntriés. C'est le cas par exemple de `TreeSet`\net `TreeMap` en Java, ou de `std::set` et\n`std::map` en C++."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "implementation",
        "code"
      ],
      "title": "Recherche récursive",
      "statement": "Quelle fonction Python recherche correctement\nla valeur `v` dans un arbre binaire de\nrecherche ?",
      "options": [
        {
          "text": "```\ndef chercher(n, v):\n    if n is None: return False\n    if v == n.valeur: return True\n    if v < n.valeur:\n        return chercher(n.gauche, v)\n    return chercher(n.droit, v)\n```\n",
          "correct": true,
          "feedback": "Cette fonction ne descend que dans **un\nseul** sous-arbre selon la comparaison.\nC'est ce qui donne la complexité\n$O(\\log n)$ pour un arbre équilibré.\n"
        },
        {
          "text": "```\ndef chercher(n, v):\n    return v == n.valeur\n```\n",
          "correct": false,
          "feedback": "Cette fonction ne descend pas dans\nl'arbre. Elle ne renvoie un résultat\ncorrect que si la valeur cherchée est\nexactement à la racine.\n"
        },
        {
          "text": "```\ndef chercher(n, v):\n    return True\n```\n",
          "correct": false,
          "feedback": "Cette fonction renvoie toujours `True`,\nsans effectuer la moindre recherche.\nElle est manifestement incorrecte.\n"
        },
        {
          "text": "```\ndef chercher(n, v):\n    if n is None: return False\n    if n.valeur == v: return True\n    return chercher(n.gauche, v) or chercher(n.droit, v)\n```\n",
          "correct": false,
          "feedback": "Cette fonction parcourt l'arbre entier\nsans exploiter la propriété d'ordre. Sa\ncomplexité est en $O(n)$ et non en\n$O(\\log n)$.\n"
        }
      ],
      "explanation": "C'est précisément la propriété d'ordre qui\npermet d'éliminer la moitié de l'arbre à\nchaque comparaison. Sans cette propriété, on\nretomberait sur un parcours linéaire de tous\nles nœuds."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "equilibrage",
        "avl"
      ],
      "title": "Arbres AVL",
      "statement": "Un arbre AVL est un arbre binaire de\nrecherche auto-équilibré. Quelle contrainte\nmaintient-il pour rester équilibré ?",
      "options": [
        {
          "text": "Pour chaque nœud, les hauteurs des sous-arbres gauche et droit ne diffèrent jamais de plus de $1$",
          "correct": true,
          "feedback": "C'est la définition stricte d'un arbre\nAVL. Lorsque cette propriété est violée\naprès une insertion, des **rotations**\nrétablissent l'équilibre. Les arbres AVL\nont été inventés par Adelson-Velsky et\nLandis en $1962$.\n"
        },
        {
          "text": "Tous les chemins de la racine aux feuilles ont la même longueur",
          "correct": false,
          "feedback": "Cette contrainte définirait un arbre\nparfait, beaucoup plus rigide qu'un\narbre AVL.\n"
        },
        {
          "text": "Tous les sous-arbres doivent avoir la même taille",
          "correct": false,
          "feedback": "La contrainte porte sur la **hauteur** des\nsous-arbres, et non sur leur taille en\nnombre de nœuds.\n"
        },
        {
          "text": "La racine doit toujours être la valeur médiane des valeurs présentes",
          "correct": false,
          "feedback": "Maintenir la médiane à la racine après\nchaque opération serait beaucoup trop\ncoûteux. Ce n'est pas la stratégie\nadoptée par les arbres AVL.\n"
        }
      ],
      "explanation": "L'arbre AVL garantit, pour $n$ nœuds, une\nhauteur en $O(\\log n)$, et donc des\nopérations en $O(\\log n)$. Le coût à payer\nest l'effort de maintien (rotations) à\nchaque insertion ou suppression."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "rouge-noir"
      ],
      "title": "Arbres rouge-noir",
      "statement": "Les arbres rouge-noir sont une autre famille\nd'arbres binaires de recherche\nauto-équilibrés. Quelle est leur particularité\npar rapport aux arbres AVL ?",
      "options": [
        {
          "text": "Ils n'autorisent que les insertions, et pas les suppressions",
          "correct": false,
          "feedback": "Les arbres rouge-noir gèrent toutes les\nopérations classiques : recherche,\ninsertion et suppression.\n"
        },
        {
          "text": "Ils sont parfaitement équilibrés",
          "correct": false,
          "feedback": "Ils sont en réalité moins strictement\néquilibrés que les arbres AVL, mais avec\nun coût d'équilibrage moindre.\n"
        },
        {
          "text": "Ils utilisent un tri par tas en interne",
          "correct": false,
          "feedback": "Les arbres rouge-noir n'ont aucun rapport\navec les tas, qui constituent une\nstructure de données différente.\n"
        },
        {
          "text": "Ils tolèrent un déséquilibre légèrement supérieur (la hauteur reste majorée par $2 \\log n$), en échange d'opérations d'équilibrage moins fréquentes",
          "correct": true,
          "feedback": "Ce compromis est intéressant en pratique.\nLes arbres AVL sont plus rapides en\nrecherche, parce que plus plats, mais\nplus coûteux à maintenir. Les arbres\nrouge-noir équilibrent ces deux aspects.\n"
        }
      ],
      "explanation": "Les arbres rouge-noir sont à la base de\nnombreuses bibliothèques standard (`std::map`\nen C++, `TreeMap` en Java). Ils ont été\ninventés en $1972$ par Bayer, puis\ngénéralisés par Sedgewick."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "insertion-pire-cas"
      ],
      "title": "Insertion dans le pire cas",
      "statement": "Dans un arbre binaire de recherche **non\néquilibré**, quelle séquence d'insertions\nconduit au pire cas (forme dégénérée) ?",
      "options": [
        {
          "text": "Insérer les valeurs en commençant par la médiane",
          "correct": false,
          "feedback": "Au contraire, insérer la médiane en\npremier conduit à un arbre bien\néquilibré.\n"
        },
        {
          "text": "Insérer une seule valeur",
          "correct": false,
          "feedback": "Un arbre composé d'un seul nœud ne pose\névidemment aucun problème de\ndégénérescence.\n"
        },
        {
          "text": "Insérer les valeurs dans un ordre aléatoire",
          "correct": false,
          "feedback": "Avec un ordre aléatoire, on tend vers une\nbonne forme d'arbre, dont la hauteur est\nen $O(\\log n)$ en espérance.\n"
        },
        {
          "text": "Insérer les valeurs déjà triées, par ordre croissant ou décroissant",
          "correct": true,
          "feedback": "En insérant les valeurs $1, 2, 3, \\ldots, n$\ndans l'ordre, chaque nouvelle valeur va à\ndroite de la précédente, ce qui crée un\npeigne. La hauteur atteint alors $n - 1$\net toutes les opérations dégénèrent en\n$O(n)$.\n"
        }
      ],
      "explanation": "Pour éviter ce piège, deux solutions\nclassiques. On peut utiliser un arbre\nauto-équilibré, ou bien mélanger les\ninsertions au préalable. C'est aussi la\nmotivation des arbres randomisés (treaps),\nqui maintiennent l'équilibre grâce au\nhasard."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "parcours-infixe"
      ],
      "title": "Application du parcours infixe",
      "statement": "Comment obtenir efficacement les valeurs\n**triées** d'un arbre binaire de recherche ?",
      "options": [
        {
          "text": "Trier les valeurs à chaque opération",
          "correct": false,
          "feedback": "Un arbre binaire de recherche maintient\ndéjà l'ordre par sa structure même. Il\nserait redondant de retrier les valeurs.\n"
        },
        {
          "text": "Ne renvoyer que les feuilles de l'arbre",
          "correct": false,
          "feedback": "On perdrait alors toutes les valeurs des\nnœuds internes, ce qui est manifestement\nincorrect.\n"
        },
        {
          "text": "Effectuer un parcours préfixe",
          "correct": false,
          "feedback": "Le parcours préfixe ne donne pas les\nvaleurs triées en général. Seul le\nparcours infixe possède cette propriété.\n"
        },
        {
          "text": "Effectuer un parcours infixe, qui visite naturellement les valeurs en ordre croissant",
          "correct": true,
          "feedback": "Le parcours infixe en $O(n)$ donne\ndirectement les valeurs triées. On ne\npeut pas faire mieux : il faut bien\nvisiter chaque élément au moins une\nfois.\n"
        }
      ],
      "explanation": "Cette propriété permet d'utiliser un arbre\nbinaire de recherche pour réaliser un tri.\nAvec $n$ insertions en $O(n \\log n)$ amortis\net un parcours infixe en $O(n)$, on obtient\nun tri en $O(n \\log n)$, comparable à un\ntri fusion ou à un tri rapide."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "code-insertion"
      ],
      "title": "Code d'insertion récursive",
      "statement": "Quelle fonction Python insère correctement la\nvaleur `v` dans un arbre binaire de\nrecherche, en renvoyant la nouvelle racine ?",
      "options": [
        {
          "text": "```\ndef inserer(n, v):\n    if n is None: return Noeud(v)\n    if v < n.valeur:\n        n.gauche = inserer(n.gauche, v)\n    else:\n        n.droit = inserer(n.droit, v)\n    return n\n```\n",
          "correct": true,
          "feedback": "Si l'arbre est vide, la fonction crée un\nnouveau nœud. Sinon, elle descend\nrécursivement à gauche ou à droite selon\nla comparaison, puis réaffecte le\nsous-arbre modifié.\n"
        },
        {
          "text": "```\ndef inserer(n, v):\n    return Noeud(v)\n```\n",
          "correct": false,
          "feedback": "Cette fonction remplace l'arbre entier\npar un seul nœud, perdant toutes les\nvaleurs précédemment insérées.\n"
        },
        {
          "text": "```\ndef inserer(n, v):\n    n.valeur = v\n    return n\n```\n",
          "correct": false,
          "feedback": "Cette fonction écrase la valeur de la\nracine au lieu d'ajouter un nœud. Elle\nne construit pas un arbre binaire de\nrecherche.\n"
        },
        {
          "text": "```\ndef inserer(n, v):\n    n.gauche = Noeud(v)\n    return n\n```\n",
          "correct": false,
          "feedback": "Cette fonction insère toujours à gauche,\nsans tenir compte de la valeur de `v`.\nElle viole donc systématiquement la\npropriété d'ordre.\n"
        }
      ],
      "explanation": "Cette fonction est typique : elle parcourt\nl'arbre depuis la racine en comparant les\nvaleurs, et crée un nouveau nœud à la place\nvide trouvée. Sa complexité est en $O(h)$,\noù $h$ est la hauteur de l'arbre."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "equilibre-construction"
      ],
      "title": "Construire un arbre équilibré",
      "statement": "Pour construire un arbre binaire de recherche\néquilibré à partir d'un tableau trié, quelle\nstratégie est la plus simple ?",
      "options": [
        {
          "text": "Insérer les éléments dans un ordre aléatoire",
          "correct": false,
          "feedback": "Cette approche donne un arbre presque\néquilibré en moyenne, mais sans aucune\ngarantie absolue.\n"
        },
        {
          "text": "Insérer les éléments dans l'ordre du tableau",
          "correct": false,
          "feedback": "Cette stratégie produit précisément un\npeigne dégénéré, puisque les valeurs\nsont déjà triées.\n"
        },
        {
          "text": "Prendre l'élément du milieu comme racine, puis construire récursivement le sous-arbre gauche à partir de la moitié inférieure et le sous-arbre droit à partir de la moitié supérieure",
          "correct": true,
          "feedback": "C'est l'algorithme classique. La moitié\nde gauche devient le sous-arbre gauche,\nconstruit récursivement par la même\nméthode, et de même pour la moitié de\ndroite. L'arbre obtenu est parfaitement\néquilibré, de hauteur $O(\\log n)$.\n"
        },
        {
          "text": "Insérer les éléments du dernier au premier",
          "correct": false,
          "feedback": "Cette stratégie produit un peigne\ndégénéré dans l'autre sens, ce qui n'est\npas plus satisfaisant.\n"
        }
      ],
      "explanation": "Cet algorithme est en $O(n)$. Il est utile\npour reconstruire un arbre équilibré à\npartir d'une séquence triée, par exemple\nlors d'une migration de données."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "recherche-pas-trouve"
      ],
      "title": "Échec de la recherche",
      "statement": "Lorsque la recherche d'une valeur **absente**\nse termine dans un arbre binaire de recherche,\nà quel endroit s'arrête-t-on ?",
      "options": [
        {
          "text": "À la racine de l'arbre",
          "correct": false,
          "feedback": "On s'arrête en général à un endroit plus\nprofond dans l'arbre, lorsque la branche\nrecherchée est vide.\n"
        },
        {
          "text": "La fonction lève une exception",
          "correct": false,
          "feedback": "On renvoie typiquement la valeur `False`\nou `None`, sans lever d'exception\n(sauf si on adopte volontairement cette\nconvention).\n"
        },
        {
          "text": "À un sous-arbre vide (`None`), preuve que la valeur n'est pas présente dans l'arbre",
          "correct": true,
          "feedback": "À chaque étape, on choisit le sous-arbre\ngauche ou le sous-arbre droit. Si la\nvaleur n'est jamais égale au nœud\ncourant et que l'on atteint un\nsous-arbre vide, c'est qu'elle est\nabsente.\n"
        },
        {
          "text": "À une feuille, peu importe sa valeur",
          "correct": false,
          "feedback": "On ne s'arrête pas systématiquement à\nune feuille. On peut s'arrêter à un\nsous-arbre vide pendant à un nœud\ninterne.\n"
        }
      ],
      "explanation": "C'est ce comportement qui permet la recherche\nen $O(h)$ : on ne descend que dans une seule\nbranche, ce qui prend au plus $\\log n$\nétapes pour un arbre équilibré."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "verification"
      ],
      "title": "Vérifier la propriété d'ordre",
      "statement": "Pour vérifier qu'un arbre binaire respecte la\npropriété d'arbre binaire de recherche, il\n**ne suffit pas** de vérifier localement à\nchaque nœud que :",
      "options": [
        {
          "text": "Les feuilles se trouvent toutes au même niveau",
          "correct": false,
          "feedback": "Aucune contrainte de niveau n'est\nimposée dans un arbre binaire de\nrecherche.\n"
        },
        {
          "text": "La hauteur des sous-arbres est équilibrée",
          "correct": false,
          "feedback": "Cette propriété correspond à\nl'équilibre, et non à la propriété\nd'ordre qui définit un arbre binaire de\nrecherche.\n"
        },
        {
          "text": "L'enfant gauche est plus petit et l'enfant droit est plus grand. Il faut aussi vérifier que toutes les valeurs du sous-arbre gauche sont inférieures (et symétriquement pour le sous-arbre droit)",
          "correct": true,
          "feedback": "C'est un piège classique. Vérifier\nuniquement les enfants directs ne\nsuffit pas. La propriété d'arbre\nbinaire de recherche demande que\n**tout** le sous-arbre gauche soit\ninférieur à la valeur du nœud, et pas\nseulement la racine de ce sous-arbre.\n"
        },
        {
          "text": "La valeur du nœud est la moyenne de celles de ses enfants",
          "correct": false,
          "feedback": "Cette propriété correspond à un autre\ntype d'arbre, mais elle n'a aucun\nrapport avec la définition d'un arbre\nbinaire de recherche.\n"
        }
      ],
      "explanation": "L'algorithme correct consiste à transmettre\nrécursivement les bornes (`min`, `max`) que\nla valeur de chaque nœud doit respecter. La\nracine accepte toutes les valeurs, le\nsous-arbre gauche reçoit l'intervalle\n$(\\min, \\text{valeur})$ et le sous-arbre\ndroit reçoit l'intervalle\n$(\\text{valeur}, \\max)$."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "comparaison-table-hash"
      ],
      "title": "Comparaison avec une table de hachage",
      "statement": "Comparé à une **table de hachage**, quel est\nl'**avantage** d'un arbre binaire de\nrecherche ?",
      "options": [
        {
          "text": "Il maintient les éléments dans l'ordre, ce qui permet de répondre à des requêtes par intervalle ou de retrouver le voisin le plus proche",
          "correct": true,
          "feedback": "Un arbre binaire de recherche permet de\nrépondre à des questions comme « tous\nles éléments entre $a$ et $b$ » ou\n« le plus petit élément supérieur ou égal\nà $v$ » en $O(\\log n)$. Une table de\nhachage ne le permet pas.\n"
        },
        {
          "text": "Il consomme moins de mémoire",
          "correct": false,
          "feedback": "Cette affirmation n'est pas\nsystématiquement vraie. La consommation\nmémoire dépend de l'implémentation des\ndeux structures.\n"
        },
        {
          "text": "Il est plus simple à implémenter",
          "correct": false,
          "feedback": "Au contraire, un arbre binaire de\nrecherche auto-équilibré est en\ngénéral plus complexe à implémenter\nqu'une table de hachage.\n"
        },
        {
          "text": "La recherche y est plus rapide",
          "correct": false,
          "feedback": "C'est l'inverse. Une table de hachage\na une recherche en $O(1)$ amorti, ce\nqui est plus rapide que le $O(\\log n)$\nd'un arbre binaire de recherche.\n"
        }
      ],
      "explanation": "Le choix dépend du besoin. Pour des accès\npar clé sans contrainte d'ordre, la table\nde hachage est préférable. Pour maintenir\nles valeurs en ordre ou répondre à des\nrequêtes par intervalle, l'arbre binaire de\nrecherche est mieux adapté."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "code-exemple"
      ],
      "title": "Lecture d'un arbre",
      "statement": "On insère successivement les valeurs $5$,\n$2$, $8$, $1$, $3$ dans un arbre binaire de\nrecherche initialement vide. Quel est le\nrésultat de son parcours infixe ?",
      "options": [
        {
          "text": "$8, 5, 3, 2, 1$ (ordre décroissant)",
          "correct": false,
          "feedback": "Cette séquence correspond au parcours\ninfixe inversé, et non au parcours\ninfixe standard.\n"
        },
        {
          "text": "$1, 2, 3, 5, 8$ (ordre croissant)",
          "correct": true,
          "feedback": "Le parcours infixe donne toujours les\nvaleurs en ordre croissant. On peut le\nvérifier en construisant l'arbre :\nracine $5$, sous-arbre gauche enraciné\nen $2$ (avec $1$ et $3$), sous-arbre\ndroit réduit à $8$. Le parcours infixe\ndonne bien $1, 2, 3, 5, 8$.\n"
        },
        {
          "text": "$5, 2, 1, 3, 8$ (parcours préfixe)",
          "correct": false,
          "feedback": "Cette séquence correspond au parcours\n**préfixe** (racine, gauche, droite),\net non au parcours infixe.\n"
        },
        {
          "text": "$5, 2, 8, 1, 3$ (ordre d'insertion)",
          "correct": false,
          "feedback": "Le parcours infixe d'un arbre binaire\nde recherche donne les valeurs triées,\net non l'ordre d'insertion d'origine.\n"
        }
      ],
      "explanation": "Cet exemple illustre parfaitement la\npropriété d'ordre. Dessiner l'arbre est\nsouvent utile pour visualiser et vérifier\nles différents parcours."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "suppression-cas-difficile"
      ],
      "title": "Suppression d'un nœud à deux enfants",
      "statement": "Pour supprimer un nœud à **deux enfants** dans\nun arbre binaire de recherche, par quoi le\nremplace-t-on ?",
      "options": [
        {
          "text": "Par le sous-arbre gauche complet",
          "correct": false,
          "feedback": "Déplacer un sous-arbre entier ne\nconserverait pas la propriété d'ordre\nde l'arbre.\n"
        },
        {
          "text": "Par la racine de l'arbre",
          "correct": false,
          "feedback": "Cette opération briserait toute la\nstructure de l'arbre.\n"
        },
        {
          "text": "Par une feuille choisie au hasard",
          "correct": false,
          "feedback": "Choisir une feuille au hasard violerait\nla propriété d'ordre de l'arbre.\n"
        },
        {
          "text": "Par son successeur infixe (le plus petit nœud du sous-arbre droit) ou par son prédécesseur infixe (le plus grand du sous-arbre gauche)",
          "correct": true,
          "feedback": "Ce nœud est le plus proche du nœud\nsupprimé dans l'ordre des valeurs. Il a\nforcément au plus un enfant (sinon, un\nnœud encore plus à gauche existerait),\nce qui rend sa suppression simple. On\ncopie sa valeur dans le nœud à\nsupprimer, puis on supprime ce\nsuccesseur.\n"
        }
      ],
      "explanation": "Le successeur infixe se trouve en\ndescendant une fois à droite, puis tout à\ngauche. Cette opération s'effectue en\n$O(h)$ étapes au pire, où $h$ est la\nhauteur de l'arbre."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "hauteur-attendue"
      ],
      "title": "Hauteur attendue",
      "statement": "Pour un arbre binaire de recherche construit\nen insérant $n$ valeurs **dans un ordre\naléatoire**, quelle est la hauteur attendue ?",
      "options": [
        {
          "text": "$\\Theta(\\sqrt{n})$",
          "correct": false,
          "feedback": "Cette croissance ne correspond pas au\ncomportement asymptotique de la\nhauteur d'un arbre binaire de\nrecherche.\n"
        },
        {
          "text": "$\\Theta(n)$",
          "correct": false,
          "feedback": "Cette hauteur correspond au pire cas\n(insertion dans un ordre déjà trié) et\nnon au cas moyen. Avec un ordre\naléatoire, le résultat est bien\nmeilleur.\n"
        },
        {
          "text": "$\\Theta(\\log n)$",
          "correct": true,
          "feedback": "C'est un résultat classique. En\nespérance (sur l'ordre aléatoire\nd'insertion), la hauteur est en\n$\\Theta(\\log n)$. Toutes les opérations\nont alors une complexité moyenne en\n$O(\\log n)$, sans qu'aucun équilibrage\nexplicite soit nécessaire.\n"
        },
        {
          "text": "$\\Theta(1)$",
          "correct": false,
          "feedback": "La hauteur d'un arbre croît\nnécessairement avec le nombre de\nvaleurs $n$. Elle ne peut pas rester\nconstante.\n"
        }
      ],
      "explanation": "Ce résultat justifie l'utilisation\nd'arbres binaires de recherche non\nauto-équilibrés lorsque l'on contrôle\nl'ordre d'insertion (par exemple en\nmélangeant les données au préalable). Les\nstructures auto-équilibrées garantissent\nune complexité en $O(\\log n)$ même dans\nle pire cas."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "analyse-comparaison"
      ],
      "title": "Tri par arbre binaire de recherche",
      "statement": "Le tri par arbre binaire de recherche, qui\nconsiste à insérer $n$ valeurs dans un arbre\npuis à effectuer un parcours infixe, a une\ncomplexité de :",
      "options": [
        {
          "text": "$O(n!)$",
          "correct": false,
          "feedback": "Cette complexité factorielle est\nencore plus excessive et ne correspond\nà aucun algorithme de tri raisonnable.\n"
        },
        {
          "text": "$O(n \\log n)$ en moyenne (avec un ordre aléatoire), mais $O(n^2)$ dans le pire cas (ordre déjà trié)",
          "correct": true,
          "feedback": "Chaque insertion est en $O(\\log n)$ en\nmoyenne, ce qui donne $O(n \\log n)$\npour les $n$ insertions, auxquelles\ns'ajoute $O(n)$ pour le parcours\ninfixe. Total : $O(n \\log n)$ en\nmoyenne. Mais avec un ordre\nd'insertion défavorable, la hauteur\nexplose et la complexité atteint\n$O(n^2)$.\n"
        },
        {
          "text": "$O(n)$",
          "correct": false,
          "feedback": "Insérer $n$ valeurs dans une structure\nordonnée ne peut pas se faire en\n$O(n)$. Tout tri par comparaisons est\nau moins en $\\Omega(n \\log n)$.\n"
        },
        {
          "text": "$O(2^n)$",
          "correct": false,
          "feedback": "Cette complexité exponentielle n'a\naucune raison d'apparaître dans un\ntri.\n"
        }
      ],
      "explanation": "Cet algorithme illustre un compromis : il\nest avantageux pour des données aléatoires,\nmais fragile dans le pire cas. C'est\npourquoi on préfère en pratique le tri\nfusion (toujours en $O(n \\log n)$\ngaranti) ou le tri rapide (souvent en\n$O(n \\log n)$ avec un bon choix de\npivot)."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "iterateur"
      ],
      "title": "Successeur d'un nœud",
      "statement": "Étant donné un nœud $n$ dans un arbre binaire\nde recherche, comment trouver son\n**successeur infixe**, c'est-à-dire la valeur\nimmédiatement supérieure ?",
      "options": [
        {
          "text": "La racine de l'arbre est toujours le successeur",
          "correct": false,
          "feedback": "Cette description est trop simpliste\net incorrecte. Le successeur d'un\nnœud n'est pas en général la racine.\n"
        },
        {
          "text": "En descendant toujours à gauche",
          "correct": false,
          "feedback": "Cette stratégie n'est pas systématique.\nLe successeur peut très bien se trouver\nailleurs dans l'arbre.\n"
        },
        {
          "text": "Le successeur est choisi au hasard parmi les autres nœuds",
          "correct": false,
          "feedback": "La détermination du successeur est\nstrictement déterministe : aucun\nhasard n'intervient.\n"
        },
        {
          "text": "Si le nœud $n$ a un sous-arbre droit, son successeur est le nœud le plus à gauche de ce sous-arbre. Sinon, c'est le premier ancêtre dont $n$ se trouve dans le sous-arbre gauche",
          "correct": true,
          "feedback": "C'est la définition correcte. En\nparcourant ainsi, on visite l'arbre\ndans l'ordre infixe sans utiliser la\nrécursion, ce qui est très pratique\npour implémenter un itérateur.\n"
        }
      ],
      "explanation": "Cette logique permet d'implémenter un\n**itérateur** sur un arbre binaire de\nrecherche, qui produit les valeurs en\nordre croissant sans stockage\nsupplémentaire (à part une pile implicite\npour remonter dans l'arbre)."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Quand utiliser un arbre binaire de recherche ?",
      "statement": "Lequel des cas suivants justifie l'usage\nd'un arbre binaire de recherche\nauto-équilibré plutôt qu'une autre structure\n?",
      "options": [
        {
          "text": "Implémenter un dictionnaire dont les clés sont triées dynamiquement, avec des recherches par intervalle fréquentes",
          "correct": true,
          "feedback": "C'est exactement le cas d'usage des\narbres binaires de recherche\nauto-équilibrés. Cette structure\npermet, en $O(\\log n)$, de répondre à\ndes requêtes comme « les clés entre\n$a$ et $b$ » ou « la plus petite clé\nsupérieure ou égale à $v$ ».\n"
        },
        {
          "text": "Calculer la somme des éléments d'une grande liste",
          "correct": false,
          "feedback": "Un parcours linéaire en $O(n)$ suffit\npour cette tâche. Aucune structure\nd'arbre n'apporterait de gain.\n"
        },
        {
          "text": "Stocker une liste fixe de noms à afficher",
          "correct": false,
          "feedback": "Pour des données statiques, une liste\ntriée stockée dans un tableau suffit.\nAucun arbre binaire de recherche\nn'est nécessaire.\n"
        },
        {
          "text": "Trier $1\\,000$ entiers une seule fois",
          "correct": false,
          "feedback": "Pour ce besoin, les algorithmes de tri\nclassiques (tri fusion, tri rapide)\nsont plus simples et tout aussi\nefficaces.\n"
        }
      ],
      "explanation": "Pour résumer : on utilise un arbre binaire\nde recherche pour des accès **dynamiques**\navec maintien d'un **ordre** sur les\nvaleurs. Pour des données statiques ou\nsans besoin d'ordre, des structures plus\nsimples suffisent."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "trace-insertion"
      ],
      "title": "Trace d'insertions successives",
      "statement": "On part d'un arbre binaire de recherche\nvide. On y insère successivement les valeurs\n$10, 5, 15, 3, 7, 12$. À quoi ressemble la\nstructure obtenue ?",
      "options": [
        {
          "text": "Tous les nœuds sont des feuilles d'une\nracine vide\n",
          "correct": false,
          "feedback": "Erreur : un arbre binaire de recherche\nn'a pas de « racine vide ». La première\nvaleur insérée devient la racine, et les\nsuivantes sont placées comme descendants.\n"
        },
        {
          "text": "Tous les nœuds sur une branche droite,\nformant un peigne\n",
          "correct": false,
          "feedback": "Erreur : on aurait un peigne uniquement\nsi les valeurs étaient insérées dans\nl'ordre croissant ou décroissant. Ici,\nl'ordre est plus équilibré.\n"
        },
        {
          "text": "Racine $5$ ; tous les autres nœuds\nrépartis selon leur valeur\n",
          "correct": false,
          "feedback": "Erreur : la racine est toujours la\n**première** valeur insérée, ici $10$,\npas $5$.\n"
        },
        {
          "text": "Racine $10$ ; sous-arbre gauche : $5$ avec\n$3$ à gauche et $7$ à droite ; sous-arbre\ndroit : $15$ avec $12$ à gauche\n",
          "correct": true,
          "feedback": "Bonne réponse : à chaque insertion, on\ncompare avec la racine pour choisir le\nsous-arbre. $10$ devient racine. $5 < 10$\nva à gauche. $15 > 10$ va à droite. $3 < 10$\npuis $3 < 5$ va à gauche de $5$. $7 < 10$\npuis $7 > 5$ va à droite de $5$. $12 > 10$\npuis $12 < 15$ va à gauche de $15$.\n"
        }
      ],
      "explanation": "Cet arbre obtenu ($6$ nœuds, hauteur $2$) est\nbien équilibré, ce qui n'est pas un hasard :\nla séquence d'insertion alterne intelligemment\nentre les côtés. Avec une mauvaise séquence\n($1, 2, 3, 4, 5, 6$), on aurait obtenu un\npeigne de hauteur $5$."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "verification-code"
      ],
      "title": "Code de vérification de la propriété",
      "statement": "Quelle fonction Python vérifie correctement\nqu'un arbre binaire respecte la propriété\nd'arbre binaire de recherche (en utilisant\ndes bornes minimum et maximum) ?",
      "options": [
        {
          "text": "```\ndef est_abr(n):\n    if n is None:\n        return True\n    if n.gauche and n.gauche.valeur >= n.valeur:\n        return False\n    if n.droit and n.droit.valeur <= n.valeur:\n        return False\n    return est_abr(n.gauche) and est_abr(n.droit)\n```\n",
          "correct": false,
          "feedback": "Erreur classique : on ne vérifie que la\nrelation parent/enfant direct. Cela rate\ndes cas comme racine $10$, gauche $5$,\ndroite-gauche-droite $20$ (qui est dans\nle sous-arbre gauche de $10$ mais\nsupérieure à $10$). L'arbre serait\ndéclaré valide à tort.\n"
        },
        {
          "text": "```\ndef est_abr(n):\n    return n is None\n```\n",
          "correct": false,
          "feedback": "Erreur : cette fonction renvoie `True`\nuniquement pour l'arbre vide, et `False`\npour tout arbre non vide, ce qui est\nmanifestement faux.\n"
        },
        {
          "text": "```\ndef est_abr(n, mini=float('-inf'), maxi=float('inf')):\n    if n is None:\n        return True\n    if not (mini < n.valeur < maxi):\n        return False\n    return est_abr(n.gauche, mini, n.valeur) and est_abr(n.droit, n.valeur, maxi)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on transmet à chaque\nappel récursif les bornes que la valeur\ndu sous-arbre doit respecter. À gauche,\non resserre la borne supérieure ; à\ndroite, la borne inférieure. C'est le\nschéma correct qui détecte aussi les\nviolations « profondes » (par exemple,\nun nœud lointain qui violerait l'ordre).\n"
        },
        {
          "text": "```\ndef est_abr(n):\n    if n is None:\n        return True\n    return parcours_infixe(n) == sorted(parcours_infixe(n))\n```\n",
          "correct": false,
          "feedback": "Cette approche est correcte mais\ninefficace : elle nécessite un parcours\ncomplet, un tri en $O(n \\log n)$ et une\ncomparaison. La version récursive avec\nbornes est en $O(n)$ et plus élégante.\n"
        }
      ],
      "explanation": "Le piège pédagogique de cette question est\nla première option, qui semble naturelle\nmais rate les violations à distance. C'est\npour cela que la transmission de bornes par\nparamètre est essentielle : elle propage la\ncontrainte d'ordre tout au long de la\ndescente récursive."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "extremums"
      ],
      "title": "Minimum et maximum dans un ABR",
      "statement": "Comment trouver respectivement le **minimum**\net le **maximum** d'un arbre binaire de\nrecherche non vide ?",
      "options": [
        {
          "text": "Le minimum est toujours la racine, le\nmaximum est toujours une feuille\n",
          "correct": false,
          "feedback": "Erreur : la racine est la **première\nvaleur insérée**, pas le minimum. Le\nminimum est dans le sous-arbre le plus à\ngauche. Quant au maximum, il peut être\nn'importe où à droite, pas forcément une\nfeuille.\n"
        },
        {
          "text": "On parcourt tout l'arbre et on retient les\nvaleurs minimale et maximale au fur et à\nmesure\n",
          "correct": false,
          "feedback": "Cette méthode fonctionne (en $O(n)$) mais\nignore la propriété d'ordre. Avec\nl'arbre binaire de recherche, on peut\nfaire bien mieux en descendant\ndirectement à gauche ou à droite, en\n$O(h)$.\n"
        },
        {
          "text": "Le minimum se trouve en descendant toujours\nà gauche depuis la racine ; le maximum\nen descendant toujours à droite. Les\ndeux opérations sont en $O(h)$ où $h$ est\nla hauteur\n",
          "correct": true,
          "feedback": "Bonne réponse : par la propriété d'ordre,\ntous les nœuds plus petits qu'un nœud\ndonné sont à sa gauche. En descendant\nsystématiquement à gauche jusqu'à\natteindre une feuille (sans enfant\ngauche), on trouve la plus petite valeur.\nSymétriquement à droite pour le maximum.\n"
        },
        {
          "text": "Le minimum et le maximum sont les deux\nenfants de la racine\n",
          "correct": false,
          "feedback": "Erreur : seul l'enfant gauche **direct**\nde la racine peut être plus petit qu'elle,\npas un éventuel descendant plus profond.\nEt symétriquement à droite. La descente\njusqu'au bout est nécessaire.\n"
        }
      ],
      "explanation": "Cette propriété est exploitée dans la\nsuppression d'un nœud à deux enfants : on\nremplace le nœud par son **successeur\ninfixe** (le minimum de son sous-arbre droit)\nou son **prédécesseur infixe** (le maximum de\nson sous-arbre gauche), tous deux trouvables\nen $O(h)$."
    }
  ]
}