{
  "chapter": {
    "id": "tris",
    "level": "premiere",
    "theme": "Algorithmique",
    "title": "Tris par sélection et par insertion",
    "description": "Algorithmes de tri étudiés en Première NSI conformément\nau programme officiel : tri par sélection et tri par\ninsertion. Principes, implémentations Python, invariants\nde boucle, terminaison, coût quadratique au pire,\nstabilité, comparaison.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Qu'est-ce qu'un tri ?",
      "statement": "Que signifie « **trier** une liste » en informatique ?",
      "options": [
        {
          "text": "Réorganiser ses éléments selon un ordre défini",
          "correct": true,
          "feedback": "Bonne réponse : un tri produit une\n**permutation** de la liste d'origine\nrespectant un ordre (croissant, décroissant,\nalphabétique, etc.).\n"
        },
        {
          "text": "Supprimer les doublons",
          "correct": false,
          "feedback": "Erreur : trier ne supprime pas les doublons.\nPour cela, il faut une étape séparée, par\nexemple un parcours qui ne recopie dans une\nnouvelle liste que les valeurs pas encore\nvues.\n"
        },
        {
          "text": "Convertir les éléments en chaînes",
          "correct": false,
          "feedback": "Erreur : aucun changement de type.\n"
        },
        {
          "text": "La découper en plusieurs morceaux de tailles égales",
          "correct": false,
          "feedback": "Erreur : c'est le découpage, pas le tri.\n"
        }
      ],
      "explanation": "Trier permet ensuite des opérations efficaces\n(recherche dichotomique, médiane, fusion).\nAu programme de Première : tri par **sélection**\net tri par **insertion**."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "tri-selection-principe"
      ],
      "title": "Tri par sélection : principe",
      "statement": "Quel est le principe du **tri par sélection** ?",
      "options": [
        {
          "text": "Couper la liste en deux et trier chaque moitié",
          "correct": false,
          "feedback": "Erreur : c'est le tri **fusion** (diviser\npour régner), au programme de Terminale.\n"
        },
        {
          "text": "Insérer chaque élément à sa place dans la partie déjà triée",
          "correct": false,
          "feedback": "Erreur : c'est le tri par **insertion**.\n"
        },
        {
          "text": "Sélectionner le minimum non trié et le placer en tête du reste",
          "correct": true,
          "feedback": "Bonne réponse : à chaque étape, on cherche\nle minimum du sous-tableau non trié et on\nl'échange avec le premier élément de cette\npartie.\n"
        },
        {
          "text": "Comparer systématiquement chaque paire d'éléments voisins adjacents",
          "correct": false,
          "feedback": "Erreur : ce serait l'idée du tri à bulles,\nqui n'est pas au programme.\n"
        }
      ],
      "explanation": "Sélection : « je cherche le min du non trié, je\nle mets en tête de la partie non triée, je\nrecommence sur le reste ». Simple et systématique."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "tri-insertion-principe"
      ],
      "title": "Tri par insertion : principe",
      "statement": "Quel est le principe du **tri par insertion** ?",
      "options": [
        {
          "text": "Insérer chaque élément à sa place dans la partie déjà triée",
          "correct": true,
          "feedback": "Bonne réponse : on parcourt la liste de\ngauche à droite ; pour chaque élément, on\nle décale vers la gauche tant qu'il est plus\npetit que son voisin de gauche.\n"
        },
        {
          "text": "Compter le nombre d'occurrences",
          "correct": false,
          "feedback": "Erreur : c'est le « tri par comptage », un\nalgorithme différent (hors programme).\n"
        },
        {
          "text": "Sélectionner le minimum et le placer en tête",
          "correct": false,
          "feedback": "Erreur : c'est le tri par **sélection**.\n"
        },
        {
          "text": "Inverser complètement l'ordre de tous les éléments de la liste",
          "correct": false,
          "feedback": "Erreur : aucune relation avec un tri.\n"
        }
      ],
      "explanation": "C'est exactement comme on trie une main de\ncartes : on prend une carte, on l'insère à sa\nplace dans le paquet déjà trié."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "echange"
      ],
      "title": "Échange Python",
      "statement": "Quelle est la façon la plus simple d'**échanger deux éléments** d'une liste en Python ?",
      "options": [
        {
          "text": "`tmp = liste[i]; liste[i] = liste[j]; liste[j] = tmp`",
          "correct": false,
          "feedback": "Code correct mais verbeux. Python offre\nplus simple.\n"
        },
        {
          "text": "`liste.swap(i, j)`",
          "correct": false,
          "feedback": "Erreur : la méthode `.swap` n'existe pas\nsur les listes Python.\n"
        },
        {
          "text": "`liste[i] = liste[j]`",
          "correct": false,
          "feedback": "Erreur : on perd la valeur originale en\n`liste[i]`.\n"
        },
        {
          "text": "`liste[i], liste[j] = liste[j], liste[i]`",
          "correct": true,
          "feedback": "Bonne réponse : affectation simultanée,\nsyntaxe pythonique. Python crée un tuple\ntemporaire `(liste[j], liste[i])` puis\ndéballe.\n"
        }
      ],
      "explanation": "L'affectation simultanée est utilisée dans les\ntris classiques. Compacte et lisible."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "complexite"
      ],
      "title": "Coût au pire cas",
      "statement": "Quel est le **coût** au pire cas du tri par\nsélection et du tri par insertion sur une liste\nde longueur n ?",
      "options": [
        {
          "text": "Logarithmique",
          "correct": false,
          "feedback": "Erreur : aucun tri ne peut être\nlogarithmique, il faut au moins examiner\nles n éléments. Les meilleurs tris par\ncomparaison atteignent n log n (tri\nfusion, programme de Terminale).\n"
        },
        {
          "text": "Constant (indépendant de n)",
          "correct": false,
          "feedback": "Erreur : aucun algorithme ne trie en temps\nconstant.\n"
        },
        {
          "text": "Quadratique (proportionnel à n²)",
          "correct": true,
          "feedback": "Bonne réponse : les deux tris sont\n**quadratiques** au pire à cause d'une\n**double boucle** sur la liste. Pour\nn = 1000, on fait ~ 10⁶ opérations.\n"
        },
        {
          "text": "Linéaire (proportionnel à n)",
          "correct": false,
          "feedback": "Erreur : trop optimiste. La double boucle\nimpose plus.\n"
        }
      ],
      "explanation": "Coût quadratique = limite des tris « par\ncomparaisons simples ». Pour aller plus vite,\nil faut des stratégies plus malines (diviser\npour régner, Terminale)."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "tri-en-place"
      ],
      "title": "Tri en place",
      "statement": "Que signifie « tri **en place** » ?",
      "options": [
        {
          "text": "Le tri se fait sur place dans le disque dur",
          "correct": false,
          "feedback": "Erreur : aucune relation avec le stockage\nphysique.\n"
        },
        {
          "text": "Le tri remplace les valeurs par leurs indices",
          "correct": false,
          "feedback": "Erreur : le tri ne modifie que l'ordre, pas\nles valeurs.\n"
        },
        {
          "text": "Le tri ne fonctionne que sur des listes courtes",
          "correct": false,
          "feedback": "Erreur : aucune restriction de taille.\n"
        },
        {
          "text": "Le tri ne crée pas de nouvelle liste : il modifie la liste d'origine",
          "correct": true,
          "feedback": "Bonne réponse : un tri en place utilise une\nquantité de mémoire **constante**\n(indépendante de n), juste quelques\nvariables temporaires. Sélection et\ninsertion sont des tris en place.\n"
        }
      ],
      "explanation": "Un tri **non en place** crée une copie triée\n(par exemple `sorted(liste)` en Python). Avec\n`liste.sort()`, on trie en place."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "meilleur-cas-insertion"
      ],
      "title": "Tri par insertion : meilleur cas",
      "statement": "Quel est le meilleur cas pour le **tri par insertion** ?",
      "options": [
        {
          "text": "Liste déjà triée",
          "correct": true,
          "feedback": "Bonne réponse : si la liste est déjà triée,\nchaque élément est déjà à sa place ; on ne\nfait que vérifier (un seul test par\nélément). Coût linéaire (n - 1\ncomparaisons).\n"
        },
        {
          "text": "Liste de longueur impaire",
          "correct": false,
          "feedback": "Erreur : la parité de la longueur n'a\naucune influence.\n"
        },
        {
          "text": "Liste mélangée aléatoirement",
          "correct": false,
          "feedback": "Erreur : c'est le cas moyen, de coût\nquadratique (chaque élément remonte en\nmoyenne la moitié de la partie triée).\n"
        },
        {
          "text": "Liste triée à l'envers",
          "correct": false,
          "feedback": "Erreur : c'est au contraire le **pire cas**\npour insertion (chaque élément remonte tout\nà fait à gauche).\n"
        }
      ],
      "explanation": "Cette propriété fait du tri par insertion un\nbon choix pour les listes **presque triées**\n(par exemple un fichier journal triable au fil\nde l'eau)."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "trace-selection"
      ],
      "title": "Trace tri par sélection",
      "statement": "On trie `[4, 1, 3]` par tri par sélection\n(croissant). Que vaut la liste après la\n**première** passe ?",
      "options": [
        {
          "text": "`[1, 3, 4]`",
          "correct": false,
          "feedback": "Erreur : c'est le résultat **final**, pas\naprès une passe.\n"
        },
        {
          "text": "`[1, 4, 3]`",
          "correct": true,
          "feedback": "Bonne réponse : on cherche le min de la\nliste entière (= 1, indice 1), on l'échange\navec `liste[0]`. Résultat : `[1, 4, 3]`.\n"
        },
        {
          "text": "`[3, 4, 1]`",
          "correct": false,
          "feedback": "Erreur : aucune permutation cohérente avec\nla sélection.\n"
        },
        {
          "text": "`[4, 1, 3]`",
          "correct": false,
          "feedback": "Erreur : la liste a bien été modifiée à la\npremière passe.\n"
        }
      ],
      "explanation": "Tri sélection passe par passe : passe 1 fixe\nl'élément en position 0 ; passe 2 fixe celui en\nposition 1 ; etc."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "pire-cas-insertion"
      ],
      "title": "Tri par insertion : pire cas",
      "statement": "Quel est le **pire cas** pour le tri par\ninsertion ?",
      "options": [
        {
          "text": "Liste triée à l'envers (décroissant)",
          "correct": true,
          "feedback": "Bonne réponse : chaque nouvel élément doit\nêtre déplacé jusqu'au début de la partie\ndéjà traitée, soit n(n-1)/2 décalages au\ntotal. Coût quadratique.\n"
        },
        {
          "text": "Liste déjà triée",
          "correct": false,
          "feedback": "Erreur : c'est le **meilleur** cas (linéaire).\n"
        },
        {
          "text": "Liste comportant de nombreuses valeurs en double",
          "correct": false,
          "feedback": "Pas spécifique : le coût dépend de l'ordre,\npas des doublons.\n"
        },
        {
          "text": "Liste entièrement vide, sans aucun élément à traiter",
          "correct": false,
          "feedback": "Pas un pire cas : coût constant (la boucle\nne s'exécute pas).\n"
        }
      ],
      "explanation": "Symétrie typique : « ordre inverse » est\nsouvent le pire cas pour les tris naïfs ;\n« déjà trié » est souvent le meilleur cas pour\nl'insertion."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "tri-selection-code"
      ],
      "title": "Code tri par sélection",
      "statement": "Quel code implémente correctement le **tri par sélection** croissant ?",
      "options": [
        {
          "text": "```python\nfor i in range(len(liste)):\n    j_min = i\n    for j in range(i + 1, len(liste) - 1):\n        if liste[j] < liste[j_min]:\n            j_min = j\n    liste[i], liste[j_min] = liste[j_min], liste[i]\n```\n",
          "correct": false,
          "feedback": "Erreur : `len(liste) - 1` exclut le dernier\nélément de la recherche du minimum. S'il est\nle plus petit, il n'est jamais déplacé :\n`[2, 3, 1]` reste `[2, 3, 1]`.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste)):\n    j_min = i\n    for j in range(i + 1, len(liste)):\n        if liste[j] > liste[j_min]:\n            j_min = j\n    liste[i], liste[j_min] = liste[j_min], liste[i]\n```\n",
          "correct": false,
          "feedback": "Erreur : la comparaison est inversée\n(`>` au lieu de `<`) : la boucle interne\ncherche le **maximum**, ce code trie donc\nen ordre **décroissant**.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste)):\n    j_min = i\n    for j in range(i + 1, len(liste)):\n        if liste[j] < liste[j_min]:\n            j_min = j\n    liste[i], liste[j_min] = liste[j_min], liste[i]\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : double boucle ; la boucle\ninterne cherche l'indice du min ; échange\navec `liste[i]` à la fin.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste) - 1):\n    if liste[i] > liste[i + 1]:\n        liste[i], liste[i + 1] = liste[i + 1], liste[i]\n```\n",
          "correct": false,
          "feedback": "Erreur : une seule passe, ne trie pas\ncomplètement.\n"
        }
      ],
      "explanation": "Schéma classique : `j_min` mémorise l'indice\ndu minimum, échange à la fin pour minimiser\nles écritures."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "tri-insertion-code"
      ],
      "title": "Code tri par insertion",
      "statement": "Quel code implémente correctement le **tri par insertion** croissant ?",
      "options": [
        {
          "text": "```python\nfor i in range(len(liste)):\n    liste.append(min(liste))\n```\n",
          "correct": false,
          "feedback": "Erreur : on ajoute des éléments au lieu de\nréorganiser.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    j = i\n    while j > 0 and liste[j - 1] > liste[j]:\n        liste[j - 1], liste[j] = liste[j], liste[j - 1]\n        j -= 1\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : pour chaque élément à\npartir du second, on le décale vers la\ngauche tant qu'il est plus petit que son\nvoisin gauche.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    j = i\n    while j > 0 and liste[j - 1] > liste[j]:\n        liste[j - 1], liste[j] = liste[j], liste[j - 1]\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque le `j -= 1` dans le\n`while` : l'élément n'est remonté que\nd'une seule position, et le tri échoue\ndès qu'un élément doit remonter de plus\nd'une case (tester sur `[3, 4, 1]`).\n"
        },
        {
          "text": "```python\nfor i in range(len(liste)):\n    for j in range(i):\n        if liste[i] < liste[j]:\n            liste[i] = liste[j]\n```\n",
          "correct": false,
          "feedback": "Erreur : `liste[i] = liste[j]` écrase la\nvaleur sans la sauvegarder. La liste est\ncorrompue.\n"
        }
      ],
      "explanation": "Variante équivalente : sauvegarder\n`valeur = liste[i]`, décaler à droite les\néléments plus grands, insérer la valeur. Plus\néconome en échanges."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "presque-trie"
      ],
      "title": "Liste presque triée",
      "statement": "Entre le **tri par insertion** et le **tri par sélection**, lequel est le plus rapide sur une\nliste « presque triée » (peu d'éléments mal\nplacés) ?",
      "options": [
        {
          "text": "Les deux sont équivalents",
          "correct": false,
          "feedback": "Erreur : leur comportement diffère\nprécisément dans le cas favorable.\nL'insertion s'adapte aux données déjà\ntriées, la sélection non.\n"
        },
        {
          "text": "Le tri par sélection, car il effectue moins d'échanges",
          "correct": false,
          "feedback": "Erreur : la sélection fait certes peu\nd'échanges, mais son nombre de\n**comparaisons** reste quadratique quel\nque soit l'état de la liste. Sur une\nliste presque triée, l'insertion (coût\nquasi linéaire) l'emporte largement.\n"
        },
        {
          "text": "Impossible à prévoir sans connaître les valeurs",
          "correct": false,
          "feedback": "Erreur : pas besoin des valeurs, on\nraisonne sur les algorithmes : l'insertion\ns'arrête dès que l'élément est à sa place,\nla sélection balaie toujours tout le reste\nde la liste.\n"
        },
        {
          "text": "Tri par insertion",
          "correct": true,
          "feedback": "Bonne réponse : sur une liste presque\ntriée, le tri par insertion est en coût\nlinéaire (peu de décalages). C'est pour\ncela qu'on le préfère sur les petites\nlistes ou les listes presque triées.\n"
        }
      ],
      "explanation": "Cette propriété de l'insertion explique que\nPython (Timsort, hors programme) bascule sur\nle tri par insertion sur les petits sous-\ntableaux."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "invariant-selection"
      ],
      "title": "Invariant tri par sélection",
      "statement": "Quel **invariant** vérifie le tri par sélection\naprès l'itération `i` de la boucle externe ?",
      "options": [
        {
          "text": "La liste reste totalement inchangée par rapport au départ",
          "correct": false,
          "feedback": "Erreur : on a fait des échanges.\n"
        },
        {
          "text": "Les `i` premiers éléments sont les plus grands",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse pour un tri\ncroissant.\n"
        },
        {
          "text": "Les `i` premiers éléments sont triés et inférieurs aux suivants",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'invariant clé. À la\nfin (i = n), les n premiers (= toute la\nliste) sont triés.\n"
        },
        {
          "text": "La liste est entièrement triée",
          "correct": false,
          "feedback": "Trop fort : seules les `i` premières\npositions sont triées.\n"
        }
      ],
      "explanation": "Cet invariant rend la correction du tri par\nsélection évidente. Il combine deux conditions :\n« triés » + « plus petits que les autres »."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "terminaison-tri"
      ],
      "title": "Terminaison",
      "statement": "Pourquoi les tris par sélection et par\ninsertion **terminent** toujours ?",
      "options": [
        {
          "text": "Parce que chaque boucle possède un variant qui décroît strictement",
          "correct": true,
          "feedback": "Bonne réponse : un `for` sur un `range`\nfini termine automatiquement (variant\nimplicite). Pour le `while` du tri par\ninsertion, `j` décroît strictement à\nchaque tour et reste minoré par $0$, donc\nla boucle s'arrête en au plus `j`\nitérations.\n"
        },
        {
          "text": "Parce que Python limite les boucles à 1000 itérations",
          "correct": false,
          "feedback": "Erreur : Python n'impose aucune telle limite.\n"
        },
        {
          "text": "Parce qu'aucun de ces deux tris n'utilise de boucle while non bornée",
          "correct": false,
          "feedback": "Erreur : le tri par insertion utilise un\nwhile.\n"
        },
        {
          "text": "Parce que les listes Python sont toujours de taille finie",
          "correct": false,
          "feedback": "Vrai mais pas une justification suffisante :\nune boucle `while` peut tourner sans fin sur\nune liste finie ; il faut prouver que les\nboucles s'arrêtent.\n"
        }
      ],
      "explanation": "Justification rigoureuse : pour chaque boucle,\nidentifier ce qui change strictement à chaque\nitération (variant) et la borne qui sera\natteinte."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "trace-insertion"
      ],
      "title": "Trace tri par insertion",
      "statement": "On trie `[3, 1, 4, 1, 5]` par tri par\ninsertion croissant. Quel est l'état de la\nliste **après l'itération i = 3** ?",
      "options": [
        {
          "text": "`[3, 1, 4, 1, 5]`",
          "correct": false,
          "feedback": "Erreur : c'est la liste d'origine non\nmodifiée.\n"
        },
        {
          "text": "`[1, 1, 3, 4, 5]`",
          "correct": true,
          "feedback": "Bonne réponse. Trace : i=1 → `[1,3,4,1,5]` ;\ni=2 → `[1,3,4,1,5]` (le 4 ne bouge pas) ;\ni=3 → on insère le 1 (indice 3) à sa place\n: `[1,1,3,4,5]`.\n"
        },
        {
          "text": "`[1, 3, 4, 1, 5]`",
          "correct": false,
          "feedback": "Erreur : c'est l'état après i = 2\n(l'élément d'indice 2, le 4, est déjà à sa\nplace).\n"
        },
        {
          "text": "`[1, 3, 1, 4, 5]`",
          "correct": false,
          "feedback": "Erreur : le 1 doit aller jusqu'à l'indice\n1, pas à l'indice 2.\n"
        }
      ],
      "explanation": "Méthode : à chaque itération i, l'élément\n`liste[i]` rejoint la partie triée\n`liste[0..i-1]` (en Python : `liste[:i]`) à sa\nbonne place."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "echanges-selection"
      ],
      "title": "Nombre d'échanges sélection",
      "statement": "Combien d'échanges (au maximum) effectue le\ntri par sélection sur une liste de longueur n ?",
      "options": [
        {
          "text": "`log n`",
          "correct": false,
          "feedback": "Erreur : pas de mécanisme dichotomique.\n"
        },
        {
          "text": "`n - 1`",
          "correct": true,
          "feedback": "Bonne réponse : un échange par itération\nde la boucle externe. Quel que soit l'ordre\ninitial, on fait au plus n - 1 échanges\n(parfois on évite l'échange si j_min = i,\nmais pour le pire cas, c'est n - 1).\n"
        },
        {
          "text": "`n × (n - 1) / 2`",
          "correct": false,
          "feedback": "Erreur : c'est l'ordre de grandeur des\n**comparaisons**, pas des échanges.\n"
        },
        {
          "text": "`n`",
          "correct": false,
          "feedback": "Erreur : la dernière itération est inutile.\n"
        }
      ],
      "explanation": "Caractéristique remarquable du tri par\nsélection : très peu d'échanges, indépendamment\nde l'ordre initial. Avantageux quand les\nécritures sont coûteuses (mémoire flash)."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "comparaison-selection-insertion"
      ],
      "title": "Sélection vs insertion",
      "statement": "Sur une liste **déjà triée** de 1000 éléments,\nquel tri est le plus rapide ?",
      "options": [
        {
          "text": "Aucun ne fonctionne",
          "correct": false,
          "feedback": "Les deux algorithmes sont\nparfaitement corrects ; ils\nfiniront par trier la liste.\nLa question portait sur leur\nperformance comparée, et le\ntri par insertion est très\nrapide sur une liste presque\ntriée.\n"
        },
        {
          "text": "Le tri par sélection",
          "correct": false,
          "feedback": "Erreur : il fait quand même le coût\nquadratique (comparaisons), même si peu\nd'échanges.\n"
        },
        {
          "text": "Les deux sont équivalents",
          "correct": false,
          "feedback": "Erreur : différence d'ordre de grandeur sur\nce cas.\n"
        },
        {
          "text": "Le tri par insertion",
          "correct": true,
          "feedback": "Bonne réponse : sur une liste déjà triée,\nl'insertion est en coût linéaire (~ n\ncomparaisons, aucun décalage). La sélection\nreste quadratique.\n"
        }
      ],
      "explanation": "Choix selon le cas pratique : sélection si\nles écritures sont coûteuses (peu d'échanges),\ninsertion si les données arrivent presque\ntriées."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "tri-prealable"
      ],
      "title": "Trier puis chercher",
      "statement": "On veut faire **plusieurs recherches**\nd'éléments dans une liste. Pourquoi peut-il\nêtre intéressant de la **trier** d'abord ?",
      "options": [
        {
          "text": "Pour économiser une quantité importante de mémoire vive",
          "correct": false,
          "feedback": "Erreur : trier ne change pas la mémoire.\n"
        },
        {
          "text": "Aucun, le tri coûte plus cher que toutes les recherches",
          "correct": false,
          "feedback": "Au contraire, dès que l'on\nfait plusieurs recherches,\ntrier une fois pour toutes\ndevient très rentable, car\nchaque recherche passe d'un\ncoût linéaire à un coût\nlogarithmique.\n"
        },
        {
          "text": "Pour utiliser ensuite la recherche dichotomique plutôt que linéaire",
          "correct": true,
          "feedback": "Bonne réponse : le tri (n log n par tri\nfusion en Terminale, ou quadratique en\nPremière) est un investissement amorti par\nde nombreuses recherches rapides.\n"
        },
        {
          "text": "Pour rendre chaque recherche linéaire deux fois plus rapide",
          "correct": false,
          "feedback": "Erreur : trier ne change pas le coût d'une\nrecherche linéaire, qui reste\nproportionnel à la taille. Le bénéfice du\ntri préalable est de rendre la\n**dichotomie** applicable.\n"
        }
      ],
      "explanation": "Investissement initial (tri) vs économie sur\nles recherches futures. Pour une seule\nrecherche, le tri n'est pas rentable."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "bug-borne"
      ],
      "title": "Bug d'indice",
      "statement": "```python\nfor i in range(1, len(liste) + 1):\n    j = i\n    while j > 0 and liste[j - 1] > liste[j]:\n        liste[j - 1], liste[j] = liste[j], liste[j - 1]\n        j -= 1\n```\nPourquoi ce code (tentative de tri par\ninsertion) est-il **bogué** ?",
      "options": [
        {
          "text": "Sa complexité est trop élevée pour un tri par insertion",
          "correct": false,
          "feedback": "Erreur : la complexité (quadratique) est\ncelle attendue pour un tri par insertion ;\nle problème est ailleurs, dans une borne\nde boucle.\n"
        },
        {
          "text": "La dernière itération accède à `liste[len(liste)]`, qui n'existe pas",
          "correct": true,
          "feedback": "Bonne réponse : bug d'**off-by-one**. La\nborne externe est `len(liste) + 1` au lieu\nde `len(liste)` : au dernier tour,\n`i = len(liste)` puis `j = i`, et l'accès\n`liste[j]` lève un `IndexError`. La forme\ncorrecte est `range(1, len(liste))`.\n"
        },
        {
          "text": "La fonction `range` n'accepte pas deux paramètres",
          "correct": false,
          "feedback": "Erreur : la syntaxe `range(début, fin)` est\nparfaitement valide.\n"
        },
        {
          "text": "Il n'y a aucun bug, ce code trie correctement la liste",
          "correct": false,
          "feedback": "Le code contient bien une\nerreur de borne : la boucle\nva jusqu'à `len(liste)`\ninclus, ce qui provoque un\naccès hors tableau lors de\nla dernière itération.\n"
        }
      ],
      "explanation": "Bugs « off-by-one » : très fréquents.\nToujours tester avec des listes simples\n(`[]`, `[1]`, `[1, 2]`) pour les détecter."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "analyse-comparaisons"
      ],
      "title": "Nombre de comparaisons",
      "statement": "Combien de comparaisons effectue le tri par\n**sélection** sur une liste de longueur n,\nquel que soit l'ordre initial ?",
      "options": [
        {
          "text": "Environ n²/2 comparaisons (toujours)",
          "correct": true,
          "feedback": "Bonne réponse : la boucle externe fait n\nitérations, l'interne fait en moyenne n/2.\nTotal : n × n/2 = n²/2 comparaisons. **Le tri par sélection ne tire aucun parti**\nd'un ordre favorable : il fait toujours le\nmême travail.\n"
        },
        {
          "text": "Environ log n",
          "correct": false,
          "feedback": "Erreur : pas de dichotomie.\n"
        },
        {
          "text": "Environ n comparaisons",
          "correct": false,
          "feedback": "Erreur : la double boucle impose\nquadratique.\n"
        },
        {
          "text": "Aucune si la liste est déjà triée",
          "correct": false,
          "feedback": "Erreur : la sélection ne « voit » pas que la\nliste est triée. Elle parcourt quand même\ntout.\n"
        }
      ],
      "explanation": "Différence majeure : insertion s'adapte (cas\nfavorable linéaire), sélection ne s'adapte\npas. Pour de petites listes presque triées,\npréférer l'insertion."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "choix-tri"
      ],
      "title": "Choisir un tri",
      "statement": "Pour trier une liste de **5 éléments** dans un\nmicrocontrôleur où l'on doit **minimiser l'écriture en mémoire flash** (les écritures\nsont lentes), quel tri choisir parmi\nsélection et insertion ?",
      "options": [
        {
          "text": "Aucun, il faut un tri à bulles",
          "correct": false,
          "feedback": "Hors programme. Et sur le critère du\nnombre d'écritures, le tri par sélection\nest meilleur.\n"
        },
        {
          "text": "Tri par insertion",
          "correct": false,
          "feedback": "Erreur : peut faire jusqu'à n(n-1)/2 = 10\néchanges au pire.\n"
        },
        {
          "text": "Tri par sélection",
          "correct": true,
          "feedback": "Bonne réponse : il fait au plus n - 1 = 4\néchanges, indépendamment de l'ordre\ninitial. Optimal pour minimiser les\nécritures.\n"
        },
        {
          "text": "Aucun, on doit utiliser Timsort",
          "correct": false,
          "feedback": "Pas idéal sur micro-contrôleur : Timsort\nest complexe à implémenter et n'est pas\nforcément disponible.\n"
        }
      ],
      "explanation": "Le choix d'un algorithme dépend du **contexte matériel** et des **contraintes** (mémoire,\nénergie, écritures, temps). Pas d'algorithme\nuniversel."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes sur les tris\nau programme (sélection, insertion), laquelle\nest **fausse** ?",
      "options": [
        {
          "text": "Le coût au pire des deux est quadratique.",
          "correct": false,
          "feedback": "Vrai : caractéristique commune.\n"
        },
        {
          "text": "Les deux tris peuvent être implémentés en place (sans liste auxiliaire).",
          "correct": false,
          "feedback": "Vrai : caractéristique commune.\n"
        },
        {
          "text": "Le tri par sélection est stable par défaut.",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : le tri par\nsélection n'est **pas** stable en général.\nL'échange peut modifier l'ordre relatif\nd'éléments égaux. L'insertion, elle, est\nstable.\n"
        },
        {
          "text": "Le tri par insertion est en coût linéaire sur une liste déjà triée.",
          "correct": false,
          "feedback": "Vrai : sa propriété remarquable\n(meilleur cas).\n"
        }
      ],
      "explanation": "À retenir : sélection = peu d'échanges (n-1)\nmais pas stable, ne s'adapte pas au cas\nfavorable. Insertion = stable, excellent en\ncas favorable, plus d'échanges au pire."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "insertion-decalage"
      ],
      "title": "Tri par insertion par décalage",
      "statement": "Une variante classique du tri par insertion ne\nfait **pas** d'échanges deux à deux mais un\n**décalage** suivi d'une insertion. Quel code\nmet cette idée en œuvre correctement ?",
      "options": [
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    valeur = liste[i]\n    j = i\n    while j > 0 and liste[j - 1] > valeur:\n        liste[j] = liste[j - 1]\n        j -= 1\n```\n",
          "correct": false,
          "feedback": "Erreur : il manque la dernière étape\n`liste[j] = valeur`. La valeur sauvegardée\nn'est jamais réinsérée, et la position\nlibérée garde sa valeur précédente déjà\ndécalée. La liste est corrompue.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    valeur = liste[i]\n    for j in range(i, 0, -1):\n        liste[j] = valeur\n```\n",
          "correct": false,
          "feedback": "Erreur : on remplit toutes les positions de\n`j = i` jusqu'à `j = 1` avec la même\nvaleur. La liste finit par contenir des\ncopies multiples du même élément.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    valeur = liste[i]\n    j = i\n    while j > 0 and liste[j - 1] > valeur:\n        liste[j] = liste[j - 1]\n        j -= 1\n    liste[j] = valeur\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on sauvegarde la valeur\ncourante, on décale vers la droite tous les\néléments plus grands à gauche, puis on\ninsère la valeur à la position libérée. Une\nseule écriture par décalage (au lieu de deux\ndans la version par échanges), donc plus\nrapide en pratique.\n"
        },
        {
          "text": "```python\nfor i in range(1, len(liste)):\n    j = i\n    while j > 0 and liste[j - 1] > liste[j]:\n        liste[j] = liste[j - 1]\n        j -= 1\n```\n",
          "correct": false,
          "feedback": "Erreur : on n'a pas sauvegardé la valeur\nd'origine de `liste[i]` avant de la\nremplacer par `liste[j - 1]`. Dès la\npremière itération, cette valeur est\nperdue, donc l'insertion finale est\nimpossible.\n"
        }
      ],
      "explanation": "Cette variante est en général plus rapide en\npratique : un décalage = une seule écriture,\ncontre deux pour un échange. C'est ce que fait\nPython en interne pour les petits sous-tableaux\ndans Timsort."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "analyse-trace"
      ],
      "title": "Comparaisons précises sur exemple",
      "statement": "Combien de comparaisons (entre éléments)\neffectue le tri par **sélection** sur la liste\n`[5, 3, 8, 1]` (n = 4) ?",
      "options": [
        {
          "text": "4",
          "correct": false,
          "feedback": "Erreur : c'est le nombre d'éléments. La\ndouble boucle fait beaucoup plus de\ncomparaisons.\n"
        },
        {
          "text": "16",
          "correct": false,
          "feedback": "Erreur : ce serait `n²`, mais le tri par\nsélection fait `n(n - 1) / 2`\ncomparaisons, pas `n²`. La boucle interne\nne repart pas de zéro à chaque passe.\n"
        },
        {
          "text": "3",
          "correct": false,
          "feedback": "Erreur : ce serait le nombre d'**itérations**\nde la boucle externe (n - 1), pas le total\ndes comparaisons internes.\n"
        },
        {
          "text": "6",
          "correct": true,
          "feedback": "Bonne réponse : la formule générale est\n`(n - 1) + (n - 2) + ... + 1 = n(n - 1) / 2`,\nsoit ici `4 × 3 / 2 = 6`. Détail : passe 1\n→ 3 comparaisons (parcours indices 1 à 3) ;\npasse 2 → 2 ; passe 3 → 1. Total : 6.\n"
        }
      ],
      "explanation": "Formule à retenir : `n(n - 1) / 2`\ncomparaisons pour le tri par sélection, **quel que soit l'ordre initial**. C'est l'ordre de\ngrandeur quadratique. Pour n = 1000 :\n499 500 comparaisons."
    },
    {
      "id": "q29",
      "difficulty": 2,
      "skills": [
        "trace-selection-detaillee"
      ],
      "title": "Trace pas-à-pas du tri par sélection",
      "statement": "On trie la liste `[3, 1, 4, 2]` par tri par\nsélection croissant. Quels sont les états\nsuccessifs de la liste **après chaque itération de la boucle externe** (chaque\n« passe ») ?",
      "options": [
        {
          "text": "Après passe $1$ : `[1, 3, 4, 2]`. Après\npasse $2$ : `[1, 2, 4, 3]`. Après passe\n$3$ : `[1, 2, 3, 4]`\n",
          "correct": true,
          "feedback": "Bonne réponse : à chaque passe, on\ncherche le minimum à partir de la\nposition courante et on l'échange avec\nl'élément en cette position. Passe $1$\n(position $0$) : minimum de\n`[3, 1, 4, 2]` est $1$ en position\n$1$, on échange → `[1, 3, 4, 2]`.\nPasse $2$ (position $1$) : minimum de\n`[3, 4, 2]` est $2$ en position $3$,\non échange `liste[1]` avec `liste[3]`\n→ `[1, 2, 4, 3]`. Passe $3$ (position\n$2$) : minimum de `[4, 3]` est $3$,\non échange → `[1, 2, 3, 4]`. Une éventuelle\npasse $4$, sur le dernier élément seul, ne\nchange rien.\n"
        },
        {
          "text": "Après passe $1$ : `[3, 1, 4, 2]`\n(inchangée). Après passe $2$ :\n`[1, 3, 4, 2]`. Après passe $3$ :\n`[1, 2, 4, 3]`\n",
          "correct": false,
          "feedback": "Erreur : la première passe modifie\nbien la liste en plaçant le minimum\n$1$ en position $0$. Une passe ne\nlaisse la liste inchangée que si\nl'élément en position courante est\ndéjà le minimum du sous-tableau, ce\nqui n'est pas le cas ici (la position\n$0$ contient $3$, pas $1$).\n"
        },
        {
          "text": "Après passe $1$ : `[1, 3, 4, 2]`. Après\npasse $2$ : `[1, 3, 2, 4]`. Après passe\n$3$ : `[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur à la passe $2$ : à partir de la\nposition $1$, on cherche le minimum\nparmi `[3, 4, 2]`, qui est $2$ en\nposition $3$. On échange donc\n`liste[1]` (qui vaut $3$) avec\n`liste[3]` (qui vaut $2$), ce qui\ndonne `[1, 2, 4, 3]` et non\n`[1, 3, 2, 4]`.\n"
        },
        {
          "text": "Après passe $1$ : `[1, 2, 3, 4]`. Les\npasses suivantes ne changent rien\n",
          "correct": false,
          "feedback": "Erreur : le tri par sélection ne place\nqu'**un seul** élément à sa position\nfinale par passe (le minimum du\nsous-tableau restant). Il faut donc\nplusieurs passes successives, et la\nliste n'est triée qu'à la fin.\n"
        }
      ],
      "explanation": "Pour tracer le tri par sélection, on tient\nà jour à chaque passe : (1) la position\ncourante (position de la passe), (2)\nl'indice du minimum dans le sous-tableau\n`liste[pos:]`, (3) l'échange éventuel.\nLa liste se trie de gauche à droite, un\nélément à la fois. Pour `n = 4` éléments,\n$3$ passes suffisent (la dernière, sur un\nseul élément, est inutile)."
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "trace-insertion-detaillee"
      ],
      "title": "Trace pas-à-pas du tri par insertion",
      "statement": "On trie la même liste `[3, 1, 4, 2]` par\ntri par insertion croissant. Quels sont\nles états successifs **après chaque itération de la boucle externe** ?",
      "options": [
        {
          "text": "Après $i = 1$ : `[1, 3, 4, 2]`. Après\n$i = 2$ : `[1, 3, 4, 2]` (inchangée).\nAprès $i = 3$ : `[1, 2, 3, 4]`\n",
          "correct": true,
          "feedback": "Bonne réponse : à chaque itération $i$,\non prend `liste[i]` et on l'insère à sa\nplace dans la portion déjà triée\n`liste[0..i-1]` (en Python : `liste[:i]`).\nPour $i = 1$, le $1$\nremonte avant le $3$. Pour $i = 2$, le\n$4$ est déjà à sa place (rien à\nfaire). Pour $i = 3$, le $2$ remonte\njusqu'à se placer entre $1$ et $3$,\nce qui décale aussi le $3$ et le $4$\nd'un cran.\n"
        },
        {
          "text": "Après $i = 1$ : `[3, 1, 4, 2]`\n(inchangée). Après $i = 2$ :\n`[1, 3, 4, 2]`. Après $i = 3$ :\n`[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur : la première itération\n($i = 1$) insère le $1$ à sa place\ndans la portion `[3]`, ce qui le\nplace avant le $3$. La liste devient\ndonc `[1, 3, 4, 2]` après cette\nitération, pas inchangée.\n"
        },
        {
          "text": "Après $i = 1$ : `[1, 3, 4, 2]`. Après\n$i = 2$ : `[1, 2, 3, 4]`. Après\n$i = 3$ : `[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur : à $i = 2$, on insère le $4$,\npas le $2$. Le $4$ étant déjà à la\nfin de la portion triée `[1, 3]`, il\nn'a pas besoin d'être déplacé. La\nliste reste `[1, 3, 4, 2]`, pas\n`[1, 2, 3, 4]`. C'est à $i = 3$, en\ninsérant le $2$, qu'on obtient le\ntri final.\n"
        },
        {
          "text": "Identique au tri par sélection :\n`[1, 3, 4, 2]` puis `[1, 2, 4, 3]`\npuis `[1, 2, 3, 4]`\n",
          "correct": false,
          "feedback": "Erreur : les deux algorithmes ne\nproduisent pas les mêmes états\nintermédiaires. Au tri par sélection,\nchaque passe place le minimum du\nreste ; au tri par insertion, chaque\npasse insère l'élément suivant à sa\nplace dans la partie triée. Les\ncomportements à la passe $2$ diffèrent.\n"
        }
      ],
      "explanation": "Différence remarquable avec le tri par\nsélection : à $i = 2$, le tri par\ninsertion ne fait quasiment rien (juste\nune comparaison qui constate que $4 > 3$).\nC'est typique : si la liste est presque\ntriée, l'insertion devient quasi linéaire,\ntandis que la sélection fait toujours le\nmême travail quadratique. Cette propriété\nd'**adaptabilité** rend l'insertion\npréférable sur des données quasi-triées."
    },
    {
      "id": "q31",
      "difficulty": 2,
      "skills": [
        "tri-decroissant"
      ],
      "title": "Trier en ordre décroissant",
      "statement": "Quelle **modification du code** d'un tri par\ninsertion croissant le transforme en tri\n**décroissant**, sans étape supplémentaire ni\nrestriction sur le type des éléments ?",
      "options": [
        {
          "text": "Appliquer le tri croissant puis\ninverser la liste finale avec\n`liste.reverse()`\n",
          "correct": false,
          "feedback": "Cette approche fonctionne et coûte\njuste un $O(n)$ supplémentaire, mais\nce n'est pas une modification du tri :\nc'est une étape ajoutée après coup.\nModifier la comparaison interne est\nplus élégant et tout aussi efficace.\n"
        },
        {
          "text": "Trier la liste à l'envers d'abord, puis\nappliquer le tri croissant\n",
          "correct": false,
          "feedback": "Erreur : « trier à l'envers d'abord »\nn'a pas de sens si on n'a pas encore\nde tri. L'inversion d'une liste se\nfait en $O(n)$, certes, mais ce n'est\npas la modification la plus simple\npour adapter un tri.\n"
        },
        {
          "text": "Multiplier toutes les valeurs par\n$-1$, trier, puis multiplier à\nnouveau par $-1$\n",
          "correct": false,
          "feedback": "Cette astuce fonctionne pour des\nnombres, mais pas pour des chaînes ou\ndes objets quelconques. Et elle est\ninutilement compliquée par rapport à\nl'inversion de la comparaison.\n"
        },
        {
          "text": "Inverser le sens de la comparaison\nde la boucle interne (`<` au lieu\nde `>`)\n",
          "correct": true,
          "feedback": "Bonne réponse : la comparaison entre\ndeux éléments adjacents décide si on\nles permute. En écrivant\n`liste[j - 1] < liste[j]` au lieu de\n`liste[j - 1] > liste[j]`, on trie\ndans l'ordre opposé. C'est la\nmodification la plus simple et la\nplus localisée. Pour le tri par\nsélection, on cherche le **maximum**\nau lieu du minimum à chaque passe.\n"
        }
      ],
      "explanation": "Cette logique se généralise : pour un tri\navec un critère arbitraire (par âge, par\nnom, par taille, etc.), on n'a qu'à\nmodifier la **fonction de comparaison**.\nPython permet cela via le paramètre\n`key=` de `sorted` : avec une fonction clé\nnommée `def age(eleve): return eleve[1]`,\n`sorted(eleves, key=age)` trie des couples\n`(nom, age)` par âge croissant, et\n`sorted(eleves, key=age, reverse=True)`\npar âge décroissant."
    },
    {
      "id": "q32",
      "difficulty": 2,
      "skills": [
        "doublons-tri"
      ],
      "title": "Comportement avec doublons",
      "statement": "Que se passe-t-il quand on trie une liste\ncontenant des **doublons**, par exemple\n`[3, 1, 3, 2, 3]` ?",
      "options": [
        {
          "text": "Le tri renvoie `[1, 2, 3, 3, 3]`,\ntous les doublons étant conservés\n",
          "correct": true,
          "feedback": "Bonne réponse : les algorithmes de tri\nstandard fonctionnent parfaitement\navec des doublons. La distinction\nstable / non stable porte sur l'ordre\nrelatif des éléments **égaux** : un\ntri stable préserve l'ordre d'origine\ndes trois `3` (positions $0$, $2$,\n$4$). Pour des entiers, cette\ndistinction est invisible (un $3$\nvaut un $3$), mais pour des objets à\nclés composites (par exemple\n`(nom, age)` triés par `age`), la\nstabilité préserve l'ordre interne.\n"
        },
        {
          "text": "Les doublons sont supprimés et le\ntri renvoie `[1, 2, 3]`\n",
          "correct": false,
          "feedback": "Erreur : le tri ne supprime pas les\ndoublons. Il les conserve tous, dans\nl'ordre. Pour supprimer les doublons,\nil faut une étape distincte (par\nexemple un parcours qui n'ajoute chaque\nvaleur à une nouvelle liste que si elle\nn'y est pas déjà).\n"
        },
        {
          "text": "Les comparaisons d'éléments égaux\nfont boucler l'algorithme sans fin\n",
          "correct": false,
          "feedback": "Erreur : aucun risque de boucle\ninfinie. Les comparaisons restent\ndéterministes, les indices avancent\nnormalement.\n"
        },
        {
          "text": "Le tri lève une exception, les\néléments égaux étant incomparables\n",
          "correct": false,
          "feedback": "Erreur : deux valeurs égales sont\nparfaitement comparables. Les\nalgorithmes de tri sont conçus pour\nfonctionner avec n'importe quelle\nsuite de valeurs comparables,\ndoublons compris.\n"
        }
      ],
      "explanation": "Test concret : trier\n`[(\"Alice\", 17), (\"Bob\", 17), (\"Chloé\", 15)]`\npar âge avec un tri stable donne\n`[(\"Chloé\", 15), (\"Alice\", 17), (\"Bob\", 17)]`\n(Alice avant Bob, comme dans la liste\nd'origine). Avec un tri non stable, on\npourrait obtenir aussi\n`[(\"Chloé\", 15), (\"Bob\", 17), (\"Alice\", 17)]`\n(Bob avant Alice), ce qui peut être\ngênant en pratique."
    }
  ]
}