DS MP - Arbre d'expression arithmétique

20 Mar 2026 20 Mar 2026 260 vues ESSADDOUKI Mostafa 10 min de lecture
Complexité algorithmique
1 Introduction à l'analyse des algorithmes 2 Notations asymptotiques 3 Opérations élémentaires et modèles de coût 4 Complexité temporelle et spatiale 5 Méthode de comptage des pas 6 Méthode de comptage des pas pour les boucles
Diviser pour régner & algorithmes de tri
7 Rappel sur l'approche récursive 8 Diviser pour régner 9 la recherche dichotomique 10 Tri par fusion 11 Tri rapide 12 Analyse des fonctions récursives
Les arbres binaires
13 Introduction aux arbres binaires 14 Définitions récursives des arbres 15 Propriétés des arbres binaires 16 Types d'arbre binaire 17 Parcours en profondeur des arbres binaires 18 Parcours en largeur des arbres binaires 19 Exercices corrigés sur les arbres - TD 1 20 Exercices corrigés sur les arbres - TD 2 21 Exercices corrigés sur les arbres - TD 3 22 DS - Arbres binaires de recherche équilibrés (AVL) 23 DS - Codage de Huffman - Compression de données
Algorithmes gloutons
24 Introduction aux algorithmes gloutons 25 Problème de la sélection d'activités 26 Problème de séquencement des tâches 27 Problème du Sac à Dos fraction
Programmation dynamique
28 Introduction à la programmation dynamique 29 Le concept de sous-structure optimale 30 Le concept de sous-problèmes superposés 31 Méthodes de la programmation dynamique 32 Différence entre la programmation dynamique, l'approche diviser pour régner, et les algorithmes gloutons 33 Calculer les nombres de catalan en C++ et Python 34 Calculer le coefficient binomial 35 Le nombre de façons pour construire un mur de dimension 4*N 36 Défi de conversion de mots 37 Décomposition de phrases à partir d'un dictionnaire 38 La collection de pièces dans un labyrinthe 39 Nombre de façons de regrouper les étudiants 40 Compter tous les chemins possibles dans une grille MxN
Méta heuristique
41 Algorithmes heuristiques et métaheuristiques 42 Algorithme de Recuit simulé 43 Algorithme Colonies de fourmis
Théorie des graphes
44 Introduction et notions fondamentales 45 Chemins, cycles et connexité 46 Représentations des graphes 47 Parcours de graphes 48 Algorithme de Dijkstra 49 Algorithme A* 50 Algorithme de Bellman-Ford 51 Algorithme Floyd-Warshall 52 Coloration des graphes
Base de données et SQL
53 Introduction au langage SQL 54 Concepts de base de SGBDR 55 Syntaxes de différentes instructions SQL 56 Création et suppression d'une base de données 57 Opérateurs SQL 58 Les contraintes en SQL 59 Création et suppression des tables en SQL 60 Insertion et modifications des enregistrements 61 Extraction des données - SELECT 62 Filtrer les données - WHERE 63 Recherche de motifs - LIKE 64 Trier les données - ORDER BY 65 Les jointures en SQL - JOIN 66 Fonctions d'agrégation en SQL - SUM, COUNT, AVG, MIN et MAX 67 Organiser des données identiques en groupes - GROUP BY et HAVING 68 Les sous-requêtes en SQL 69 Les fonctions SQL de manipulation de date 70 DS - langage SQL 71 Exercices corrigés de langage SQL
Introduction à l'intelligence artificielle
Introduction à la théorie des jeux
Préparation aux concours
72 Réseau de distribution d'eau 73 Arbre d'expression arithmétique 74 Exploration d'une grotte souterraine
Arbres binaires Expressions arithmétiques Parcours d'arbres Avancé
6 parties
15 questions
Python CPGE MP
~90 min

Arbres d'expression arithmétique

Un arbre d'expression est un arbre binaire qui représente une expression arithmétique :

  • les feuilles contiennent des nombres entiers,
  • les nœuds internes contiennent des opérateurs : '+', '-', '*', '/'.

Représentation Python : identique à la convention habituelle.

# Arbre vide : None
# Nœud      : [val, g, d]
#   val = entier ou opérateur (str)
#   g   = sous-arbre gauche
#   d   = sous-arbre droit

Exemple — l'expression $(3 + 5) \times (8 - 2)$ :

expr1 = ['*',
           ['+', [3, None, None], [5, None, None]],
           ['-', [8, None, None], [2, None, None]]]
0
Propriétés structurelles 4 questions
Q1

Écrire une fonction est_feuille(a) qui retourne True si le nœud a (non vide) est une feuille, False sinon.

Solution — Test de feuille
def est_feuille(a):
    return a[1] is None and a[2] is None
Q2

Écrire une fonction nb_noeuds(a) qui retourne le nombre total de nœuds de l'arbre a.

Exemple

Entrée
nb_noeuds(expr1)
Sortie
7
Solution — Nombre de nœuds
def nb_noeuds(a):
    if a is None:
        return 0
    return 1 + nb_noeuds(a[1]) + nb_noeuds(a[2])
Q3

Écrire une fonction nb_feuilles(a) qui retourne le nombre de feuilles de l'arbre a.

Exemple

Entrée
nb_feuilles(expr1)
Sortie
4
Solution — Nombre de feuilles
def nb_feuilles(a):
    if a is None:
        return 0
    if est_feuille(a):
        return 1
    return nb_feuilles(a[1]) + nb_feuilles(a[2])
Q4

On admet que dans tout arbre d'expression bien formé, chaque nœud interne possède exactement deux enfants. Démontrer par récurrence sur la hauteur $h$ que le nombre de feuilles $f$ et le nombre de nœuds internes $n_i$ vérifient toujours : $$f = n_i + 1$$ Vérifier cette relation sur expr1.

Solution — Démonstration

Démonstration par récurrence sur la hauteur $h$ :

  • Cas de base ($h = 0$) : L'arbre est une feuille unique. Alors $f = 1$ et $n_i = 0$, donc $f = n_i + 1$.
  • Hypothèse de récurrence : Supposons que la propriété est vraie pour tout arbre de hauteur $h$.
  • Hérédité : Soit un arbre de hauteur $h+1$ dont la racine est un nœud interne avec deux sous-arbres $G$ et $D$ (chacun de hauteur ≤ $h$).
    Par hypothèse, pour $G$ : $f_G = n_{i_G} + 1$
    Pour $D$ : $f_D = n_{i_D} + 1$
    Pour l'arbre complet : $f = f_G + f_D$ et $n_i = 1 + n_{i_G} + n_{i_D}$
    Donc $f = (n_{i_G} + 1) + (n_{i_D} + 1) = (n_{i_G} + n_{i_D} + 1) + 1 = n_i + 1$.

Vérification sur expr1 : $f = 4$, $n_i = 3$, donc $4 = 3 + 1$.

1
Évaluation et parcours 3 questions
Q5

Écrire une fonction evaluer(a) qui calcule la valeur numérique de l'expression représentée par l'arbre a. On supposera qu'aucune division par zéro n'a lieu.

Exemple

Entrée
evaluer(expr1)
Sortie
48
Solution — Évaluation
def evaluer(a):
    if a is None:
        return None
    val = a[0]
    if isinstance(val, (int, float)):
        return val
    
    gauche = evaluer(a[1])
    droite = evaluer(a[2])
    
    if val == '+':
        return gauche + droite
    elif val == '-':
        return gauche - droite
    elif val == '*':
        return gauche * droite
    elif val == '/':
        return gauche / droite
Q6

Le parcours infixe d'un arbre d'expression produit l'expression arithmétique dans sa forme habituelle (avec parenthèses). Écrire une fonction infixe(a) qui retourne une chaîne de caractères représentant l'expression complètement parenthésée.

Exemple

Entrée
infixe(expr1)
Sortie
"((3+5)*(8-2))"
Solution — Parcours infixe
def infixe(a):
    if a is None:
        return ""
    if est_feuille(a):
        return str(a[0])
    
    return "(" + infixe(a[1]) + str(a[0]) + infixe(a[2]) + ")"
Q7

Le parcours préfixe (notation polonaise) place l'opérateur avant ses opérandes. Écrire une fonction prefixe(a) qui retourne la représentation préfixe de l'expression, les éléments étant séparés par des espaces.

Exemple

Entrée
prefixe(expr1)
Sortie
"* + 3 5 - 8 2"
Solution — Parcours préfixe
def prefixe(a):
    if a is None:
        return ""
    if est_feuille(a):
        return str(a[0])
    
    return str(a[0]) + " " + prefixe(a[1]) + " " + prefixe(a[2])
2
Construction depuis une expression préfixe 2 questions

On dispose d'une expression en notation préfixe représentée par une liste de tokens :

tokens = ['*', '+', 3, 5, '-', 8, 2]
Q8

Écrire une fonction récursive construire(tokens, i) qui prend la liste tokens et un indice i (position courante de lecture), et retourne le couple (arbre, j)arbre est le sous-arbre construit à partir de la position i, et j est l'indice suivant la dernière position consommée.

Exemple

Entrée
arbre, _ = construire(tokens, 0)
evaluer(arbre)
Sortie
48
Solution — Construction depuis préfixe
def construire(tokens, i):
    token = tokens[i]
    
    # Si le token est un nombre (feuille)
    if isinstance(token, (int, float)):
        return [token, None, None], i + 1
    
    # Sinon, c'est un opérateur
    gauche, j = construire(tokens, i + 1)
    droite, k = construire(tokens, j)
    
    return [token, gauche, droite], k
Q9

En déduire une fonction depuis_prefixe(tokens) qui retourne directement l'arbre construit depuis la liste complète.

Solution — Construction depuis préfixe (wrapper)
def depuis_prefixe(tokens):
    arbre, _ = construire(tokens, 0)
    return arbre
3
Profondeur et complexité 2 questions
Q10

Écrire une fonction profondeur_noeud(a, val) qui retourne la profondeur du premier nœud contenant la valeur val dans l'arbre a, ou -1 si cette valeur est absente.

Exemple

Entrée
profondeur_noeud(expr1, 8)
Sortie
2
Explication : La valeur 8 se trouve à la profondeur 2.
Entrée
profondeur_noeud(expr1, 9)
Sortie
-1
Solution — Profondeur d'un nœud
def profondeur_noeud(a, val, prof=0):
    if a is None:
        return -1
    
    if a[0] == val:
        return prof
    
    prof_gauche = profondeur_noeud(a[1], val, prof + 1)
    if prof_gauche != -1:
        return prof_gauche
    
    return profondeur_noeud(a[2], val, prof + 1)
Q11

On appelle arbre filiforme un arbre binaire dans lequel chaque nœud interne possède au moins un enfant None (c'est-à-dire qu'il n'a jamais deux enfants non vides simultanément). Écrire une fonction est_filiforme(a) qui retourne True si l'arbre est filiforme, False sinon.

Solution — Test d'arbre filiforme
def est_filiforme(a):
    if a is None:
        return True
    
    gauche = a[1]
    droite = a[2]
    
    # Si c'est une feuille, c'est filiforme
    if gauche is None and droite is None:
        return True
    
    # Un nœud interne doit avoir au moins un enfant None
    if gauche is None or droite is None:
        # Récursivité sur l'enfant non vide
        if gauche is not None:
            return est_filiforme(gauche)
        else:
            return est_filiforme(droite)
    
    # Si les deux enfants sont non vides, ce n'est pas filiforme
    return False
4
Transformation : arbre miroir et simplification 2 questions
Q12

Écrire une fonction miroir(a) qui retourne un nouvel arbre obtenu en échangeant récursivement les sous-arbres gauche et droit à chaque nœud.

Exemple

Entrée
infixe(miroir(expr1))
Sortie
"((2-8)*(5+3))"
Solution — Arbre miroir
def miroir(a):
    if a is None:
        return None
    
    # Copier la valeur et échanger récursivement les enfants
    return [a[0], miroir(a[2]), miroir(a[1])]
Q13

On souhaite simplifier l'arbre en appliquant deux règles :

  • tout sous-arbre dont les deux enfants sont des feuilles numériques peut être remplacé par sa valeur calculée ;
  • on n'applique cette règle qu'une seule fois, en remontant (parcours post-ordre).

Écrire une fonction simplifier(a) qui retourne l'arbre simplifié.

Exemple

Entrée
expr2 = ['*', ['+', [3,None,None],[5,None,None]], ['-', [8,None,None],['x',None,None]]]
infixe(simplifier(expr2))
Sortie
"(8*(8-x))"
Explication : Le sous-arbre (3+5) a été remplacé par 8 ; (8-x) ne peut pas l'être.
Solution — Simplification
def simplifier(a):
    if a is None:
        return None
    
    # Simplifier récursivement les enfants
    gauche = simplifier(a[1])
    droite = simplifier(a[2])
    
    # Vérifier si les deux enfants sont des feuilles numériques
    if (gauche is not None and droite is not None and 
        isinstance(gauche[0], (int, float)) and est_feuille(gauche) and
        isinstance(droite[0], (int, float)) and est_feuille(droite)):
        # Remplacer par la valeur calculée
        resultat = evaluer([a[0], gauche, droite])
        return [resultat, None, None]
    
    return [a[0], gauche, droite]
5
Application : égalité structurelle et sémantique 2 questions
Q14

Écrire une fonction egaux_structure(a, b) qui retourne True si les deux arbres a et b ont exactement la même structure et les mêmes valeurs à chaque nœud.

Solution — Égalité structurelle
def egaux_structure(a, b):
    if a is None and b is None:
        return True
    if a is None or b is None:
        return False
    
    return (a[0] == b[0] and 
            egaux_structure(a[1], b[1]) and 
            egaux_structure(a[2], b[2]))
Q15

Deux expressions sont sémantiquement équivalentes si elles produisent la même valeur pour toute affectation de variables. Dans le cas simplifié où les arbres ne contiennent que des constantes (pas de variables), cela revient à comparer leur évaluation.

Écrire une fonction egaux_semantique(a, b) qui retourne True si evaluer(a) == evaluer(b), en gérant le cas où l'un des arbres est vide (retourner False).

Exemple

Entrée
expr3 = ['+', ['*', [2,None,None],[4,None,None]], ['-', [10,None,None],[2,None,None]]]
# expr3 vaut 2*4 + (10-2) = 8+8 = 16
egaux_semantique(expr1, expr3)
Sortie
False
Entrée
expr4 = ['*', [6,None,None],[8,None,None]]  # 48
egaux_semantique(expr1, expr4)
Sortie
True
Solution — Égalité sémantique
def egaux_semantique(a, b):
    if a is None or b is None:
        return False
    
    return evaluer(a) == evaluer(b)
Sortie
// La sortie apparaîtra ici…
Prêt · Ctrl+Entrée pour exécuter

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.