diff --git a/DEV.3.2/cours/8.Arbres binaires de recherches.md b/DEV.3.2/cours/8.Arbres binaires de recherches.md new file mode 100644 index 0000000..73d312b --- /dev/null +++ b/DEV.3.2/cours/8.Arbres binaires de recherches.md @@ -0,0 +1,90 @@ +**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; + } +} +``` \ No newline at end of file