Épreuve écrite · 2026 · Jour 1

Bac NSI 2026 Polynésie jour 1

Ce sujet met la récursivité à l’épreuve de trois usages : calculer une suite sans répéter inutilement le travail, retrouver un contact dans un arbre ordonné et programmer un petit chatbot. Les 32 questions sont reprises avec leurs données, leurs programmes et des explications sur les subtilités du code fourni. Durée : 3 h 30, sans calculatrice ; les trois exercices indépendants sont à traiter.

Dans ce sujet

Un corrigé pédagogique pour comprendre et justifier vos réponses. Les conseils de rédaction ne constituent pas un barème officiel détaillé.

Exercice 1 · 6 points

Fibonacci : mesurer le prix des calculs répétés

On définit U₀ = 0, U₁ = 1 et Uₙ = Uₙ₋₁ + Uₙ₋₂ pour n ≥ 2. Les premiers termes sont 0, 1, 1, 2, 3, 5. L’exercice compare trois façons de calculer le même terme : récursion directe, mémorisation et itération.

def suite(n):
    if n < 2:
        valeur = ...
    else:
        valeur = suite(n - 1) + suite(...)
    return ...

Question 1

#

Compléter la fonction récursive suite(n) qui renvoie Uₙ.

Indice

Une même instruction peut couvrir les deux cas U₀ et U₁.

Comprendre la correction
def suite(n):
    if n < 2:
        valeur = n
    else:
        valeur = suite(n - 1) + suite(n - 2)
    return valeur

Pour n = 0 ou 1, la valeur est n. Pour n ≥ 2, la relation de récurrence est traduite directement. Les appels diminuent jusqu’à l’un des deux cas de base. On suppose n entier naturel, comme pour l’indice de la suite.

Pour suite(4), le premier appel demande suite(3) et suite(2). À son tour, suite(3) redemande suite(2), ce qui illustre déjà un calcul répété. Les cas de base renvoient les valeurs de la suite et ne font pas d’addition entre termes. La terminaison s’appuie sur la diminution stricte de n, pour un indice naturel.

Voir la question dans le sujet PDF, p. 2 (nouvel onglet)

On donne p(0) = p(1) = 0 et p(n) = 1 + p(n - 1) + p(n - 2) pour n ≥ 2.

Question 2

#

Écrire nb_additions_suite(n) qui renvoie p(n), le nombre d’additions effectuées par suite.

Indice

Chaque appel non terminal de suite contient une addition.

Comprendre la correction
def nb_additions_suite(n):
    if n < 2:
        return 0
    return 1 + nb_additions_suite(n - 1) + nb_additions_suite(n - 2)

L’addition des deux termes de la suite compte pour 1 ; il faut y ajouter les additions des deux sous-appels. Ce programme calcule le décompte p(n). Ses propres additions ne sont pas celles que l’on mesure dans suite : on applique simplement la récurrence fournie.

Voir la question dans le sujet PDF, p. 2 (nouvel onglet)

Question 3

#

Calculer p(2), p(4) et p(7).

Indice

Construisez les termes de p dans l’ordre jusqu’à l’indice 7.

Comprendre la correction
n01234567
p(n)0012471220

p(2) = 1, p(4) = 4 et p(7) = 20. Par exemple p(4) = 1 + p(3) + p(2) = 1 + 2 + 1 ; p(7) = 1 + 12 + 7. Calculer les valeurs successives évite de redessiner tout l’arbre d’appels.

Voir la question dans le sujet PDF, p. 2 (nouvel onglet)
def suite2(n, valeurs_calculees=[0, 1]):
    if n < len(valeurs_calculees):
        valeur = valeurs_calculees[n]
    else:
        valeurs_calculees.append(
            suite2(n - 1, valeurs_calculees)
            + suite2(n - 2, valeurs_calculees))
    return valeurs_calculees[n]
nNombre d’additions
21
32
43
5À compléter
6À compléter
7À compléter

Question 4

#

Compléter le tableau du nombre d’additions de suite2 pour n = 5, 6, 7.

Indice

Différenciez le coût depuis un cache neuf du coût d’un appel utilisant un cache déjà rempli.

Comprendre la correction
nAdditions à cache initial [0, 1]
21
32
43
54
65
76

À partir d’un cache neuf [0, 1], chaque terme d’indice 2 à n n’est calculé qu’une fois, soit n - 1 additions. Les valeurs manquantes sont donc 4, 5, 6. Le paramètre par défaut mutable du code conserve cependant les résultats entre appels : si on lance successivement suite2(2), suite2(3), etc. sans réinitialiser la liste, chaque nouvel indice ne demande qu’une addition. Le tableau du sujet suppose des calculs isolés, chacun avec le cache initial.

Une façon simple de lever l’ambiguïté lors d’un test consiste à appeler suite2(n,[0,1]) avec une nouvelle liste explicitement fournie pour chaque n. Ainsi le tableau compare des conditions initiales identiques. Pour éviter le partage implicite d’un paramètre mutable, une version d’application pourrait utiliser un paramètre par défaut None et créer [0,1] dans la fonction lorsque le cache n’est pas fourni.

Voir la question dans le sujet PDF, p. 2, 3 (nouvel onglet)

Question 5

#

Pourquoi suite2 est-elle plus efficace ? Nommer le procédé utilisé.

Indice

La liste évite que suite2(n - 2) refasse le travail accompli dans suite2(n - 1).

Comprendre la correction

La mémoïsation, technique de programmation dynamique, conserve chaque terme calculé dans valeurs_calculees. Un terme déjà connu se lit directement au lieu d’être recalculé. La récursion naïve répète les mêmes sous-problèmes et son coût croît exponentiellement ; le nombre d’additions de la version mémorisée croît linéairement à cache neuf. Par exemple U₇ exige 20 additions naïves, contre 6 mémorisées.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 6

#

Proposer une version itérative non récursive suite3.

Indice

Deux termes consécutifs suffisent pour produire le suivant.

Comprendre la correction
def suite3(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

Au début de l’itération k, a = Uₖ et b = Uₖ₊₁. L’affectation simultanée calcule les nouvelles valeurs avec les anciennes. Après n tours, a = Uₙ. Pour n = 0, la boucle ne s’exécute pas et renvoie bien 0. Cette version fait n additions ; on pourrait réduire à n - 1 pour n ≥ 2 avec un traitement séparé des cas de base.

Avant l’itérationab
001
111
212
323
435

Une affectation séquentielle a=b puis b=a+b n’aurait pas le même effet, car le second calcul lirait le nouveau a. L’affectation simultanée évalue d’abord les deux expressions avec les anciennes valeurs. Une variable temporaire serait une autre solution correcte. Le tableau montre qu’après quatre tours, a contient bien U₄=3.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 7

#

Pourquoi préférer la version itérative pour calculer un terme quelconque ?

Indice

Comparer seulement les valeurs renvoyées ne suffit pas : regardez la pile et le stockage.

Comprendre la correction

Elle conserve seulement deux valeurs au lieu d’une liste de tous les termes et évite l’empilement des appels récursifs, donc le risque d’atteindre la limite de récursion de Python. Par rapport à suite2, son ordre de temps reste linéaire en nombre d’additions, mais le nombre de variables de travail est constant. Les entiers eux-mêmes grandissent avec n : « mémoire constante » désigne ici le nombre de cases, pas un nombre constant de bits.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)
Combien de travail pour un seul nombre ?Un atelier pour expérimenter

Faites varier n puis le plus grand indice déjà mémorisé. Comparez les additions de la récursion naïve au coût réellement restant pour une version avec cache.

Lire le résultat de l’expérience initiale

U7 = 13

La version naïve refait les mêmes appels. Le cache contient tous les indices de 0 jusqu’à la borne sélectionnée : seules les cases manquantes demandent une addition.

IndiceTermeAdditions naïvesStatut du cache
000Déjà connu
110Déjà connu
211À calculer
322À calculer
434À calculer
557À calculer
6812À calculer
71320À calculer

Un coût dépend de l’état initial de la mémoire. Pour comparer deux algorithmes, annoncez si leur cache est neuf ou déjà rempli.

Revoir les notions de cet exercice

Exercice 2 · 6 points

Annuaire téléphonique : construire et parcourir un ABR

Des étudiants développent une application console d’annuaire. Le fichier de contacts fictifs est représenté par la variable suivante ; chaque prénom est associé à un numéro de téléphone. L’application doit ajouter, rechercher et afficher les contacts par ordre alphabétique.

contacts_fictifs = [
    {"Olivier": "01 23 45 67 89"},
    {"Benjamin": "06 12 34 56 78"},
    {"Sophie": "02 34 56 78 90"},
    {"Antoine": "09 87 65 43 21"},
    {"Eric": "03 45 67 89 01"},
    {"Roxanne": "07 45 67 89 01"},
    {"Victor": "03 56 78 90 12"},
    {"Emilie": "04 23 45 67 89"},
    {"Yann": "01 54 32 10 98"},
    {"Zoé": "05 23 45 67 89"}
]

Un ABR stocke les contacts, sans clés en double. La hauteur d’un arbre réduit à un nœud est 1. Arbre fourni :

                   Olivier
                 /         \
           Benjamin        Sophie
           /      \        /    \
       Antoine    Eric  Roxanne  Victor
                  /                 \
                Emilie              Yann
                                      \
                                      Zoé

Question 1

#

Quelle est la structure de données de contacts_fictifs ?

Indice

Les crochets extérieurs et les accolades intérieures désignent deux structures distinctes.

Comprendre la correction

C’est une liste de dictionnaires. Chaque dictionnaire contient une seule association : une clé de type chaîne (le prénom) et une valeur de type chaîne (le téléphone). Les numéros sont des textes pour conserver leurs zéros initiaux et leurs espaces.

Voir la question dans le sujet PDF, p. 4 (nouvel onglet)

Question 2

#

Justifier que l’arbre est un ABR pour l’ordre alphabétique.

Indice

La propriété porte sur tout le sous-arbre, pas seulement sur un enfant.

Comprendre la correction

Pour chaque nœud, tous les prénoms du sous-arbre gauche le précèdent et tous ceux du sous-arbre droit le suivent. Ainsi Antoine, Benjamin, Emilie et Eric précèdent Olivier ; Roxanne, Sophie, Victor, Yann et Zoé le suivent. La propriété est aussi vraie à l’intérieur : Emilie précède Eric, tandis qu’Eric suit Benjamin. Vérifier seulement les deux enfants de la racine ne suffirait pas.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)

Question 3

#

Donner la racine puis l’ensemble des feuilles.

Indice

Une feuille n’a ni enfant gauche ni enfant droit.

Comprendre la correction

Racine : Olivier. Feuilles : Antoine, Emilie, Roxanne, Zoé, car elles n’ont aucun enfant. Eric et Yann ne sont pas des feuilles même s’ils n’ont qu’un enfant.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)

Question 4

#

Donner hauteur et taille.

Indice

Ne comptez pas les arêtes pour une hauteur dont la feuille vaut 1.

Comprendre la correction

Hauteur 5 ; taille 10. Le plus long chemin est Olivier → Sophie → Victor → Yann → Zoé, soit cinq nœuds avec la convention du sujet. La taille compte tous les nœuds, un par contact.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)

Question 5

#

Quel avantage présente un ABR par rapport à contacts_fictifs ?

Indice

Comparez le nombre de contacts inspectés pour chercher Zoé ou Emilie.

Comprendre la correction

La recherche suit une seule branche, en éliminant à chaque comparaison un sous-arbre entier. Son coût dépend de la hauteur h, soit O(h), contre O(n) pour rechercher un prénom dans la liste non triée de petits dictionnaires. Un ABR équilibré donne O(log n), mais un ABR dégénéré peut encore demander O(n). L’ordre n’assure pas à lui seul l’équilibrage.

Dans cet arbre, chercher Emilie compare Olivier, Benjamin, Eric puis Emilie : quatre nœuds sont visités, alors qu’une recherche séquentielle dans la liste de départ atteint ce contact en huitième position. Cet exemple montre le gain possible sans en faire une garantie universelle. Insérer des clés déjà triées dans un ABR non équilibré créerait une longue chaîne et ferait disparaître le gain logarithmique.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)

Question 6

#

Donner le sous-arbre droit de Benjamin.

Indice

Suivez le lien droit de Benjamin puis conservez tout ce qui en descend.

Comprendre la correction
    Eric
    /
 Emilie

Il contient Eric comme racine et Emilie comme enfant gauche. Le sous-arbre inclut les descendants, pas seulement le fils direct.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)
class Contact:
    def __init__(self, v_prenom, v_num_tel):
        self.prenom = v_prenom
        self.num_tel = v_num_tel

class Noeud:
    def __init__(self, v_contact, v_gauche=None, v_droite=None):
        self.contact = v_contact
        self.gauche = v_gauche
        self.droite = v_droite

Instruction A :

abr = Noeud(Contact('Benjamin', '06 12 34 56 78'),
    Noeud(Contact('Antoine', '09 87 65 43 21')),
    Noeud(Contact('Emilie', '04 23 45 67 89'),
        Noeud(Contact('Eric', '03 45 67 89 01'))))

Instruction B :

abr = Noeud(Contact('Benjamin', '06 12 34 56 78'),
    Noeud(Contact('Antoine', '09 87 65 43 21')),
    Noeud(Contact('Eric', '03 45 67 89 01'),
        Noeud(Contact('Emilie', '04 23 45 67 89'))))

Instruction C :

abr = Noeud(Noeud(Contact('Benjamin', '06 12 34 56 78'),
    ('Antoine', '09 87 65 43 21')),
    Noeud(Contact('Eric', '03 45 67 89 01'),
        ('Emilie', '04 23 45 67 89')))

Question 7

#

Parmi A, B et C, choisir l’instruction qui construit le sous-arbre de racine Benjamin.

Indice

Le second argument de Noeud est son enfant gauche ; le troisième son enfant droit.

Comprendre la correction

B. Benjamin reçoit Antoine à gauche et Eric à droite ; Eric reçoit Emilie à gauche. A inverse Eric et Emilie. C mélange des tuples et des nœuds à des emplacements où le constructeur attend un Contact et des sous-arbres.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)
class ContactManager:
    def __init__(self):
        self.root = None

    def inserer_contact(self, contact):
        self.root = self.inserer_recursive(self.root, contact)

    def inserer_recursive(self, noeud, contact):
        if noeud is None:
            return Noeud(contact)
        if contact.prenom < noeud.contact.prenom:
            noeud.gauche = self.inserer_recursive(noeud.gauche, contact)
        else:
            ...
        return noeud

    def trouver_contact(self, prenom):
        return self.trouver_recursive(self.root, prenom)

    def trouver_recursive(self, noeud, prenom):
        if noeud is None:
            return None
        ...
            return noeud.contact.num_tel
        elif prenom < noeud.contact.prenom:
            ...
        else:
            return self.trouver_recursive(noeud.droite, prenom)

Question 8

#

Compléter les lignes 14, 23 et 26 de ContactManager.

Indice

Le résultat d’un appel récursif doit remonter jusqu’à l’appel initial.

Comprendre la correction
noeud.droite = self.inserer_recursive(noeud.droite, contact)  # 14
if prenom == noeud.contact.prenom:                         # 23
    return noeud.contact.num_tel
elif prenom < noeud.contact.prenom:
    return self.trouver_recursive(noeud.gauche, prenom)    # 26

L’insertion doit réaffecter le sous-arbre renvoyé, notamment lorsqu’un emplacement vide reçoit un nouveau nœud. La recherche teste l’égalité avant de choisir une branche et transmet avec return le résultat du sous-appel. Le code suppose, comme annoncé, que les prénoms insérés sont distincts ; sans cette précondition, le else d’insertion accepterait un doublon à droite.

Pour insérer Emilie, la comparaison avec Olivier choisit gauche ; avec Benjamin, elle choisit droite ; avec Eric, elle choisit gauche. Cet enfant est None, donc on crée le nœud. Les affectations successives replacent les racines des sous-arbres renvoyés à leur bon emplacement. Pour une recherche absente comme Adrien, on descend jusqu’à un None, puis ce résultat remonte inchangé grâce aux return.

Voir la question dans le sujet PDF, p. 6 (nouvel onglet)
contact_manager = ContactManager()
contact_manager.inserer_contact(Contact("Olivier", "01 23 45 67 89"))
contact_manager.inserer_contact(Contact("Benjamin", "06 12 34 56 78"))
contact_manager.inserer_contact(Contact("Sophie", "02 34 56 78 90"))
contact_manager.inserer_contact(Contact("Antoine", "09 87 65 43 21"))
contact_manager.inserer_contact(Contact("Eric", "03 45 67 89 01"))
contact_manager.inserer_contact(Contact("Roxanne", "07 45 67 89 01"))
contact_manager.inserer_contact(Contact("Victor", "03 56 78 90 12"))
contact_manager.inserer_contact(Contact("Emilie", "04 23 45 67 89"))
contact_manager.inserer_contact(Contact("Yann", "01 54 32 10 98"))
contact_manager.inserer_contact(Contact("Zoé", "05 23 45 67 89"))
recherche_contact("Roxanne")
Prénom recherché : Roxanne
Roxanne : 07 45 67 89 01

recherche_contact("Adrien")
Prénom recherché : Adrien
Contact 'Adrien' introuvable

Question 9

#

Écrire recherche_contact(prenom_recherche) qui utilise contact_manager et reproduit les affichages demandés.

Indice

La méthode trouver_contact renvoie soit un numéro, soit None.

Comprendre la correction
def recherche_contact(prenom_recherche):
    print("Prénom recherché :", prenom_recherche)
    numero = contact_manager.trouver_contact(prenom_recherche)
    if numero is None:
        print("Contact '" + prenom_recherche + "' introuvable")
    else:
        print(prenom_recherche + " : " + numero)

La recherche est appelée une seule fois et son résultat est conservé. On distingue explicitement None, marqueur d’absence, d’un numéro stocké sous forme de chaîne. Cette fonction affiche ; elle n’a pas besoin de renvoyer le numéro.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)
print("Liste des contacts triés :")
affichage_contacts_trie(contact_manager.root)

Affichage attendu :

Liste des contacts triés :
Antoine : 09 87 65 43 21
Benjamin : 06 12 34 56 78
Emilie : 04 23 45 67 89
Eric : 03 45 67 89 01
Olivier : 01 23 45 67 89
Roxanne : 07 45 67 89 01
Sophie : 02 34 56 78 90
Victor : 03 56 78 90 12
Yann : 01 54 32 10 98
Zoé : 05 23 45 67 89

Question 10

#

Choisir et justifier le parcours pour afficher les prénoms par ordre alphabétique : largeur, profondeur préfixe, infixe ou postfixe.

Indice

Le nœud doit être affiché entre les deux sous-arbres.

Comprendre la correction

Le parcours en profondeur infixe : sous-arbre gauche, nœud, sous-arbre droit. Les clés de gauche sont plus petites que le nœud et celles de droite plus grandes. En appliquant récursivement le même ordre, on obtient toute la liste triée.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)
def affichage_contacts_trie(noeud):
    """Afficher les contacts triés par nom."""
    if noeud is not None:
        ...
        ...
        affichage_contacts_trie(noeud.droite)

Question 11

#

Compléter les lignes 4 et 5 de affichage_contacts_trie.

Indice

Le squelette possède déjà le troisième temps du parcours infixe.

Comprendre la correction
affichage_contacts_trie(noeud.gauche)
print(noeud.contact.prenom + " : " + noeud.contact.num_tel)

Le cas None ne produit aucun affichage. Pour chaque nœud non vide, on traite à gauche, affiche son contact puis utilise l’appel à droite déjà fourni. Les numéros restent associés à leurs prénoms : on ne trie pas séparément les deux listes.

Sur le sous-arbre Benjamin, l’exécution descend d’abord sur Antoine, l’affiche, revient afficher Benjamin, puis visite le sous-arbre Eric. Dans celui-ci, Emilie est affichée avant Eric. On obtient donc Antoine, Benjamin, Emilie, Eric. Le cas None se contente de revenir à l’appel précédent : il représente une branche absente et ne doit rien afficher.

Voir la question dans le sujet PDF, p. 7, 8 (nouvel onglet)
Chercher un prénom sans visiter tout l’annuaireUn atelier pour expérimenter

Choisissez un prénom présent ou absent. Le tableau montre chaque comparaison effectuée depuis Olivier et la branche choisie. Comparez cette recherche à un balayage des dix contacts.

Lire le résultat de l’expérience initiale

Contact trouvé

La recherche s’arrête dès l’égalité.

NœudComparaisonDécision
OlivierEmilie < OlivierGauche
BenjaminEmilie > BenjaminDroite
EricEmilie < EricGauche
EmilieÉgalitéTrouvé

Le coût d’une recherche suit la hauteur de la branche réellement parcourue, pas nécessairement le nombre total de contacts.

Revoir les notions de cet exercice

Exercice 3 · 8 points

Chatbot : dictionnaire, SQL et recherche dans un arbre

La start-up fictive OpenChat développe un chatbot qui stocke des questions et leurs réponses. On commence par une classe Python munie d’un dictionnaire, puis on utilise une table SQL et enfin un arbre de mots-clés. Ces trois représentations servent le même besoin mais ne s’utilisent pas de la même façon.

Partie 1 : structures de données

Chatbot possède un attribut base_donnees initialement vide. ajouter_question_reponse y mémorise une réponse ; repondre récupère la réponse associée à une question.

class Chatbot:
    def __init__(self):
        self.base_donnees = ...

    def ajouter_question_reponse(self, question, reponse):
        self.base_donnees[question] = ...

    def repondre(self, question):
        return self.base_donnees[question]

Question 1

#

Compléter les lignes 3 et 6 de Chatbot.

Indice

La question est la clé et la réponse sa valeur.

Comprendre la correction
self.base_donnees = {}
# Dans ajouter_question_reponse :
self.base_donnees[question] = reponse

Les accolades vides créent un dictionnaire, pas une liste. Une affectation à une clé ajoute une association ou remplace sa valeur si cette même question existait déjà.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)

Question 2

#

Instancier Bot1 puis ajouter la question « Bonjour, comment vas-tu ? » et la réponse « Bonjour, je vais bien, merci. ».

Indice

Les parenthèses après Chatbot créent réellement un objet.

Comprendre la correction
Bot1 = Chatbot()
Bot1.ajouter_question_reponse(
    "Bonjour, comment vas-tu ?",
    "Bonjour, je vais bien, merci.")

L’appel au constructeur crée l’instance. La méthode agit ensuite sur cette instance : Python transmet automatiquement Bot1 comme self.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)

Question 3

#

Obtenir la réponse à « Bonjour, comment vas-tu ? ».

Indice

Utilisez la méthode qui renvoie une valeur, puis affichez cette valeur si nécessaire.

Comprendre la correction
reponse = Bot1.repondre("Bonjour, comment vas-tu ?")
print(reponse)

La réponse renvoyée est Bonjour, je vais bien, merci.. La recherche est exacte : le programme ne comprend pas les variantes de formulation et l’accès à une question absente provoquerait KeyError. Ajouter une réponse par défaut serait une amélioration distincte de la consigne.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)

Partie 2 : base de données

La table questions_reponses comporte les colonnes id, question, reponse, mot_cle. Les mots-clés disponibles incluent AND, FROM, INSERT, INTO, JOIN, OR, ON, SELECT, SET, UPDATE, VALUES et WHERE.

Question 4

#

Associer SELECT, INSERT et UPDATE à : ajouter une donnée, extraire des données, mettre à jour une table.

Indice

Pensez à lire, ajouter, modifier.

Comprendre la correction
Mot-cléAction
SELECTExtraire des données
INSERTAjouter une donnée
UPDATEMettre à jour les lignes d’une table

SELECT consulte sans modifier. INSERT crée une ligne. UPDATE modifie des lignes existantes, généralement sélectionnées par WHERE.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 5

#

Définir une clé primaire.

Indice

L’unicité et l’absence de valeur NULL sont les deux propriétés à citer.

Comprendre la correction

Une clé primaire est un attribut ou un ensemble d’attributs choisi pour identifier de façon unique chaque ligne d’une table. Ses valeurs ne peuvent pas être nulles et deux lignes ne peuvent pas partager la même valeur de clé. Une clé composée s’interprète comme un ensemble, pas comme plusieurs identifiants séparés.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 6

#

Quel attribut ou ensemble d’attributs convient le mieux comme clé primaire de questions_reponses ? Justifier.

Indice

Le mot-clé sert à classer plusieurs interactions, pas à en identifier une seule.

Comprendre la correction

id, à condition qu’il soit attribué de façon unique et non nulle. Il identifie une interaction indépendamment de son texte. Un même mot-clé peut concerner plusieurs questions ; une réponse comme « Oui » peut aussi se répéter. L’identifiant reste stable si le texte est corrigé.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 7

#

Récupérer toutes les questions dont le mot-clé est python.

SQLFiltrer le mot-clé du chatbotÉcrivez votre solution et mettez-la à l’épreuve

Renvoyez toutes les questions classées sous le mot-clé exactement égal à python. Le texte de la question peut contenir ou ne pas contenir ce mot.

SELECT ...
FROM ...
WHERE ...;

Les cas de test proposés :

  • Jeu pédagogique et entrée historique citée : L’entrée 15 sur Marignan est celle demandée plus loin dans le sujet. Les deux autres lignes sont des compléments pédagogiques qui distinguent classement par mot-clé et recherche dans le texte.
  • Cas complémentaire : projection avec répétitions : Jeu complémentaire pédagogique, distinct des données officielles. Les identifiants distinguent les lignes même si leurs textes sont identiques. Le mot-clé Python avancé ne vaut pas python.
Indice

La colonne à filtrer et la colonne à afficher ne sont pas les mêmes.

Comprendre la correction
SELECT question
FROM questions_reponses
WHERE mot_cle = 'python';

On compare le champ de classement au mot-clé demandé. On ne recherche pas le mot python à l’intérieur du texte de la question, car ce n’est pas ce que demande la consigne.

Le résultat SELECT question est une projection : même si la table contient identifiant, réponse et mot-clé, seuls les textes de questions apparaissent. WHERE est appliqué au champ mot_cle. Une question contenant le mot Python mais classée sous un autre mot-clé ne serait pas retenue par cette requête, conformément au besoin formulé.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 8

#

Insérer id 15, question « Date de la bataille de Marignan ? », réponse « 1515 », mot-clé « Histoire ».

Indice

Vérifiez qu’aucun des quatre champs n’a été oublié.

Comprendre la correction
INSERT INTO questions_reponses (id, question, reponse, mot_cle)
VALUES (15, 'Date de la bataille de Marignan ?', '1515', 'Histoire');

15 identifie l’enregistrement ; « 1515 » est le texte de la réponse. Écrire les colonnes rend explicite l’association de chaque valeur et évite de dépendre implicitement de l’ordre physique du schéma.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 9

#

Mettre à jour la réponse de l’enregistrement d’id 12 pour qu’elle devienne « Oui ».

Indice

UPDATE modifie une ligne existante ; ce n’est pas une insertion.

Comprendre la correction
UPDATE questions_reponses
SET reponse = 'Oui'
WHERE id = 12;

La condition cible une seule interaction par son identifiant. Sans WHERE, toutes les réponses deviendraient « Oui ». On ne modifie ni la question ni son mot-clé.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Partie 3 : arbre de mots-clés

Le sujet fournit cet arbre, présenté comme un ABR pour l’ordre alphabétique :

                 IA
              /      \
            bot     programmation
           /   \         /       \
        algo chatbot informatique python

Question 10

#

Quel parcours donne IA, bot, programmation, algo, chatbot, informatique, python ?

Indice

L’ordre visite d’abord tous les nœuds à une même profondeur.

Comprendre la correction

Le parcours en largeur, de gauche à droite dans chaque niveau : d’abord la racine, ensuite ses enfants, puis les quatre petits-enfants.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 11

#

Donner l’ordre du parcours en profondeur infixe sur cet arbre.

Indice

Appliquez gauche-nœud-droite récursivement.

Comprendre la correction

algo, bot, chatbot, IA, informatique, programmation, python. On visite gauche, racine, droite à chaque nœud. Le sous-arbre de bot est donc entièrement visité avant IA.

Pour ne pas se perdre, appliquez la règle à un seul sous-arbre : le nœud bot produit algo, bot, chatbot. Le nœud programmation produit informatique, programmation, python. La racine IA se place entre ces deux groupes. Les parcours se déterminent par la structure des liens ; il n’est pas nécessaire de comparer les mots pendant un parcours.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 12

#

Donner l’ordre du parcours en profondeur postfixe ou suffixe.

Indice

Pour chaque petit arbre, écrivez ses deux feuilles avant sa racine.

Comprendre la correction

algo, chatbot, bot, informatique, python, programmation, IA. Cette fois, chaque racine est visitée après ses deux sous-arbres : gauche, droite, nœud. IA est donc le dernier élément.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Question 13

#

Quel parcours donne les mots-clés dans l’ordre alphabétique ?

Indice

La propriété infixe-trié suppose que l’arbre et la recherche utilisent exactement le même ordre.

Comprendre la correction

Le parcours infixe, puisque l’arbre est déclaré ordonné alphabétiquement. Attention à la majuscule de IA : l’ordre alphabétique usuel de l’exercice ignore ici la casse. Python compare directement les chaînes selon les points de code Unicode, où "IA" < "algo". Avec cette comparaison native, le dessin ne serait pas un ABR valide. Pour réaliser le modèle alphabétique annoncé, on peut normaliser les clés avec casefold() au moment des comparaisons.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)
class Noeud:
    def __init__(self, cle):
        self.cle = cle
        self.gauche = None
        self.droit = None

    def recherche(arbre, cle):
        if arbre is None or arbre.cle == ...:
            return arbre
        if cle < arbre.cle:
            return recherche(arbre. ..., cle)
        else:
            return recherche(arbre. ..., cle)

Question 14

#

Compléter la recherche dans l’ABR aux lignes 9, 12 et 14.

PythonTrouver le nœud du chatbot sans explorer tout l’arbreÉcrivez votre solution et mettez-la à l’épreuve

Écrivez recherche(arbre, cle), qui renvoie le nœud portant la clé ou None. La fonction est placée hors de la classe pour être directement exécutable. Pour respecter l’ordre alphabétique sans casse de l’arbre imprimé (racine IA), toutes les comparaisons utilisent casefold, à l’insertion comme à la recherche. Les enfants s’appellent gauche et droit. Renvoyez le nœud lui-même, pas seulement sa chaîne.

def recherche(arbre, cle):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Un arbre vide : Le cas vide doit être traité avant l’accès à l’attribut cle.
  • La racine, quelle que soit la casse : La racine est écrite IA dans le sujet, et le classement retenu ignore la casse.
  • La branche gauche puis droite : Le nœud bot guide ensuite la recherche vers son enfant droit.
  • La branche droite jusqu’à une feuille : La normalisation s’applique à chaque comparaison, y compris à l’égalité finale.
  • Une clé absente entre des clés présentes : La recherche doit aboutir à un enfant vide sans modifier les liens.
Indice

Un nom d’attribut doit correspondre exactement au constructeur.

Comprendre la correction
if arbre is None or arbre.cle == cle:
    return arbre
if cle < arbre.cle:
    return recherche(arbre.gauche, cle)
else:
    return recherche(arbre.droit, cle)

Les trois compléments attendus sont cle, gauche, droit. Le premier test arrête la recherche sur un arbre vide ou une égalité. L’attribut s’appelle ici droit, sans e, contrairement à l’exercice précédent.

Le code imprimé place cependant la fonction à l’intérieur de la classe tout en appelant recherche sans qualification. Pour disposer d’une version directement exécutable, on peut définir cette fonction à l’extérieur de la classe, ou employer une méthode statique et appeler Noeud.recherche(...). Pour l’arbre donné avec IA, les comparaisons doivent aussi respecter l’ordre alphabétique sans casse :

def recherche(arbre, cle):
    if arbre is None or arbre.cle.casefold() == cle.casefold():
        return arbre
    if cle.casefold() < arbre.cle.casefold():
        return recherche(arbre.gauche, cle)
    return recherche(arbre.droit, cle)

La recherche sur une clé absente doit aboutir à None, tandis qu’une clé égale à celle du nœud courant renvoie ce nœud lui-même. Les deux tests de base se font avant l’accès aux enfants. La normalisation de casse doit être la même à l’insertion et à la recherche : corriger seulement la comparaison de recherche sur un arbre construit avec un autre ordre ne suffirait pas à restaurer l’invariant d’ABR.

Voir la question dans le sujet PDF, p. 11 (nouvel onglet)
Trois parcours, trois récits du même arbreUn atelier pour expérimenter

Choisissez un parcours puis déplacez le curseur pour n’en révéler que les premières visites. Anticipez le prochain mot-clé avant d’avancer.

Lire le résultat de l’expérience initiale

1 nœud visité

Gauche, nœud, droite : les clés sortent dans l’ordre alphabétique sans tenir compte de la casse.

RangMot-clé
1algo

Un parcours se définit par l’ordre de visite. Une recherche d’ABR utilise en plus une relation d’ordre cohérente sur les clés.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Annoncez la convention de hauteur avant de compter un chemin.
  • Quand un programme est complété, testez un cas absent et un cas de base, pas seulement l’exemple réussi.
  • Pour la complexité, distinguez le nombre d’opérations, le volume stocké et l’état initial du cache.

Retrouver ces notions dans d’autres sujets

Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027

Énoncé : sujet 26-NSIJ1PO1 (PDF). Corrigé et explications pédagogiques proposés par Sofien.