In [ ]:
Nom = ""
Prénom = ""

Python 7 : les arbres binaires¶

1 - Définitions et vocabulaire¶

Les arbres binaires ressemblent aux listes chainées, mais là où un maillon d'une liste a un maillon suivant, un noeud d'un arbre binaire a deux noeuds suivants : le noeud droit et le noeud gauche.

On obtient une structure qui ressemble à ça :

No description has been provided for this image

Chaque noeud a deux sous-arbres : le sous-arbre droit, et le sous-arbre gauche.

Un arbre (et donc un sous-arbre) peut éventuellement être vide.

Tous les noeuds sauf un ont un noeud parent. Le noeud qui n'a pas de noeud parent est la racine de l'arbre.

Un noeud dont les deux sous-arbres sont vides est une feuille.

Par exemple, dans l'arbre ci-dessus, la racine est le noeud 1, les noeuds 3, 6 et 7 sont des feuilles.

La taille d'un arbre est le nombre de ses noeuds. L'arbre ci-dessus est par exemple de taille 8.

La hauteur d'un arbre est le nombre maximum de noeuds entre la racine et une feuille. L'arbre ci-dessus est par exemple de hauteur 4.

Un arbre dont tous les chemins de la racine aux feuilles ont la même longueur est dit parfait.

Un arbre dont tous les sous-arbres droits sont des feuilles est un peigne gauche.

Exercice 1: Tracer un peigne gauche de hauteur 5.

Exercice 2: Tracer un arbre parfait de hauteur 5.

Exercice 3: Tracer tous les arbres de tailles 3 et 4.

Exercice 4: Déterminer le nombre d'arbres de taille 5.

2 - Représentation en Python¶

On va représenter un noeud d'un arbre par un objet d'une classe Noeud. Chaque noeud aura une valeur (attribut v) ainsi qu'un arbre gauche (attribut gauche) et un arbre droit (attribut droit).

Proposer une définition pour la classe Noeud :

In [ ]:
# Insérez votre code ici

De manière à pouvoir afficher un noeud, on ajoute une méthode __str__ avec la cellule suivante :

In [ ]:
from tree import to_string

Noeud.__str__ = to_string
In [ ]:
arbre = Noeud(Noeud(None, 1, None), 0, Noeud(None, 2, None))
assert arbre.v == 0
assert arbre.gauche.v == 1
assert arbre.droit.v == 2
print(arbre)

Exercice : Définir en Python l'arbre suivant. On placera la racine dans la variable racine.

No description has been provided for this image

In [ ]:
# Insérez votre code ici

print(racine)
In [ ]:
assert racine.v == 5
assert racine.gauche.v == 9
assert racine.droit.v == 1
assert racine.gauche.gauche.v == 0
assert racine.gauche.gauche.gauche is None
assert racine.gauche.gauche.droit is None
assert racine.gauche.droit.v == 8
assert racine.gauche.droit.gauche.v == 4
assert racine.gauche.droit.droit.v == 3
assert racine.gauche.droit.gauche.droit is None
assert racine.gauche.droit.gauche.gauche is None
assert racine.gauche.droit.droit.droit is None
assert racine.gauche.droit.droit.gauche is None

Exercice : Définir une fonction peigne qui prend en argument en nombre n strictement positif et renvoie la racine d'un peigne gauche de taille n. Toutes les feuilles auront pour valeur 1 et les autres noeuds auront des valeurs de 1 à n. La racine aura pour valeur n :

          ┌─6─┐
        ┌─5─┐ 1
      ┌─4─┐ 1  
     ┌3─┐ 1    
    ┌2┐ 1      
    1 1        
In [ ]:
# Insérez votre code ici

print(peigne(6))
In [ ]:
noeud = peigne(10)
for i in range(9):
    assert noeud.v == 10 - i
    assert noeud.droit.v == 1
    assert noeud.droit.gauche is None
    assert noeud.droit.droit is None
    noeud = noeud.gauche
assert noeud.v == 1 and noeud.gauche is None and noeud.droit is None

3 - Taille d'un arbre¶

Définir une fonction récursive taille qui renvoie la taille d'un arbre.

In [ ]:
def taille(racine):
    # Insérez votre code ici
In [ ]:
racine = Noeud(Noeud(Noeud(None, 2, None), 5, Noeud(Noeud(None, -7, None), 2, Noeud(None, 3, None))), 5, Noeud(None, 7, None))
assert taille(racine) == 7

4 - Hauteur d'un arbre¶

Définir une fonction récursive hauteur qui renvoie la hauteur d'un arbre.

In [ ]:
def hauteur(racine):
    # Insérez votre code ici
In [ ]:
racine = Noeud(Noeud(Noeud(None, 2, None), 5, Noeud(Noeud(None, -7, None), 2, Noeud(None, 3, None))), 5, Noeud(None, 7, None))
assert hauteur(racine) == 4

5 - Des objets vers les listes¶

On veut pouvoir représenter un arbre par des listes imbriquées.

Un noeud sera représenté par une liste de 3 éléments : le premier sera le sous-arbre gauche, le deuxième la valeur du noeud et le troisième le sous-arbre droit.

Définir une fonction obj2list qui prend en argument un noeud de la classe Noeud et renvoie une représentation avec des listes de l'arbre dont il est racine.

In [ ]:
def obj2list(racine):
    """
    Transforme un arbre représenté par des noeuds
    en le même arbre représenté par des listes
    imbriquées
    :param racine: (Objet Noeud) le noeud qui est la racine
    :return: (list) la liste contenant toutes les autres
    """
    # Insérez votre code ici
In [ ]:
racine = Noeud(Noeud(Noeud(None, 2, None), 5, Noeud(Noeud(None, -7, None), 2, Noeud(None, 3, None))), 5, Noeud(None, 7, None))
assert obj2list(racine) == [[[None, 2, None], 5, [[None, -7, None], 2, [None, 3, None]]], 5, [None, 7, None]]

6 - Des listes vers les objets¶

Définir une fonction list2obj qui fait la transformation inverse de la fonction obj2list.

In [ ]:
def list2obj(l):
    """
    Transforme un arbre représenté par des listes
    imbriquées en le même arbre représenté par des noeuds
    :param racine: (list) la liste contenant toutes les autres
    :return: (Objet Noeud) le noeud qui est la racine
    """
    # Insérez votre code ici
In [ ]:
l = [[None, 0, [None, 1, None]], 2, [None, 3, None]]
racine = list2obj(l)
assert racine.v == 2
assert racine.gauche.v == 0
assert racine.gauche.gauche is None
assert racine.gauche.droit.v == 1
assert racine.gauche.droit.gauche is None
assert racine.gauche.droit.droit is None
assert racine.droit.v == 3
assert racine.droit.gauche is None
assert racine.droit.droit is None

7 - Représentation par une chaine de caractères¶

Le module json (JavaScript Object Notation) permet de convertir des listes en chaines de caractère et inversement (fonctions json.dumps et json.loads respectivement).

a - Vers la chaine de caractères¶

Utiliser la fonction json.dumps et la fonction obj2list pour définir une méthode __repr__ à la classe Noeud.

In [ ]:
def repr_tmp(self):
    """
    Renvoie une représentation de l'arbre dont la racine est self
    sous la forme d'une chaine de caractères
    :return: (str) la représentation
    """
    # Insérez votre code ici

Noeud.__repr__ = repr_tmp
In [ ]:
import json
racine = Noeud(Noeud(Noeud(None, 2, None), 5, Noeud(Noeud(None, -7, None), 2, Noeud(None, 3, None))), 5, Noeud(None, 7, None))
assert repr(racine) == '[[[null, 2, null], 5, [[null, -7, null], 2, [null, 3, null]]], 5, [null, 7, null]]'

b - Vers l'arbre¶

Définir une fonction obtenir_arbre qui, à partir d'une chaine de caractères telle que renvoyer par la méthode __repr__, renvoie un arbre défini avec des instances de la classe Noeud.

In [ ]:
def obtenir_arbre(s):
    """
    Renvoie un arbre représenté par des noeuds
    à partir de sa représentation sous la forme d'une chaine de caractères
    :param s: (str) la représentation
    :return: (objet Noeud) la racine de l'arbre
    """
    # Insérez votre code ici
In [ ]:
import json
s = '[[null, 0, [null, 1, null]], 2, [null, 3, null]]'
racine = obtenir_arbre(s)
assert racine.v == 2
assert racine.gauche.v == 0
assert racine.gauche.gauche is None
assert racine.gauche.droit.v == 1
assert racine.gauche.droit.gauche is None
assert racine.gauche.droit.droit is None
assert racine.droit.v == 3
assert racine.droit.gauche is None
assert racine.droit.droit is None

8 - Égalité de deux arbres¶

Rajouter à la classe Noeud une méthode __eq__ qui permet de tester l'égalité de deux arbres avec l'opérateur ==.

In [ ]:
def eq_tmp(self, noeud):
    # Insérez votre code ici

Noeud.__eq__ = eq_tmp
In [ ]:
import json
s = '[[null, 0, [null, 1, null]], 2, [null, 3, null]]'
racine1 = obtenir_arbre(s)
racine2 = obtenir_arbre(s)
assert racine1 == racine2
assert not racine1 == None
racine1.gauche.v = 4
assert racine1 != racine2

9 - Arbre parfait¶

Exercice : Définir une fonction parfait qui prend en argument en nombre n strictement positif et renvoie la racine d'un arbre parfait de hauteur n. La valeur des noeuds sera une chaine de caractères de la forme 'i-j' où i est la distance du noeud de la racine et j la distance du noeud le plus à gauche de la ligne (une ligne étant définie comme les noeuds ayant le même i).

In [ ]:
def parfait(n, i = 0, j = 0):
    # Insérez votre code ici

print(parfait(5))
In [ ]:
racine = parfait(5)
assert repr(racine) == '[[[[[null, "4-0", null], "3-0", [null, "4-1", null]], "2-0", [[null, "4-2", null], "3-1", [null, "4-3", null]]], "1-0", [[[null, "4-4", null], "3-2", [null, "4-5", null]], "2-1", [[null, "4-6", null], "3-3", [null, "4-7", null]]]], "0-0", [[[[null, "4-8", null], "3-4", [null, "4-9", null]], "2-2", [[null, "4-10", null], "3-5", [null, "4-11", null]]], "1-1", [[[null, "4-12", null], "3-6", [null, "4-13", null]], "2-3", [[null, "4-14", null], "3-7", [null, "4-15", null]]]]]'