**Principe** On prend pour hypothèse que les clés sont ordonnées. Un arbre binaire possède la propriété "de recherche" si tous ses nœuds vérifient les deux propositions suivantes : * l'étiquette de ce nœud est plus grande que les étiquettes de tous les nœuds de son sous-arbre gauche. * l'étiquette de ce nœud est plus petite que les étiquettes de tous les nœuds de son sous-arbre droit. Cette forme permet d'obtenir toutes les étiquettes dans l'ordre croissant par un parcours en profondeur infixe, donc elle est équivalente à une liste triée. Il est plus facile de maintenir cette propriété lors d'un ajout ou d'une suppression que pour une structure linéaire, car de nombreuses formes sont possibles. Un **ABR** repose sur une règle de structure stricte : pour chaque nœud, les éléments à gauche sont plus petits et les éléments à droite sont plus grands. Sans l'interface [`Comparable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html), l'arbre ne saurait pas comment ranger un objet personnalisé (comme une classe `Etudiant` ou `Produit`), car il ne saurait pas si `Etudiant A` est "avant" `Etudiant B`. ![image d'arbre de recherche](abr%20img.png) Une autre application de cette structure est la recherche d'élément. Il suffit de descendre dans l'arbre une seule fois pour savoir si une valeur donnée est présente. Le coût de cette recherche est proportionnel à la hauteur de l'arbre. La hauteur minimum d'un arbre binaire à n nœuds est *log2n* et la hauteur maximum est *n*. On peut *équilibrer* un arbre binaire pour diminuer sa hauteur. **Dictionnaires** Un arbre binaire de recherche peut représenter un dictionnaire. Les étiquettes sont les paires clé-valeur et l'ordre des étiquettes est l'ordre des clés. Pour faire l'opération *put*, on descend dans l'arbre à la recherche de la clé fournie. Si on trouve la clé, on remplace sa valeur. Si on ne la trouve pas, on ajoute un nœud là où la recherche s'est arrêtée. Pour faire *get*, on recherche la clé et on a immédiatement notre réponse. Pour faire *remove*, on recherche la clé. * Si le nœud est une feuille, on le retire directement. * Si le nœud a un enfant, il est remplacé par celui-ci. * Si le nœud a deux enfants, on le remplace par son *successeur directe* (qui est retiré d'abord). Il faut trouver le successeur direct à la racine que l'on souhaite supprimé, en allant une fois à droite puis au plus à gauche possible. Exemple : ```java public class ArbreDeRecherche> { private class Noeud { T valeur; Noeud gauche, droite; Noeud(T valeur) { this.valeur = valeur; } } private Noeud racine; public void remove(T valeur) { racine = supprimerRecursif(racine, valeur); } private Noeud supprimerRecursif(Noeud actuel, T valeur) { if (actuel == null) return null; int comparaison = valeur.compareTo(actuel.valeur); if (comparaison < 0) { // On cherche à gauche actuel.gauche = supprimerRecursif(actuel.gauche, valeur); } else if (comparaison > 0) { // On cherche à droite actuel.droite = supprimerRecursif(actuel.droite, valeur); } else { // ÉLÉMENT TROUVÉ : On applique les 3 cas de suppression // Cas 1 & 2 : Pas d'enfant ou un seul enfant if (actuel.gauche == null) return actuel.droite; if (actuel.droite == null) return actuel.gauche; // Cas 3 : Deux enfants // 1. Trouver le successeur (le plus petit du sous-arbre droit) actuel.valeur = trouverSuccesseur(actuel.droite); // 2. Supprimer le successeur dans le sous-arbre droit actuel.droite = supprimerRecursif(actuel.droite, actuel.valeur); } return actuel; } private T trouverSuccesseur(Noeud racine) { T valeurMin = racine.valeur; // On descend au plus profond à gauche avec une boucle while while (racine.gauche != null) { valeurMin = racine.gauche.valeur; racine = racine.gauche; } return valeurMin; } } ```