Aller au contenu

Les algorithmes sur les arbres binaires

1) notations utilisĂ©es⚓

Dans ce chapitre nous allons utiliser les notations suivantes :

Soit un arbre T : T.racine correspond au noeud racine de l'arbre T

Soit un noeud x :

  • x.gauche correspond au sous-arbre gauche du noeud x
  • x.droit correspond au sous-arbre droit du noeud x
  • x.clĂ© correspond Ă  la clĂ© du noeud x

Il faut noter que si le noeud x est une feuille, x.gauche et x.droite sont des arbres vides (NIL)

2) calculer la hauteur d'un arbre⚓

Voici l'algorithme qui permet de calculer la hauteur d'un arbre :

Text Only
VARIABLE
T : arbre
x : noeud

DEBUT
HAUTEUR(T) :
  si T ≠ NIL :
    x ← T.racine
    renvoyer 1 + max(HAUTEUR(x.gauche), HAUTEUR(x.droit))
  sinon :
    renvoyer 0
  fin si
FIN

N.B. la fonction max renvoie la plus grande valeur des 2 valeurs passées en paramÚtre (exemple : max(5,6) renvoie 6

Nous avons ici un algorithme récursif. Vous aurez l'occasion de constater que c'est souvent le cas dans les algorithmes qui travaillent sur des structures de données telles que les arbres.

3) calculer la taille d'un arbre⚓

Nous allons maintenant étudier un algorithme qui permet de calculer le nombre de noeuds présents dans un arbre.

Text Only
VARIABLE
T : arbre
x : noeud

DEBUT
TAILLE(T) :
  si T ≠ NIL :
    x ← T.racine
    renvoyer 1 + TAILLE(x.gauche) + TAILLE(x.droit)
  sinon :
    renvoyer 0
  fin si
FIN

4) parcours d'un arbre⚓

a) introduction⚓

Il existe plusieurs façons de parcourir un arbre (parcourir un arbre = passer par tous les noeuds), nous allons en étudier quelques-unes. Le choix du parcours dépend du problÚme à traiter

b) parcourir un arbre dans l'ordre prĂ©fixe⚓

Voici l'algorithme qui va permettre de parcourir un arbre dans l'ordre préfixe :

Text Only
VARIABLE
T : arbre
x : noeud

DEBUT
PARCOURS-PREFIXE(T) :
  si T ≠ NIL :
    x ← T.racine
    affiche x.clé
    PARCOURS-PREFIXE(x.gauche)
    PARCOURS-PREFIXE(x.droit)
  fin si
FIN

Comme vous pouvez le constater ci-dessus, dans le cas du parcours préfixe, on affiche chaque noeud avant de parcourir son sous-arbre gauche et son sous-arbre droit.

Exemple

voici un arbre représenté avec des listes en Python. Ecrire les fonctions qui donnent la taille, la hauteur et le parcours préfixe de cet arbre.

Python
arbre = ['a', [['b', [['d', [None, None]], ['e', [None, None]]]], ['c', [None, ['f', [None, None]]]]]]

def taille(arb):
    ...

def hauteur(arb):
    ...

def parcours_prefixe(arb):
    ...

c) parcourir un arbre dans l'ordre postfixe(suffixe)⚓

Voici l'algorithme qui va permettre de parcourir un arbre dans l'ordre suffixe :

Text Only
VARIABLE
T : arbre
x : noeud

DEBUT
PARCOURS-SUFFIXE(T) :
  si T ≠ NIL :
    x ← T.racine
    PARCOURS-SUFFIXE(x.gauche)
    PARCOURS-SUFFIXE(x.droit)
    affiche x.clé
  fin si
FIN
Dans le cas du parcours suffixe, on affiche chaque noeud aprĂšs avoir parcouru son sous-arbre gauche et son sous-arbre droit.

d) parcourir un arbre dans l'ordre infixe⚓

Voici l'algorithme qui va permettre de parcourir un arbre dans l'ordre infixe :

Text Only
VARIABLE
T : arbre
x : noeud

DEBUT
PARCOURS-INFIXE(T) :
  si T ≠ NIL :
    x ← T.racine
    PARCOURS-INFIXE(x.gauche)
    affiche x.clé
    PARCOURS-INFIXE(x.droit)
  fin si
FIN
Dans le cas du parcours infixe, pour un noeud A donné, on parcourra le sous-arbre gauche de A, puis on affichera la clé de A puis enfin, on parcourra le sous-arbre droite de A

e) parcourir un arbre en largeur d'abord⚓

Voici l'algorithme qui va permettre de parcourir un arbre en largeur d'abord :

Text Only
T : arbre
Tg : arbre
Td : arbre
x : noeud
f : file (initialement vide)

DEBUT
PARCOURS-LARGEUR(T) :
  enfiler(T.racine, f) //on place la racine dans la file
  tant que f non vide :
    x ← defiler(f)
    affiche x.clé
    si x.gauche ≠ NIL :
      Tg ← x.gauche
      enfiler(Tg.racine, f)
    fin si
    si x.droit ≠ NIL :
      Td ← x.droite
      enfiler(Td.racine, f)
    fin si
  fin tant que
FIN

Vous noterez aussi que cet algorithme n'utilise pas de fonction récursive. Il est aussi important de bien noter l'utilisation d'une file (FIFO) pour cet algorithme de parcours en largeur.

Dans le cas d'un parcours en largeur d'abord on affiche tous les noeuds situés à une profondeur n avant de commencer à afficher les noeuds situés à une profondeur n+1.

code Python utilisant la POO⚓

Python
class BinTree:
    def __init__(self, value, lt = None, rt = None):
        self.value = value
        self.lt = lt
        self.rt = rt

    def prefixe(self):
        # cas de base
        if self.lt == None and self.rt == None:
            return [self.value]
        # 3 étapes : racine, sous-arbre gauche puis sous-arbre droite
        parcours = []
        parcours.append(self.value)
        if  self.lt != None:
            parcours += self.lt.prefixe() # parocurs du sous -arbre de gauche
        if  self.rt != None:
            parcours += self.rt.prefixe() # parocurs du sous -arbre de gauche
        return parcours

    def infixe(self):
        # cas de base
        if self.lt == None and self.rt == None:
            return [self.value]
        # 3 étapes : sous-arbre gauche, racine, puis sous-arbre droite
        parcours = []

        # sous-arbre gauche
        if  self.lt != None:
            parcours += self.lt.infixe() # parocurs du sous -arbre de gauche

        #racine
        parcours.append(self.value)

        # sous-arbre droite
        if  self.rt != None:
            parcours += self.rt.infixe() # parocurs du sous -arbre de gauche
        return parcours

# Arbre exemple
a = BinTree('T', BinTree('Y',BinTree('P'), None), BinTree('O',BinTree('H'), BinTree('N')))