Favicon
NSI Terminale

Correction : Algorithmes sur les arbres - Mesures

Taille d'un arbre

Version fonction


def taille(arbre):
	if arbre is None:
		return 0
	else:
		return 1 + taille(arbre.filsgauche) + taille(arbre.filsdroit)						
			

Version POO

La première idée est d'écrire :


class ABR(Arbre):
	....
	
	def taille(self)->int:
		if self is None:
			return 0
		else:
			return 1 + self.filsgauche.taille() + self.filsdroit.taille()						
			

Mais ce code génère alors cette erreur : AttributeError: 'NoneType' object has no attribute 'taille'...

Interprétation : lorsqu'on arrive à une feuille, ou à un nœud qui n'a qu'un seul fils, self.filsdroit et/ou self.filsgauche sont égaux à None, à partir duquel on ne peut évidemment pas appeler la méthode taille...

Ce problème ne se posait pas avec la fonction taille, qui est définie "en dehors" de la classe ABR, et "existe" donc toujours, même si les arbres sont égaux à None...

Une première solution

On peut compléter l'algorithme de calcul de taille présenté dans le cours, en tenant compte de toutes les situations que l'on peut rencontrer :


class ABR(Arbre):
	....
	
	def taille(self)->int:
        """Renvoie le nombre de nœuds présents dans un ABR"""
        if self.filsdroit is None and self.filsgauche is None: # le noeud est une feuille : cas de base
            return 1
        elif self.filsdroit is None: # le noeud n'a pas de fils droit,
            return 1 + self.filsgauche.taille() # alors l'appel récursif se fait sur le fils gauche ( qui existe forcément, le nœud n'est pas une feuille ! )
        elif self.filsgauche is None: # nœud qui n'a pas de fils gauche
            return 1 + self.filsdroit.taille()
        else:
            return 1 + self.filsgauche.taille() + self.filsdroit.taille() # nœud qui a ses deux fils
					
			

Mais on perd alors "l'élégance" de la solution à un seul appel récursif !...

Une deuxième solution

On peut tout simplement rajouter un paramètre à la méthode, l'équivalent du paramètre arbre de la fonction :


class ABR(Arbre):
	....
	
	def taille(self, arbre):
		if arbre is None:
			return 0
		else:
			return 1 + self.taille(arbre.filsgauche) + self.taille(arbre.filsdroit)						
			

Lors de l'appel de la méthode, il faudra alors penser à passer l'objet Arbre lui-même en argument :


print(arbre.taille(arbre))					
			

Ce n'est donc pas très élégant...en plus, du point de vue de l'interface, l'utilisateur sait qu'il faut appeler la méthode à partir de l'objet, mais pas qu'il faut aussi le passer en paramètre ! Ce n'est donc pas du tout ergonomique...

Amélioration de l'interface

On garde le même algorithme dans une méthode "privée" à la classe ( donc qui ne fait pas partie de son interface ), et on utilise une autre méthode "publique" pour l'appeler en lui passant la racine de l'arbre au premier appel comme paramètre, comme la fonction taille précédente; cette méthode "publique" renverra également le résultat renvoyé par la méthode "privée" :


class ABR(Arbre):
	....
	
	def taille(self):             # méthode "publique"
		return self._taille(self) # 'self' représente ici la racine de l'arbre
		
	def _taille(self, arbre):     # méthode "privée" ( le caractère underscore '_' signale une méthode "privée", qui ne fait donc pas partie de l'interface )
		if arbre is None:
			return 0
		else:
			return 1 + self._taille(arbre.filsgauche) + self._taille(arbre.filsdroit)						
			

On appelle alors la méthode "publique", sans avoir besoin de passer l'arbre en paramètre.

Hauteur d'un arbre

Version fonction


def hauteur(arbre):
	if arbre is None:
		return -1
	else:
		return 1 + max(hauteur(arbre.filsgauche), hauteur(arbre.filsdroit))			
			

La valeur -1 renvoyée par le cas de base correspond au fait qu'il ne faut pas compter la branche de la feuille lors de la "remontée" dans l'arbre.

Version POO


class ABR(Arbre):
	....
	
	def hauteur(self):             # méthode "publique"
		return self._hauteur(self) # 'self' représente ici la racine de l'arbre
		
	def _hauteur(self, arbre):     # méthode "privée" ( le caractère underscore '_' signale une méthode "privée", qui ne fait donc pas partie de l'interface )
		if arbre is None:
			return -1
		else:
			return 1 + max(self._hauteur(arbre.filsgauche), self._hauteur(arbre.filsdroit))