Arbres binaires de recherche¶
On considère le type suivant :
type 'a abr =
| Noeud of 'a abr * 'a * 'a abr
| Nil
On reprend la définition du cours : un arbre binaire est un arbre binaire de recherche (abr) s'il est vide ou si ses deux sous-arbres sont des abr et si l'étiquette \(x\) de sa racine vérifie \(\max_{y \in g} y \le x \le \min_{y \in d} y\), où \(g\) et \(d\) sont les sous-arbres gauche et droit (on autorise donc les doublons).
Question 1. Écrire une fonction etendue qui à tout arbre a
non vide renvoie le couple (min_v, max_v) constitué des valeurs minimale
et maximale de ses étiquettes.
Corrigé
let rec etendue a =
match a with
| Nil -> failwith "Arbre vide"
| Noeud(Nil,x,Nil) -> (x,x)
| Noeud(a',x,Nil) | Noeud(Nil,x,a') ->
let min_a', max_a' = etendue a' in
(min min_a' x, max max_a' x)
| Noeud(g,x,d) ->
let min_g, max_g = etendue g in
let min_d, max_d = etendue d in
(min (min min_g min_d) x,
max (max max_g max_d) x)
Question 2. En déduire une fonction est_abr1 qui renvoie un booléen
indiquant si un arbre binaire est un abr.
Corrigé
let rec est_abr1 a =
match a with
| Nil | Noeud(Nil,_,Nil) -> true
| Noeud(g,x,Nil) ->
let _, max_g = etendue g in
est_abr1 g && max_g <= x
| Noeud(Nil,x,d) ->
let min_d, _ = etendue d in
est_abr1 d && x <= min_d
| Noeud(g, x, d) ->
let _, max_g = etendue g in
let min_d, _ = etendue d in
est_abr1 g && est_abr1 d && max_g <= x && x <= min_d
Question 3. Expliquer pourquoi cette fonction n'est pas efficace et en
déduire une version est_abr linéaire dans le nombre de nœuds de l'arbre
binaire.
Corrigé
est_abr1 appelle etendue sur chaque sous-arbre, ce qui parcourt à
nouveau tout ce sous-arbre : chaque nœud est visité autant de fois qu'il a
d'ancêtres, d'où une complexité quadratique dans le pire cas (pour un
peigne). On renvoie plutôt, en un seul parcours, le booléen et l'étendue.
let rec est_abr_aux a =
match a with
| Nil -> failwith "Arbre vide"
| Noeud(Nil,x,Nil) -> (true, x, x)
| Noeud(g,x,Nil) ->
let abr_g, min_g, max_g = est_abr_aux g in
(abr_g && max_g <= x, min_g, x)
| Noeud(Nil,x,d) ->
let abr_d, min_d, max_d = est_abr_aux d in
(abr_d && x <= min_d, x, max_d)
| Noeud(g, x, d) ->
let abr_g, min_g, max_g = est_abr_aux g in
let abr_d, min_d, max_d = est_abr_aux d in
(abr_g && abr_d && max_g <= x && x <= min_d,
min_g, max_d)
let est_abr a =
match a with
| Nil -> true
| _ -> let b, _, _ = est_abr_aux a in b
Question 4. Écrire une fonction contient qui teste si une valeur est
présente dans un abr.
Corrigé
Deux versions possibles, puis une version à éviter :
let rec contient x a =
match a with
| Nil -> false
| Noeud(_,y,_) when x = y -> true
| Noeud(g,y,_) when x < y -> contient x g
| Noeud(_,y,d) -> contient x d
let rec contient x a =
match a with
| Nil -> false
| Noeud(g,y,d) ->
x = y || (if x < y then contient x g else contient x d)
(* à éviter : *)
let rec contient x a =
match a with
| Nil -> false
| Noeud(g,y,d) ->
if x = y
then true (* perdu *)
else if x < y
then contient x g
else if x > y
then contient x d
else false (* impossible a atteindre *)
Question 5. Écrire une fonction insere qui rajoute un élément dans un
abr et renvoie le nouvel abr.
Corrigé
let rec insere x a =
match a with
| Nil -> Noeud(Nil, x, Nil)
| Noeud(g,y,d) ->
if x <= y
then Noeud(insere x g, y, d)
else Noeud(g, y, insere x d)
Question 6. Écrire une fonction supprime qui supprime un élément d'un
abr et renvoie le nouvel abr. Si jamais l'élément n'est pas dedans, cette
fonction se comporte comme l'identité.
On pourra définir une fonction plus_a_droite qui renvoie le nœud le plus à
droite d'un abr.
Corrigé
(* On va chercher le noeud le plus à droite y et on le court-circuite en
renvoyant le couple (y, a') où a' est a privé de y *)
let rec plus_a_droite a =
match a with
| Nil -> failwith "Vide"
| Noeud(g,y,Nil) -> (y, g)
| Noeud(g,y,d) ->
let z, d' = plus_a_droite d in
(z, Noeud(g,y,d'))
let rec supprime x a =
match a with
| Noeud(Nil, y, Nil) when x = y -> Nil
| Noeud(g, y, Nil) when x = y -> g
| Noeud(Nil, y, d) when x = y -> d
| Noeud(g, y, d) when x = y ->
let z, g' = plus_a_droite g in
Noeud(g', z, d)
| Noeud(g, y, d) ->
if x < y then Noeud(supprime x g, y, d)
else Noeud(g, y, supprime x d)
| _ -> a
Arbres 2-3 et 2-3-4¶
On définit les deux types suivants :
type 'a a23 =
| Bin23 of 'a a23 * 'a * 'a a23
| Ter23 of 'a a23 * 'a * 'a a23 * 'a * 'a a23
| Nil23
type 'a a234 =
| Bin234 of 'a a234 * 'a * 'a a234
| Ter234 of 'a a234 * 'a * 'a a234 * 'a * 'a a234
| Quat234 of 'a a234 * 'a * 'a a234
* 'a * 'a a234 * 'a * 'a a234
| Nil234
Question 7. Écrire une fonction insere23 qui insère un élément dans un
arbre 2-3 en utilisant un arbre 2-3-4 de manière transitoire.
Corrigé
On commence par des fonctions de conversion :
let rec vers234 a =
match a with
| Nil23 -> Nil234
| Bin23(g,x,d) -> Bin234(vers234 g, x, vers234 d)
| Ter23(g,x,m,y,d) -> Ter234(vers234 g, x, vers234 m, y, vers234 d)
let rec vers23 a =
match a with
| Nil234 -> Nil23
| Bin234(g,x,d) -> Bin23(vers23 g, x, vers23 d)
| Ter234(g,x,m,y,d) -> Ter23(vers23 g, x, vers23 m, y, vers23 d)
| _ -> failwith "Quaternaire"
On insère alors dans une feuille, ce qui peut créer un nœud quaternaire,
que l'on fait remonter en l'éclatant en deux nœuds binaires et en
insérant sa clé médiane dans le père (repare234). Si la racine devient
quaternaire, on l'éclate et la hauteur augmente de 1.
let elim_racine_quat a =
match a with
| Quat234(g,x,m,y,n,z,d) -> Bin234( Bin234(g,x,m), y, Bin234(n,z,d) )
| _ -> a
let rec repare234 a =
match a with
| Bin234(Quat234(g,x,m,y,n,z,d),t,d') ->
Ter234(Bin234(g,x,m), y, Bin234(n,z,d), t, d')
| Bin234(g',t, Quat234(g,x,m,y,n,z,d)) ->
Ter234(g', t, Bin234(g,x,m), y, Bin234(n, z,d))
| Ter234(Quat234(g,x,m,y,n,z,d),x',m',y',d') ->
Quat234(Bin234(g,x,m),y,Bin234(n,z,d),x',m',y',d')
| Ter234(g',x',Quat234(g,x,m,y,n,z,d),y',d') ->
Quat234(g',x',Bin234(g,x,m),y,Bin234(n,z,d),y',d')
| Ter234(g',x',m',y',Quat234(g,x,m,y,n,z,d)) ->
Quat234(g',x',m',y',Bin234(g,x,m),y,Bin234(n,z,d))
| _ -> a
let rec insere23_aux x a =
match a with
| Nil23 -> Bin234(Nil234, x, Nil234)
| Bin23(Nil23,y,Nil23) ->
if x <= y
then Ter234(Nil234,x,Nil234,y,Nil234)
else Ter234(Nil234,y,Nil234,x,Nil234)
| Bin23(g, y, d) -> repare234
(if x <= y then Bin234(insere23_aux x g, y, vers234 d)
else Bin234(vers234 g, y, insere23_aux x d))
| Ter23(Nil23,y,Nil23,z,Nil23) ->
if x <= y
then Quat234(Nil234, x, Nil234, y, Nil234, z, Nil234)
else if x <= z
then Quat234(Nil234, y, Nil234, x, Nil234, z, Nil234)
else Quat234(Nil234, y, Nil234, z, Nil234, x, Nil234)
| Ter23(g,y,m,z,d) -> repare234
(if x <= y
then Ter234(insere23_aux x g, y, vers234 m, z, vers234 d)
else if x <= z
then Ter234(vers234 g, y, insere23_aux x m, z, vers234 d)
else Ter234(vers234 g, y, vers234 m, z, insere23_aux x d))
let insere23 a x = vers23 (elim_racine_quat (insere23_aux x a))