Upload files to "DEV.3.2/cours"
This commit is contained in:
90
DEV.3.2/cours/8.Arbres binaires de recherches.md
Normal file
90
DEV.3.2/cours/8.Arbres binaires de recherches.md
Normal file
@@ -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`.
|
||||
|
||||

|
||||
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<T extends Comparable<T>> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user