"""Module implémentant un Arbre Binaire (AB) avec un modèle objet"""# Déclaration de la classe ABclassAB:"""Implémentation d'un Arbre Binaire"""def__init__(self,valeur,gauche,droite):self.v=valeurself.g=gaucheself.d=droitedefrelier_a_gauche(self:'AB NON VIDE',sous_arbre:'AB'):"""Méthode qui définit sous_arbre comme le sous-arbre gauche de l'arbre actif"""self.g=sous_arbredefrelier_a_droite(self:'AB NON VIDE',sous_arbre:'AB'):"""Méthode qui définit sous_arbre comme le sous-arbre droite de l'arbre actif"""self.d=sous_arbredefdefinir_donnees_racine(self:'AB',element:'Element NON VIDE'):"""Méthode qui place l'élément NON VIDE en tant que racine de l'arbre"""self.v=elementdefvider(self:'AB'):"""Méthode qui transforme l'arbre en AB VIDE"""self.v=Noneself.g=Noneself.d=None# Fonctions d'interface (accessibles de l'extérieur)defcontenu(noeud:'Noeud')->'Element':"""Renvoie le contenu associé au noeud fourni"""returnnoeud.vdefnv_ABV()->'AB VIDE':"""Renvoie un nouvel Arbre Binaire Vide"""returnAB(None, None, None)
defnv_AB(valeur:'Element',g:'AB',d:'AB')->'AB NON VIDE':"""Renvoie un nouvel Arbre Binaire dont la racine porte valeur et qui mène aux sous-arbres g et d"""returnAB(valeur,g,d)defest_ABV(arbre:'AB')->bool:"""Prédicat qui renvoie True si l'arbre est vide"""returnarbre.visNoneandarbre.gisNoneandarbre.disNonedefracine(arbre:'AB NON VIDE')->'Noeud':"""Renvoie le noeud-racine de l'Arbre Binaire NON VIDE"""returnarbre# ici arbre et racine sont des aliasdefgauche(arbre:'AB NON VIDE')->'AB':"""Renvoie le sous-arbre gauche de l'Arbre Binaire NON VIDE"""returnarbre.gdefdroite(arbre:'AB NON VIDE')->'AB':"""Renvoie le sous-arbre droite de l'Arbre Binaire NON VIDE"""returnarbre.d# Programme de test du moduleif__name__=="__main__":ad=nv_AB("D", nv_ABV(), nv_ABV())af=nv_AB("F",ad, nv_ABV())ae=nv_AB("E",nv_ABV(), af)ah=nv_AB("H", nv_ABV(), nv_ABV())ag=nv_AB("G",nv_ABV(), ah)ab=nv_AB("B", nv_ABV(), nv_ABV())ac=nv_AB("C",ag,ab)aa=nv_AB("A",ac,ae)print(contenu(racine(gauche(aa))))
Le jour de l'examen, comment parvenir à retrouver la fonction récursive hauteur() ?
Définir le cas de base : quand peut-on répondre définitivement ?
Si l'Arbre Binaire est un arbre VIDE, on peut répondre : la hauteur est -1 si on prend comme convention une profondeur de 0 pour la racine.
Définir le cas récursif
Si on tombe sur un Noeud, il peut pas répondre immédiatement il doit lancer des appels récursifs. Lesquels ?
Le cas récursif doit répondre 1 + la plus grande de deux hauteurs à gauche et à droite.
Version Python
1
2
3
4
5
6
7
defhauteur(arbre:'AB',profondeur_vide=-1)->int:"""Renvoie la hauteur de l'Arbre Binaire"""ifest_ABV(arbre):returnprofondeur_videelse:return1+max(hauteur(gauche(arbre)),hauteur(droite(arbre)))
Exemple détaillé avec la recherche de la hauteur de l'Arbre ag, l'Arbre dont la racine est G.
On notera AV l'arbre vide.
hauteur(ag) va renvoyer 1 + max(hauteur(gauche(ag)), hauteur(droite(ag))), ce qui devient
hauteur(ag) va renvoyer 1 + max(hauteur(AV), hauteur(ah)).
Comment rédiger cela sans y passer 15 minutes ? Deux façons de faire :
Réponse textuelle : noter les appels récursifs au fur et à mesure, en laissant de la place pour des informations qu'on remplira ultérieurement. De cette façon, on pourra noter les résultats en dessous au fur et à mesure des réponses définitives lors d'un dépilement.
Réponse graphique : encore plus court, mais difficile à comprendre tant qu'on a pas compris la notion d'appels récursifs.
Commençons avec un exemple de rédaction de réponse complète si on vous demande des justifications :
On écrit les appels dans l'ordre d'apparition :
Ensuite, on rajoute les réponses lorsqu'elles arrivent lors du dépilement.
Il suffit alors de compléter votre rédaction au fur et à mesure des réponses qu'on parvient à obtenir.
Au final, on aura alors quelque chose qui ressemble à cela :
01° Réaliser l'empilement des appels récursifs de la fonction hauteur() sur l'arbre de racine A, qu'on notera aa. Fournir ensuite la réponse finale que va renvoyer la fonction.
1
2
3
4
5
6
7
defhauteur(arbre:'AB',profondeur_vide=-1)->int:"""Renvoie la hauteur de l'Arbre Binaire"""ifest_ABV(arbre):returnprofondeur_videelse:return1+max(hauteur(gauche(arbre)),hauteur(droite(arbre)))
Pour avoir moins de choses à noter, écrire h() plutôt que hauteur().
02° Recopier cet arbre et noter sur les arêtes parent-enfant la réponse que l'enfant va faire à son parent sur les appels à la fonction hauteur() sur l'arbre aa. Fournir ensuite la réponse finale que va renvoyer la fonction.
...CORRECTION...
03° En utilisant uniquement les fonctions d'interface (et sans regarder le code déjà fourni), retrouver le code de la fonction récursive hauteur().
Pour cela, demandez-vous quel est (ou sont) le (ou les) cas de base qui permettent de répondre immédiatement. Réflechir ensuite à la réponse qu'on peut fournir dans le cas récursif.
...CORRECTION...
1
2
3
4
5
6
7
defhauteur(arbre:'AB',profondeur_vide=-1)->int:"""Renvoie la hauteur de l'Arbre Binaire"""ifest_ABV(arbre):returnprofondeur_videelse:return1+max(hauteur(gauche(arbre)),hauteur(droite(arbre)))
3 - Recherche recursive de la taille de l'Arbre Binaire
04° Fournir le code d'une fonction taille() où le seul cas de base est l'arbre vide : on doit renvoyer 0 puisqu'il ne contient pas de noeud.
...CORRECTION...
1
2
3
4
5
6
7
deftaille(arbre:'AB')->int:"""Renvoie la taille de l'Arbre Binaire"""ifest_ABV(arbre):return0else:return1+taille(gauche(arbre))+taille(droite(arbre))
05° Fournir le code d'une fonction taille() qui possède deux cas de base : l'arbre vide (on renvoie 0) et la feuille (on renvoie 1).
Pour un humain, est-ce une meilleure ou une moins bonne version que la précédente ?
Pour la machine, est-ce une meilleure ou une moins bonne version que la précédente ?
...CORRECTION...
1
2
3
4
5
6
7
8
9
deftaille(arbre:'AB')->int:"""Renvoie la taille de l'Arbre Binaire"""ifest_ABV(arbre):return0elifest_ABV(gauche(arbre))andest_ABV(droite(arbre)):return1else:return1+taille(gauche(arbre))+taille(droite(arbre))
Bien mieux pour un humain qui rédige car on atteint plus vite un cas de base (la feuille) qu'on parvient à détecter visuellement et facilement.
Par contre, pour la machine c'est moins bien : on tombe plus souvent sur des noeuds qui ne sont pas des feuillles. Et donc :
Avec cette version à deux cas de base, on effectue 3 vérifications avant de passer la main;
Avec la version précédente, on effectue uniquement 1 vérification (suis-le vide ?)
La version à un seul cas de base va donc être plus rapide.
06° Réaliser l'empilement des appels récursifs de la fonction taille() sur l'arbre de racine A, qu'on notera aa. Fournir ensuite la réponse finale que va renvoyer la fonction. On prendra l'algorithme à deux cas de base pour limiter les appels récursifs : sur le cas d'une feuille, on peut répondre immédiatement.
...CORRECTION...
07° Dessiner l'arbre ci-dessous et noter sur les arêtes parent-enfant le résultat que l'enfant envoie à son parent lors de l'utilisation de taille(). Fournir ensuite la réponse finale que va renvoyer la fonction. On prendra l'algorithme à deux cas de base pour limiter les appels récursifs : sur le cas d'une feuille, on peut répondre immédiatement.
un noeud n'ayant que des sous-arbres vides (une feuille donc) ?
08° Que doit renvoyer la fonction récursive nb_f() qui compte les feuilles sur le cas de base "Arbre Vide" ? Sur le cas de base "Feuille" ? Comment tester qu'un arbre est en réalité constitué uniquement d'une feuille ?
Fournir alors le code de la fonction récursive nb_f().
...CORRECTION...
Si l'arbre est vide : on renvoie 0.
Si l'arbre est constitué uniquement une feuille : on renvoie 1.
L'arbre est constitué d'une feuille si les sous-arbres gauche et droite sont des arbres vides.
Sinon, c'est le cas récursif : on renvoie la somme de l'appel de cette fonction à gauche et de l'appel à droite.
1
2
3
4
5
6
7
8
9
defnb_f(arbre:'AB')->int:"""Renvoie le nombre de feuilles de l'Arbre Binaire"""ifest_ABV(arbre):return0elifest_ABV(gauche(arbre))andest_ABV(droite(arbre)):return1else:returnnb_f(gauche(arbre))+nb_f(droite(arbre))
09° Réaliser l'empilement des appels récursifs de la fonction nb_f() sur l'arbre de racine A, qu'on notera aa. Fournir ensuite la réponse finale que va renvoyer la fonction.
...CORRECTION...
10° Recopier cet arbre et noter sur les arêtes parent-enfant les réponses que fournissent les enfants à leur parent sur un appel de la fonction nb_f() sur l'arbre aa. Fournir ensuite la réponse finale que va renvoyer la fonction.
...CORRECTION...
11° Réaliser le prédicat est_present(). La fonction doit renvoyer True si la clé fournie apparaît bien dans l'Arbre Binaire.
Cas de base 1 : si l'arbre est vide, on renvoie False car elle ne peut être là.
Cas de base 2 : la clé de la racine a la bonne valeur, on renvoie True
Cas récursif :
On renvoie le résultat de la recherche sur l'arbre gauche OU de la recherche dans l'arbre droite.
Question subsidiaire : quel est le seul cas où un OU renvoie False ?
1
2
3
defest_present(arbre:'AB',c:'Cle')->bool:"""Prédicat : True si c est la clé d'un des noeuds"""pass
...CORRECTION...
1
2
3
4
5
6
7
8
9
defest_present(arbre:'AB',c:'Cle')->bool:"""Prédicat : True si c est la clé d'un des noeuds"""ifest_ABV(arbre):returnFalseelifcontenu(racine(arbre))==c:returnTrueelse:returnest_present(gauche(arbre),c)orest_present(droite(arbre),c)
12° Réaliser l'empilement des appels récursifs de la fonction est_present() sur l'arbre de racine A en recherchant la clé "B". Fournir ensuite la réponse finale que va renvoyer la fonction. N'oubliez pas que le OU et le ET sont paresseux en Python : si la lecture du premier terme permet de répondre, on n'évalue pas le deuxième terme.
...CORRECTION...
13° Réaliser l'empilement des appels récursifs de la fonction est_present() sur l'arbre de racine A en recherchant la clé "C". Fournir ensuite la réponse finale que va renvoyer la fonction. N'oubliez pas que le OU et le ET sont paresseux en Python.
Fonctions réciproques : logarithme et exponentielle
14° Calculer ceci avec Python.
>>> from math import log2
>>> log2(2**3)
>>> log2(2**8)
>>> log2(2**27)
Que vaut log2(2x) visiblement ?
...CORRECTION...
>>> from math import log2
>>> log2(2**3)
3.0>>> log2(2**8)
8.0>>> log2(2**27)
27.0
Ce n'est pas une démonstration, mais on constate qu'on peut certainement écrire log2(2x) = x.
15° Calculer ceci avec Python.
>>> from math import log2
>>> 2**(log2(3))
>>> 2**(log2(13))
>>> 2**(log2(23))
Que vaut 2log2(x) à priori ?
...CORRECTION...
>>> from math import log2
>>> 2**(log2(3))
3.0>>> 2**(log2(13))
13.0>>> 2**(log2(26))
25.999999999999993
Ce n'est pas une démonstration, mais on constate qu'on peut certainement écrire 2log2(x) = x.
Le dernier cas pourrait être interprété comme le fait que la formule n'est pas totalement exacte, mais il s'agit bien entendu d'un exemple de l'inexactitude des calculs sur les flottants par un ordinateur.
Influence de la forme des Arbres binaires
Les rappels ci-dessous sont à lire uniquement si vous ne savez plus de quoi ils parlent.
(Rappel).1 Vocabulaire : quelques arbres particuliers
Arbre binaire parfait
Un arbre est parfait lorsqu'il ne manque aucun noeud.
Arbre binaire complet
On dit qu'un est complet lorsque les noeuds manquants sont uniquement sur son dernier étage.
Arbre binaire dégénéré
Lorsque chaque noeud possède au maximum un seul enfant, on dit qu'il est dégénéré.
Arbre binaire filiforme
Lorsque l'arbre comporte uniquement des sous-arbres d'un côté identique, on dit qu'il est filiforme. Il pourrait représenter une liste.
D'autres définitions existent !
Mais ce vocabulaire n'est pas si défini que cela. Certains utilisent des termes différents. On trouve parfois :
Un arbre binaire parfait est un arbre binaire où le dernier niveau est entièrement composé de Feuilles.
Exemple avec un Arbre Binaire Parfait de hauteur 3 si on prend une profondeur de 1 pour la racine :
L'arbre binaire parfait est la meilleure configuration en terme de réduction de la hauteur.
Pour un Arbre Binaire Parfait, la hauteur h est liée au log2 de la taille n quelque que soit la convention :
h = log2(n+1)(avec la convention PR=1)
Sur l'exemple : h = log2(7+1)= log2(8) = log2(23) = 3
Pour un arbre binaire parfait, la taille n est liée à une puissance de 2 de la hauteur h quelque que soit la convention. Avec la convention choisie ici, cela donne :
L'Arbre Binaire Filiforme est la pire configuration en terme de réduction de la hauteur.
h = n(avec la convention PR=1)
(Rappel).4 Formules à connaître par coeur sur l'Arbre Binaire
Le plus simple est d'apprendre par coeur ces trois formules, valables avec la convention profondeur 1 :
Arbre binaire filiforme :
h = n(avec convention profondeur 1)
Arbre binaire complet :
h = log2(n+1)(avec convention profondeur 1)
n = 2h - 1(avec convention profondeur 1)
(Rappel).5 Encadrements de la hauteur d'un Arbre Binaire
Les deux cas extrêmes
Les deux cas extrêmes sont :
Arbre binaire filiforme : chaque noeud n'a qu'un seul enfant au maximum.
A savoir : pour un arbre filiforme, l'ordre de grandeur de h est en n.
Arbre binaire parfait : tous les noeuds possèdent deux enfant, hormis les noeuds de l'étage le plus bas qui sont tous des feuilles.
A savoir : pour un arbre parfait, l'ordre de grandeur de h est en lg(n).
Un arbre binaire quelconque est situé entre ces deux cas extrêmes. On peut encadrer la hauteur de l'arbre binaire quelconque à l'aide de l'une des formules suivantes.
Encadrement avec une profondeur 1 pour la racine (à savoir utiliser)
Arbre binaire complet : h = log2(n+1)
Arbre binaire filiforme : h = n
Pour un arbre binaire quelconque :
⌈log2(n+1)⌉ ≤ h ≤ n(formule valable si profondeur 1 pour la racine)
Les signes ⌈ ⌉ indiquent un arrondi à l'entier supérieur (demi-crochets vers le haut).
>>> import math
>>> math.log2(15+1)
4.0# n = 15 donc h=4 au minimum>>> math.log2(16+1)
4.087462841250339# Pour n = 16, on arrondi à 5 : h=5 au minimum
On voit alors qu'un arbre de 15 noeuds a une hauteur comprise dans [4; 15].
Par contre, avec 16 noeuds, on obtient une hauteur comprise dans [5;16].
C'est normal : avec 15 noeuds, l'arbre peut être parfait. Si on en rajoute un, il faut nécessairement rajouter un étage...
Encadrement avec une profondeur 0 pour la racine (à savoir utiliser)
Arbre binaire complet : h = log2(n+1) - 1
Arbre binaire filiforme : h = n - 1
Pour un arbre binaire quelconque :
⌈log2(n+1)⌉ - 1 ≤ h ≤ n - 1(formule valable si profondeur 0 pour la racine)
>>> import math
>>> math.log2(15+1)
4.0# Pour n = 15, on calcule donc h=(4-1)=3 au minimum>>> math.log2(16+1)
4.087462841250339# Pour n = 16, on arrondit à 5 et on calcule h=(5-1)=4 au minimum
Ou encore cette autre formule :
⌊log2(n)⌋ ≤ h ≤ n - 1(formule valable si profondeur 0 pour la racine)
Les signes ⌊ ⌋ indiquent un arrondi à l'entier inférieur (demi-crochets vers le bas).
>>> import math
>>> math.log2(15)
3.9068905956085187Pour n=15, on arrondit donc à 3 et on a h=3 au minimum>>> math.log2(16)
4.0Pour n=16, on arrondit donc à 4 et on a h=4 au minimum
Ordre de grandeur de la hauteur d'un arbre (à connaître par coeur)
L'ordre de grandeur de la hauteur d'un Arbre Binaire est compris entre log2(n) et n.
16-A° Si on ne sait pas comment cibler la recherche dans un arbre binaire (c'est à dire utiliser la différence de position entre sous-arbre gauche et droite), quel est le coût (dans le pire des cas) d'une recherche NON CIBLEE qui doit trouver un noeud dans un arbre binaire de taille n ?
Constant en n
Logarithmique en n
Linéaire en n
Quadratique en n
Exponentiel en n
Factoriel en n
...CORRECTION...
On doit fouiller l'arbre binaire noeud par noeud.
Le coût est donc linéaire par rapport à n.
Si on ne sait pas comment utiliser les propriétés de l'arbre, qu'on ai un arbre binaire parfait ou filiforme, il faut donc chercher un peu au hasard.
16-B° Une recherche NON CIBLEE dans un arbre binaire est-elle efficace que dans une Listen ?
...CORRECTION...
Non, puisque la recherche dans une Liste est également à coût linéaire par rapport au nombre n de Places/Cases/Cellules.
Regardons maintenant la différence d'efficacité si on sait où chercher.
17-A° Répondre à ces questions :
Si on sait où cibler la recherche, combien de noeuds va-t-on parcourir pour atteindre le noeud du père de la mère d'Alice ?
Combien de choix de destination à faire dans le pire des cas si on a un Arbre de hauteur 12 ?
Quel est le coût d'une recherche CIBLEE par rapport à la hauteur h d'un Arbre Binaire quelconque ?
Constant en h
Logarithmique en h
Linéaire en h
Quadratique en h
Exponentiel en h
Factoriel en h
...CORRECTION...
Lorsqu'on sait de quel côté descendre, on divise globalement le nombre de noeuds encore à fouiller par deux.
En partant de la racine Alice, on a trois choses à faire :
Partir à droite pour trouver la mère d'Alice
Partir à gauche pour trouver le père de la mère
Renvoyer le Noeud s'il existe.
Avec cet arbre de hauteur 3, on a donc 3 noeuds à parcourir.
Avec un arbre de hauteur 12, on aura 12 noeuds.
On a donc un coût linéaire par rapport à la hauteur : O(h).
17-B° Le coût d'une recherche ciblée dans un arbre binaire est donc linéaire en h.
Est-ce que la convention choisie a une influence sur ce coût asymptotique ?
...CORRECTION...
Aucune différence car lorsqu'on détermine le coût, on cherche ce qui se passe lorsque h devient très grand.
O(h-1) = O(h+1) = O(h) : on ne tient pas compte des facteurs constants.<
Tout ce qui nous intéresse est de savoir globalement comment l'algorithme va réagir à l'augmentation des données.
Ici, c'est bien linéaire en h à chaque fois.
17-C° En déduire le coût de la recherche CIBLEE dans un arbre binaire FILIFORME en fonction de la taille n de l'arbre. Choisir parmi :
Constant en n
Logarithmique en n
Linéaire en n
Quadratique en n
Exponentiel en n
Factoriel en n
...CORRECTION...
La complexité de la recherche ciblée est O(h).
Dans le cas filiforme, h est de l'ordre de n quelque soit la convention.
La complexité de la recherche est donc O(n) et donc linéaire par rapport à n.
17-D° En déduire le coût de la recherche CIBLEE dans un arbre binaire PARFAIT en fonction de la taille n de l'arbre. Choisir parmi :
Constant en n
Logarithmique en n
Linéaire en n
Quadratique en n
Exponentiel en n
Factoriel en n
...CORRECTION...
La complexité de la recherche ciblée est O(h).
Dans le cas parfait, h est de l'ordre de log2(n) quelque soit la convention.
La complexité de la recherche est donc O(log2(n)), logarithmique par rapport à n.
Le coût de la recherche ciblée dans un arbre quelconque est donc compris entre le coût logarithmique et le coût linéaire.
5 Coût d'une recherche dans un Arbre binaire
Résumé à connaître par coeur
Recherche non ciblée est linéaire en n : 𝓞(n)
Recherche ciblée est linéaire en h :
Sur un arbre binaire parfait : 𝓞(h) donc 𝓞(log2(n))
Sur un arbre binaire filiforme : 𝓞(h) donc 𝓞(n)
Sur un arbre binaire quelconque : 𝓞(h) donc 𝓞(n)
Il faut différencier la recherche non ciblée et ciblée.
Recherche non ciblée
La recherche non ciblée est linéaire en n, aucun avantage par rapport à une Liste.
On peut noter 𝓞(n) puisqu'on peut tomber par hasard sur le bon résultat avant la fin de l'exploration.
Recherche ciblée
La recherche ciblée est linéaire en h, cela veut dire que son efficacité va dépendre de la forme réelle de l'arbre.
On peut noter 𝓞(h) puisqu'on peut tomber par hasard sur le bon résultat avant la fin de l'exploration.
Mais attention, cette fois la valeur de h va dépendre de la forme de l'arbre :
Si l'AB est parfait, h dépend de log2(n) et le coût de la recherche est donc logarithmique en n.
On peut noter 𝓞(log2(n))
Si l'AB est filiforme, h dépend de n et le coût de la recherche est donc linéaire en n.
On peut noter 𝓞(n)
Si l'AB est quelconque, on est entre les deux et la recherche est donc linéaire en n.
On peut noter 𝓞(n)
Exercices optionnels de rappels d'utilisation des formules
16° Montrer qu'on peut passer de la formule (1) à la formule (2).
h = log2(n+1)( Formule 1)
n = 2h - 1( Formule 2)
...CORRECTION...
On part de cela la formule 1.
Formule (1)
h = log2(n+1)
2h = 2log2(n+1)
On sait que 2log2(x) = x car ces deux fonctions sont reciproques.
2h = n + 1
Il suffit de passer +1 dans le membre de gauche :
2h - 1 = n
On obtient bien la formule (2) :
n = 2h - 1
17° Quelle est la taille n d'un arbre binaire parfait de hauteur 11 (avec la convention pR = 1) ?
...CORRECTION...
>>> 2**(11) - 1
2047
Un arbre binaire parfait de 11 étages possède donc 2047 Noeuds.
Comment calculer cela sans calculatrice ? Il faut énumérer les cas un par un jusqu'au bon.
2**1 = 2.
2**2 = 4.
2**3 = 8.
2**4 = 16.
2**5 = 32.
2**6 = 64.
2**7 = 128.
2**8 = 256.
2**9 = 512.
2**10 = 1024.
2**11 = 2048.
18° Un Arbre Binaire de 500 Noeuds peut-il être parfait ?
...CORRECTION...
On peut faire un raisonnement par l'absurde :
Hypothèse : on considère qu'il l'est. On peut donc utiliser la formule permettant de trouver sa hauteur :
h = log2(n+1)(avec PR=1)
Il faut donc calculer h = log2(500+1) sans calculatrice.
On sait que :
log2(1) = log2(20) = 0
log2(2) = log2(21) = 1
log2(4) = log2(22) = 2
log2(8) = log2(23) = 3
log2(16) = log2(24) = 4
log2(32) = log2(25) = 5
log2(64) = log2(26) = 6
log2(128) = log2(27) = 7
log2(256) = log2(28) = 8
log2(512) = log2(29) = 9
Puisque la fonction log2 est croissante et continue, on en déduit que h = log2(501) ne peut pas être entière car cela vaut 8,...
On tombe sur un résultat qui n'est pas un entier : c'est donc que l'hypothèse est fausse, un arbre binaire de 500 noeuds ne peut pas être un Arbre Binaire Parfait.
Remarque :
>>> from math import log2
>>> log2(500+1)
8.968666793195208
Activité publiée le 13 12 2020
Dernière modification : 13 12 2020
Auteur : ows. h.