Arbres binaires de recherche
Judicaël Courant 2018-W37-3
1 Rappels
1.1 Arbres binaires
Les arbres binaires sont aussi appelésarbres binaires-unaires. Un exemple d’arbre binaire est donné figure 1.
Un arbre binaire (non-vide) est formé denœuds.
Un arbre est soitvideet ne possède aucun nœud, soitnon videet possède alors un unique nœudracine, qui a lui-même unfils-gaucheet unfils-droit qui sont deux arbres.
Les nœuds dont les deux fils sont vides sont appelésnœuds externes(oufeuilles). Les autres sont appelésnœuds internes.
Un nœud peut posséder0,1ou2nœuds fils.
1.2 Arbres étiquetés
Étant donné un ensembleα, on peut étiqueter les nœuds par des éléments deα.
Dans la suite, on ne s’intéressera qu’à des arbres binaires étiquetés.
1.3 Définition formelle
Définition 1.3.1. L’ensembleA(α) des arbres binaires-unaires étiquetés parαest le plus petit ensembleA(α)possédant un élément particulier qu’on notera∅(représentant l’arbre vide) et tel que sigetdsont deux éléments deA(α)etn∈ α, alors (n, g, d)est un arbre1 (voir figure 2 le code OCaml permettant de représenter le type des arbres binaires-unaires).
1.4 Hauteur
Définition 1.4.1. La hauteur de l’arbre vide est 0et tout arbre (n, g, d), où g(resp. d) est de hauteurhg (resp. hd), est lui-même de hauteur 1 + max(hg, hd) (voir figure 2 le code OCaml permettant de calculer la hauteur d’un arbre).
1. De façon à éviter toute ambiguïté, on supposera de plus que l’arbre vide n’est pas un triplet.
a
b c
d f g
e
Figure 1 – Un arbre binaire-unaire.
Type des arbres binaires-unaires étiquetés par des éléments de typeα typeαarbre bin =
| Vide
| N of(αarbre bin) × α × (αarbre bin) hauteur : αarbre bin → int
hauteur arenvoie la hauteur dea let rechauteur a =matchawith
| Vide → 0
| N(g, , d) → 1 + max(hauteur g) (hauteur d) taille : αarbre bin → int
taille arenvoie la taille dea let rectaille a =matchawith
| Vide → 0
| N(g, , d) → 1 + (taille g) + (taille d)
Figure 2 – Arbres binaires-unaires en OCaml.
1.5 Profondeur
Définition 1.5.1. La profondeur d’un nœud d’un arbre est le nombre d’arêtes du plus court chemin reliant ce nœud à la racine.
1.6 Vrai ou faux ?
La hauteur d’un arbre est :
A. la profondeur maximum de ses nœuds ;
B. le nombre maximum de nœuds se trouvant sur un chemin partant de la racine de l’arbre ; C. les deux ;
D. ni l’un, ni l’autre.
1.7 Taille d’un arbre
Définition 1.7.1. La taille de l’arbre vide est son nombre de nœuds, soit 0pour l’arbre vide et 1 +tg+tdpour un arbre(n, g, d)oùdetdsont respectivement de taillestgettd(voir figure 2 le code OCaml permettant de calculer la taille d’un arbre).
1.8 Relation entre taille et hauteur
Théorème 1.8.1. Pour tout arbre de taillenet de hauteurh, on a h≤n≤2h−1
ou, si l’on préfère :
n∈[[h,2h[[
1.9 Arbre parfait
Définition 1.9.1. Un arbre est parfait (on dit parfois aussi complet) si tous les nœuds internes ont deux fils et les nœuds externes sont tous à la même profondeur.
Théorème 1.9.2. Un arbre binaire est parfait si et seulement sin= 2h−1oùhest sa hauteur etnson nombre de nœuds.
1.10 Arbre quasi-complet
Définition 1.10.1. Un arbre (quasi-)complet (ou tas) est un arbre de hauteur h où toutes les feuilles sont à profondeurh−2ouh−1, les feuilles de profondeurh−1étant bien tassées sur la gauche.
a
b c
d e f g
h i j k l
Exemple d’arbre quasi-complet
a
b c
d e f g
h i j k l
Exemple d’arbre non quasi-complet
1.11 Relation taille/hauteur
Théorème 1.11.1. Pour tout arbre (quasi-)complet de hauteurhet de taillen, on a 2h−1≤n≤2h−1
où, si l’on préfère :
n∈[[2h−1,2h[[
1.12 Arbres binaires entiers
Les arbres binaires entiers, appelés aussi arbresstrictement binaires ou arbres hétérogènes, peuvent être vus comme des arbres binaires non-vides dans lesquels tout nœud a soit deux fils vides soit deux fils non-vides.
Donc soit un arbre binaire entier est réduit à une feuille soit sa racine possède un fils-gauche et un fils-droit qui sont deux arbres binaires entiers non-vides.
1.13 Exemples
a
b c
d e
f g
Exemple d’arbre binaire entier.
a
b c
d e f g
h i
Exemple d’arbre binaire entier.
Type des arbres binaires-entiers étiquetés par des éléments de typeαpour les nœuds internes et des éléments de typeβpour les feuilles.
type(α, β)abin ent =
| Feuilleofβ
| NSof(α, β)abin ent × α × (α, β)abin ent hauteur ent : (α, β)abin ent → int
hauteur ent arenvoie la hauteur dea let rechauteur ent a =matchawith
| Feuille → 0
| NS(fg, , fd) → 1 + max(hauteur ent fg) (hauteur ent fd) taille ent : (α, β)arbre → int
taille ent arenvoie la taille dea let rectaille ent a =matchawith
| Feuille → 0
| NS(fg, , fd) → 1 + taille ent fg + taille ent fd
Figure 3 – Arbres binaires entiers en OCaml.
1.14 Arbres binaires entiers étiquetés
Étant donné un ensembleαet un ensembleβ, on peut étiqueter les nœuds (internes) par des éléments deαet les feuilles par des éléments deβ(d’où le nomhétérogène).
Dans la suite, les arbres strictement binaires que nous considérerons seront étiquetés.
1.15 Définition formelle
Définition 1.15.1. L’ensemble A(α, β)des arbres binaires étiquetés parαcomme le plus petit ensembleA(α, β)tel queβ ⊂ A(α, β)et tel que sigetdsont deux éléments deA(α, β)etn∈α, alors(n, g, d)est un arbre2(voir figure 3 un code OCaml pour une implantation possible).
1.16 Hauteur
Définition 1.16.1. La hauteur d’un arbre hétérogène est0si c’est une feuille et1 + max(hg, hd) pour les arbres (n, g, d) où g etd sont respectivement de hauteur hg ethd (voir figure 3 une implantation en OCaml).
1.17 Profondeur d’un nœud
Comme pour les arbres homogènes :
Définition 1.17.1. La profondeur du nœud ou d’une feuille d’un arbre hétérogène est le nombre d’arêtes du plus court chemin reliant ce nœud ou cette feuille à la racine.
2. On supposera queβne contient aucun triplet de façon à éviter toute ambiguïté ; dans le cas contraire, on peut encore construire un ensemble représentant les arbres strictement binaires, au prix de petites complications.
1.18 Vrai ou faux ?
La hauteur d’un arbre hétérogène est : A. la profondeur maximum de ses feuilles ;
B. le nombre maximum de nœuds se trouvant sur un chemin partant de la racine de l’arbre ; C. les deux ;
D. ni l’un, ni l’autre.
1.19 Taille
Définition 1.19.1. La taille d’un arbre hétérogène est définie comme0pour les feuilles et1 + tg+tdpour les arbres(n, g, d)oùgetdsont respectivement de taillesgetd. Autrement dit, c’est le nombre de nœuds internes (voir figure 3 une implantation en OCaml).
Théorème 1.19.2. Un arbrestrictement binairede taillenpossèden+ 1feuilles.
Démonstration. On peut donner au moins trois démonstrations différentes de ce résultats : 1. Par induction.
2. Voir l’arbre comme représentant un tournoi (les nœuds représentent les vainqueurs des différents matchs, les feuilles les compétiteurs). Chaque match permet d’éliminer un com- pétiteur. À la fin, seul un compétiteur n’est pas éliminé. Dont s’il y anmatchs, il y an+ 1 compétiteurs.
3. Considérer la fonction qui associe son père à chaque feuille ou nœud (interne) différent de la racine. Elle réalise une surjection sur l’ensemble des nœud internes. De plus chaque nœud interne possède exactement2antécédents. En notantf le nombre de feuilles, on a doncf+n−1 = 2×n, d’oùf =n+ 1.
2 Arbres binaires de recherche
2.1 Contexte
Le type abstrait d’ensemble fini est d’usage fréquent en informatique. Il s’agit d’un type para- métré par un typeT(le type des éléments), disposant d’une constante représentant l’ensemble vide ainsi que de fonctions permettant d’ajouter ou d’enlever des éléments.
Une façon possible d’implanter des ensembles est d’utiliser une liste : on représente sim- plement l’ensemble{x0, . . . , xn−1}par une liste[x0;. . .;xn−1]. Notez que le choix n’est pas unique lorsquen≥2puisqu’il y an!façons de permuter cette liste.
2.1.1 Complexité de la recherche séquentielle
On s’intéresse à la complexité du test de la présence d’un élément dans une liste d’association
`de longueurn, d’une part dans le cas où cette valeur est bien présente, d’autre part dans le cas où elle ne l’est pas, dans les cas le pire, le meilleur et en moyenne (6 possibilités).
A. Une de ces complexités est enO(1), les cinq autres sont enO(n).
B. Deux de ces complexités sont enO(1), les quatre autres enO(n).
C. Trois de ces complexités sont enO(1), les trois autres enO(n).
D. Toutes ces complexités sont enO(n).
2.1.2 Notion de préordre
Définition 2.1.1. On appellepréordre sur un ensembleE une relation binaire 4réflexive et transitive surE.
Remarque 2.1.2. Un ordre n’est donc rien d’autre qu’un préordre antisymétrique.
Définition 2.1.3. Étant donné un préordre4sur un ensembleE, on peut définir une relation∼ surEpar
∀(x, y)∈E2 x∼y ⇐⇒ (x4yety4x)
Proposition 2.1.4. La relation ainsi définie est une relation d’équivalence surE
Exercice 2.1.5. Que peut-on dire de cette relation∼dans le cas particulier où4est un ordre ? Exemple 2.1.6. SiE etF sont deux ensembles, et4E une relation d’ordre surE, alors on peut définir une relation de préordre4surE×F par
∀((x1, y1),(x2, y2))∈(E×F)2 (x1, y1)4(x2, y2) ⇐⇒ x14E x2
En particulier dès queEpeut être muni d’un ordre,E×F peut être muni d’un préordre.
Exemple 2.1.7. SoitEun ensemble. On peut le munir d’un préordre4à partir de n’importe quel autre ensembleKmuni d’un préordre4Ket d’une fonctionk:E →K par
∀(x, y)∈E2 x4y ⇐⇒ k(x)4K k(y)
La fonctionkest appelée fonctionclé,k(x)laclé associée à xetKl’ensemble des clés.
Exercice 2.1.8. Laquelle de ces affirmations est correcte ? A. L’exemple 2.1.6 est un cas particulier de l’exemple 2.1.7.
B. L’exemple 2.1.7 est un cas particulier de l’exemple 2.1.6.
C. Aucune des deux affirmations précédentes n’est correcte.
Remarque 2.1.9. Informatiquement, la situation précédente est fréquente. Par exemple, lorsqu’on trie par date de naissance des données représentant des personnes, on ne trie pas suivant un ordre mais suivant le préordre sur ces données obtenu en prenant pour ensembleKl’ensemble des dates de naissances, pour fonctionkla fonction qui à chaque donnée représentant une personne associe sa date de naissance, et pour préordre surK l’ordre canonique sur les dates. On dit que laclé de triest la date de naissance.
2.1.3 Une tentative d’amélioration
On suppose maintenant queT est muni d’un ordrepréordre total4. On veut alors stocker les éléments dans l’ensemble à équivalence près, c’est-à-dire que lorsqu’on teste si un élément xest dansE, on doit répondre oui si on a mis dansE unyvérifiantx∼y.
Questions :
1. Peut-on améliorer la complexité des fonctions usuelles de la structure précédente en ne conservant que des listes triées pour4?
2. Peut-on améliorer la complexité de la fonction de test d’appartenance en utilisant des tableaux triés plutôt que des listes ?
A. Oui et oui.
B. Non et oui (respectivement).
C. Oui et non (respectivement).
D. Non et non.
2.2 Définition
On appelle arbre binaire de recherche (permettant d’implanter des ensembles finis) un arbre binaire dont chaque nœud est étiqueté par un élément deT tel que pour tout nœudn:
— l’étiquette denmajore l’ensemble des étiquettes du fils gauche den
— et l’étiquette denminore l’ensemble des étiquettes du fils droit den.
Voir par exemple la figure 4.
2.2.1 Vrai ou faux ?
Un arbre binaire est un arbre binaire de recherche si et seulement si l’étiquette de la racine de chaque fils-gauche est plus petite (au sens large) que celle de son père et celle de chaque fils-droit plus grande (au sens large) que celle de son père.
A. Vrai B. Faux
2.3 Fonctions usuelles sur un abr
Dans cette implantation, on considère que le préordre considéré sur les arbres est donné par une fonction clé, prise en argument par chacune des fonctions considérées.
Pour chercher si un élément est présent, il n’y a pas besoin de donner l’élément lui-même, mais juste sa clé.
Pour l’ajout, il y a un choix à faire au cas où il y a déjà dans l’arbre un élémentyéquivalent à l’élémentxqu’on veut ajouter. On peut :
1. Ne pas modifier l’arbre.
2. Remplaceryparx.
3. Ajouterxen conservanty.
25
17 42
12 33 56
14
Figure 4 – Un arbre binaire de recherche
cherche : (α → κ) → κ → αarbre bin → αoption
cherche k kx acherche dansa, supposé être un arbre binaire de recherche pour la clé de trik, si un élémentxde clékxest présent et renvoieSome x le cas échéant. Dans le cas contraire, cette fonction renvoieNone.
let reccherche k kx a = matchawith
| Vide → None
| N(g, y, d) → letky = k y in
ifkx < kythencherche k kx g else ifky < kxthencherche k kx d elseSome y(∗kx = ky∗)
ajoute : (α → κ) → α → αarbre bin → αarbre bin
ajoute k x aajoutex à l’arbre binaire de recherchea. Si un élément équivalent àxest déjà présent, il est remplacé parx.
let recajoute k x a = matchawith
| Vide → N(Vide, x, Vide)
| N(g, y, d) →
letkx = k x andky = k yin
ifkx < kythenN(ajoute k x g, y, d) else ifky < kxthenN(g, y, ajoute k x d) elseN(g, x, d) (∗kx = ky∗)
Figure 5 – Fonctions d’ajout et de recherche dans un abr.
Nous choisissons la deuxième solution (pour une raison qui apparaîtra plus tard). Le code est donné figure 5.
Pour la suppression, il y a des questions à se poser :
1. Que faire si l’élément qu’on veut enlever n’est pas là ? Échouer ou rendre l’arbre in- changé ? On choisit arbitrairement la deuxième possibilité.
2. L’élément qu’on veut enlever peut-il apparaître plusieurs fois et si oui que doit-on faire : enlever toutes les occurrences ou une seule ? Il est clair que si les arbres que l’on a construit l’ont été à partir de l’arbre vide et de la fonctionajoute, il ne peut y avoir qu’un seul nœud équivalent à l’élément à enlever. Donc dès qu’on trouve un nœud équivalent à l’élément qu’on veut enlever, on peut s’arrêter.
En outre, comme précédemment pour la fonction recherche, on ne donnera pas en argument à la fonction de suppression un élément à supprimer mais simplement la clé de l’élément à supprimer.
La suppression s’écrit assez naturellement. . . sauf dans le cas où la valeur x à enlever est sur un nœud de la formeN(g, x, d)ayant deux filsgetdnon-vides. Dans ce cas, on adopte la stratégie suivante : on travaille sur le fils-gaucheg, afin de calculerg0, qui est l’arbregprivé de
enleve noeud droite : α’arbre bin → α × αarbre bin
Enlève le nœud le plus à droite de l’arbre qui lui est passé en argument. Plus précisément, retourne le couple(x, a)constitué de l’arbre privé de son nœud le plus à droite et de l’étiquettex de ce nœud.
Précondition : l’arbre ne doit pas être vide.
let recenleve noeud droite a = matchawith
| Vide → assert false
| N(g, x, Vide) → x, g
| N(g, x, d) →
lety, d0 = enleve noeud droite din y, N(g, x, d0)
supprime : (α → κ) → κ → αarbre bin → αarbre bin
supprime k kx asupprime de l’arbreal’élément de clékx, s’il existe. Sinon, retourne l’arbre inchangé.
Précondition :aest un arbre binaire de recherche pour la clé de trik. let recsupprime k kx a =
matchawith
| Vide → Vide(∗conforme au premier choix∗)
| N(g, y, d) → letky = k y in
ifkx < kythenN(supprime k kx g, y, d) else ifkx > kythenN(g, y, supprime k kx d) else ifg = Videthend
else(∗g <> Vide et kx = ky∗)
letz, g0 = enleve noeud droite g in N(g0, z, d)
Figure 6 – Suppression d’une clé d’un arbre binaire de recherche.
son nœud le plus à droite, d’étiquettez. On remplace alorsN(g, x, d)parN(g0, z, d). D’où le code donné figure 6.
2.4 Complexité de ces fonctions
Les fonctionsrecherche,ajouteetsupprimetravaillent toutes trois en tempsO(h), oùhest lahauteurde l’arbre qui leur est passé en argument.
Pour un arbre de hauteur h contenant n valeurs, la complexité de ces fonctions est-elle meilleure que dans le cas d’une liste ?
— Ce n’est jamais pire (asymptotiquement) carh=O(n).
— Ce n’est pas mieux sihest de l’ordre den.
— C’est beaucoup mieuxsion peut faire en sorte quehsoit unO(logn).
Pour faire en sorte quehsoit unO(logn), on peut :
— Essayer de faire en sorte que les arbres soient plus équilibrés, typiquement on les rééqui- libre si nécessaire à chaque fois qu’on ajoute ou retire un élément (AVL, arbres rouge- noir) voire quand on accède à un élément (splay tree). (Toutes ces techniques sont hors-
programme. . . mais font de très bons sujets de concours.
— Compter sur le hasard. . .
Pour compter sur la hasard, on peut s’appuyer sur le résultat suivant (hors-programme éga- lement) :
Théorème 2.4.1. On considère une liste denvaleursx0, . . . ,xn−1toutes distinctes. On tire au hasard de façon uniforme une permutationσdansS[[0,n[[. Considérons alors la variable aléatoireH désignant la hauteur de l’arbre de recherche obtenu en ajoutant successivementxσ(0), . . . ,xσ(n−1)
à l’arbre vide. AlorsE(H) =O(logn).
On pourra consulterhttp://tinyurl.com/oku3skk3pour une démonstration longue et délicate mais n’utilisant que des outils mathématiques vus en MPSI.
2.5 Application à l’implantation de tables d’associations
Le type abstrait de tables d’associations est d’usage fréquent en informatique. Il s’agit d’un type paramétré par un typeK (le type des clés) et un typeV (le type des valeurs). Une table d’association dispose de fonctions permettant d’ajouter ou d’enlever des liaisons entre clés et valeurs ainsi que d’obtenir la valeur (voire les valeurs) associée à une clé donnée.
Une façon d’implanter ces tables est d’utiliser un arbre binaire de recherche, stockant des éléments de typeK ×V et dont la clé de tri est la fonction première composante du couple (fst en OCaml).
Exercice 2.5.1. Comment se compare cette implantation en termes de complexité (en temps et en espace) avec les implantations vues en première année ?
Exercice 2.5.2. Vérifier qu’il est effectivement possible d’implanter des tables d’association de cette façon. Quel(s) choix effectué(s) précédemment sur les fonctions sur les abr s’avère(nt) très utile(s) pour implanter ces tables ?
3. Redirigeant surhttp://staff.ustc.edu.cn/~csli/graduate/algorithms/book6/chap13.htm