🍇 Tas binaire

Sommaire

On considère des arbres binaires particuliers, avec des données toutes de même type, un type qui possède une relation d'ordre. Par exemple : des entiers, on peut les comparer.

Définition

Un tas binaire (ou tas-max) est un arbre binaire presque complet à gauche, tel que s'il est non vide :

Un tas-min est une structure équivalente mais avec la racine portant une valeur inférieure ou égale à celles de sa filiation.

La structure de tas binaire est utilisée dans un algorithme de tri efficace (le tri par tas), dans la gestion des files de priorité, etc.

Exemple

expression Tas-max 1 10 2 8 1->2 3 4 1->3 4 5 2->4 5 6 2->5 6 2 3->6 7 1 3->7 8 4 4->8 9 2 4->9 10 3 5->10 11 5->11

expression Tas-min 1 1 2 2 1->2 3 5 1->3 4 2 2->4 5 3 2->5 6 6 3->6 7 10 3->7 8 4 4->8 9 8 4->9 10 4 5->10 11 5->11

Construction d'une classe Tas

Implémentation avec un tableau

Tout arbre binaire presque complet gauche peut être implémenté en stockant les données dans un tableau.

Par exemple, le tas-max précédent peut être stocké dans le tableau :

Indice 00 11 22 33 44 55 66 77 88 99 1010
Élément 1010 88 44 55 66 22 11 44 22 33

Ce tableau correspond à une lecture lors d'un parcours en largeur.

Exercice 1 : Donner le tableau correspondant au tas-min donné en exemple.

Exercice 2 : Compléter

Cette implémentation permet de se déplacer facilement à partir d'un nœud.

La classe Tas

On propose de stocker :

La taille est souvent stockée à l'indice 00. Nous ne le ferons pas, en effet, la taille est un entier, mais les éléments du tas ne le sont peut-être pas !

On propose les méthodes :

class Tas:
    """ Implémentation de la structure de données tas-max
    """
    def __init__(self):
        self.__tas = [None]
        self.__taille = 0

    def _échange(self, i, j):
        tmp = self.__tas[i]
        self.__tas[i] = self.__tas[j]:
        self.__tas[j] = tmp


    def __str__(self):
        return str(self.__tas)

    def est_vide(self):
        return (self.__tas == [None]) and (self.__taille == 0)

    def ajoute(self, x):
        self.__taille += 1
        self.__tas.append(x)
        i = self.__taille
        parent_i = i // 2
        while (i > 1) and self.__tas[parent_i] < self.__tas[i]:
            self._échange(i, parent_i)
            i = parent_i
            parent_i = i // 2

    def extrait(self, x):

        def est_valide(i):
            """Indique si la règle est respectée pour le nœud stocké en i
            """
            if 2*i > self.__taille:
                # On est sur une feuille
                return True
            if 2*i == self.taille:
                # Le nœud n'a qu'un enfant à gauche
                return self.__tas[i] >= self.__tas[2*i]
            # Le nœud possède deux enfant, à gauche, à droite
            return self.__tas[i] >= self.__tas[2*i] and \
                   self.__tas[i] >= self.__tas[2*i + 1]
            
        if self.est_vide():
            raise ValueError("Tas vide")
        élément = self.__tas[1] # l'élément à renvoyer
        # On place à la racine le dernier élément
        self.__tas[1] = self.__tas.pop()
        self.__taille -= 1
        # On va le remettre à une place qui respecte la règle
        i = 1
        while not est_valide(i):
            if (2*i == self.__taille) or (self.__tas[2*i] > self.__tas[2 * i + 1]):
                j = 2 * i     # vers l'enfant gauche
            else:
                j = 2 * i + 1 # vers l'enfant droite
            self._échange(i, j)
            i = j
        return élément