Arbres Binaires Recherche



Introduction

Quelle structure de données permet :

  • d’organiser les données selon un ordre donné (numérique, lexicographique, etc.) ;
  • d’effectuer des recherches le plus efficacement possible ;
  • d’accéder à, d’insérer ou de supprimer les données le plus efficacement possible.

Tableaux


Propriétés
  • On peut ordonner des données dans un tableau mais l’algorithme de tri le plus rapide, pour un jeu de données aléatoires, est en $O(n \; \log n)$ ;
  • On peut accéder à une donnée en $O(1)$ ;
  • On peut rechercher une valeur efficacement en utilisant la dichotomie (si le tableau est trié !) : $O(\log n)$ ;
  • Supprimer ou insérer une valeur n’est pas très efficace : $O(n)$ (sauf à la fin du tableau).

Dictionnaires


Propriétés
  • Accéder à, supprimer et insérer une valeur se fait très efficacement : $O(1)$ ;
  • Les données ne sont pas ordonnées.

Remarque
Dans la suite de ce document, on considérera que les valeurs des nœuds sont des entiers.

Un arbre binaire de recherche, ou ABR, est un arbre binaire avec la propriété suivante : quel que soit le nœud $x$, tous les nœuds situés dans le sous-arbre gauche de $x$ ont une valeur inférieure à la valeur du nœud $x$, et tous ceux situés dans son sous-arbre droit ont une valeur supérieure à la valeur du nœud $x$.

Remarque
La définition précédente est récursive.
  1. Cet arbre binaire est-il un arbre binaire de recherche ?
graph TD
    A(3) --- B(1)
    B --- C(None)
    B --- D(2)
    D --- E(None)
    D --- F(None)
    A --- G(4)
    G --- H(None)
    G --- I(None)

Réponse

oui


  1. Cet arbre binaire est-il un arbre binaire de recherche ?
graph TD
    A(3) --- B(2)
    B --- C(1)
    C --- D(None)
    C --- E(None)
    B --- F(None)
    A --- G(4)
    G --- H(None)
    G --- I(None)

Réponse

oui


  1. Cet arbre binaire est-il un arbre binaire de recherche ?
graph TD
   A(3) --- B(2)
    B --- C(None)
    B --- D(1)
    D --- E(None)
    D --- F(None)
    A --- G(4)
    G --- H(None)
    G --- I(None)

Réponse

Non ! La propriété est bien vérifiée pour la racine mais pas pour le sous-arbre gauche.
La définition est récurisve !


Les ABR servent à maintenir un ensemble de valeurs ordonnées, avec une meilleure complexité qu’une liste chaînée par exemple. On commence par la recherche d’un élément. La structure de l’ABR permet d’éviter de le parcourir en entier.

Un ABR est un arbre binaire

Le paradigme de programmation utilisé ci-dessous est le paradigme impératif : on crée dans un premier temps une structure élémentaire à l’aide d’une classe et on définit ensuite des fonctions qui manipulent cette structure.
On ne crée pas de structure arbre binaire (accompagnée de méthodes).
  1. Pour représenter un ABR, on va à nouveau utiliser la classe Noeud. Écrire à nouveau le code de cette classe.
    Les contraintes sur les valeurs des nœuds ne sont pas codées dans la classe.

  2. Écrire à nouveau les spécifications des fonctions taille, hauteur et parcours (parcours infixe).

1
2
3
4
def est_vide(n: Noeud) -> bool:
    """
    Teste si le noeud passé en argument est vide ou pas.
    """
1
2
3
4
def hauteur(a: Noeud) -> int:
    """
    Retourne la hauteur de l'arbre binaire de recherche a. 
    """
1
2
3
4
def taille(a: Noeud) -> int:
    """
    Retourne le nombre de noeuds dans l'arbre binaire de recherche.
    """
1
2
3
4
def parcours(n: Noeud) -> None:
    """
    Parcours infixe de l'arbre
    """
  1. Vérifier que la fonction parcours affiche les valeurs dans l’ordre croissant.
Rappel
On peut voir le parcours infixe comme la projection de l’arbre sur une droite.

Recherche dans un ABR

  1. Écrire le code de la fonction appartient dont la spécification est
1
2
3
4
def appartient(a: Noeud, x: int) -> bool:
    """
    Détermine si la valeur x apparaît dans l'arbre a.
    """
  1. Quelle est la complexité de cette recherche ?

Ajout d’une valeur dans un ABR

  1. Envisager un ABR et, à la main, ajouter un élément.

  2. Quel peut-être le code de la fonction ajoute dont la spécification est

1
2
3
4
5
6
def ajoute(a: Noeud, x: int) -> None:
    """
    Ajoute x à l'arbre a (en place).

    HYPOTHÈSE : a n'est pas l'arbre vide.
    """
  1. Quelle est la complexité de cette fonction ?

  2. Pour résoudre le problème de l’ajout d’une valeur à un arbre initialement vide et/ou pour ne pas modifier un arbre déjà constitué, il est possible de programmer la fonction ajoute_copie dont la spécification est

1
2
3
4
def ajoute_copie(n: Noeud, x: int) -> Noeud:
    """
    Ajoute la valeur x à l'ABR n. Un nouvel arbre est créé.
    """

Écrire le code de cette fonction.

Utilisation d’un arbre binaire de recherche

  1. Créer la fonction plus_petit_element dont la spécification est
1
2
3
4
5
6
def plus_petit_element(a: Noeud) -> int:
    """
    Retourne le plus petit élément de l'arbre binaire de recherche a.

    Lève une exception si l'arbre est vide.
    """
  1. Créer la fonction plus_grand_element dont la spécification est
1
2
3
4
5
6
def plus_grand_element(a: Noeud) -> int:
    """
    Retourne le plus grand élément de l'arbre binaire de recherche a.

    Lève une exception si l'arbre est vide.
    """
  1. Créer la fonction est_abr dont la spécification est
1
2
3
4
def est_abr(a: Noeud) -> bool:
    """
    Détermine si l'arbre a est un arbre binaire de recherche.
    """

Corrigé

Corrigé de l'activité

À retenir

Un arbre binaire de recherche est un arbre binaire contenant des valeurs ordonnées, tel que tout nœud contient une valeur plus grande (respectivement plus grande ou égale) que les valeurs de tous les nœuds situés dans son sous-arbre gauche. Cette organisation permet de procéder par dichotomie, en n’ayant à considérer qu’un seul sous-arbre pendant une opération. Lorsqu’ils sont équilibrés, les arbres binaires de recherche constituent une méthode très efficace pour stocker et rechercher de l’information.

Voir également