Initiation aux algorithmes des arbres binaires
Plan I. Les arbres biniaires I. Définition II. Représentation graphique d un arbre III. Terminologie IV. Représentation en mémoire des arbres binaires V. Parcours d un arbre binaire I. Parcours en hauteur II. II. I. Parcours préfixé II. Parcours infixé III. Parcours postfixé Parcours en largeur Application: tri en tas I. Definition II. Principe du tri en tas
I. Les arbres binaires Definition: Un arbre binaire est un type d arbre ordonné (ordre des sous arbres est significatifs : arbre généalogique par exemple) tel que chaque nœud a au plus deux fils et quand il n y en a qu un, on précise s il s agit du fils droit ou du fils gauche. Représentation graphique:
I. Les arbres binaires Terminologie :
I. Les arbres binaires Terminologie : Arbre binaire équilibré : un arbre binaire est équilibré si pour chaque nœud les hauteurs des sous arbres gauches et droit différent au plus d un. Un arbre est dégénéré (filiforme) si tous ses nœuds ont exactement 0 ou 1 fils (à droite ou à gauche)
I. Les arbres binaires Terminologie : Arbre binaire complet : est un arbre binaire de taille 2 k -1 où k étant le niveau des feuilles. Arbre binaire parfaitement équilibré : un arbre binaire est parfaitement équilibré si pour chaque nœud, le nombre de nœuds des sous arbres gauche et droit différent au plus d un.
I. Les arbres binaires Représentation en mémoire des arbres binaires: Un arbre binaire est un ensemble fini de nœuds : Vide Constitué d une racine et de deux arbres disjoints : Sous arbre gauche et sous arbre droit Dont l un des deux est possiblement vide En python, on peut représenter un arbre vide par une liste vide et un arbre non vide par une liste comprenant trois éléments : [racine,fils_gauche, fils_droit]
I. Les arbres binaires Représentation en mémoire des arbres binaires: Exemple : [10,[6,[12,[13,[],[]], [5,[],[]]],[]], [7,[2,[],[]],[11,[],[]]]]
I. Les arbres binaires Parcours d un arbre binaire Un algorithme de parcours d arbre est un procédé permettant d accéder à chaque nœud de l arbre. On distingue deux catégories de parcours d arbres : les parcours en hauteur où on explore branche par branche et le parcours en largeur où on explore niveau par niveau. Parcours en hauteur (en profondeur) Il y a 6 types de parcours possibles, nous ne considérons dans la suite de ce chapitre que le parcours gauche-droit.
I. Les arbres binaires Préfixé Infixé Postfixé Gauche droit Père-sous arbre gauche-sous arbre droit sous arbre gauche- Père-sous arbre droit sous arbre gauche -sous arbre droit- Père Droit-gauche Père-sous arbre droit-sous arbre gauche sous arbre droit- Père-sous arbre gauche sous arbre droit - sous arbre gauche- Père
I. Les arbres binaires Parcours en hauteur (en profondeur) Parcours préfixé Le premier type de parcours consiste à traiter le nœud lors de première visite, puis explorer le sous arbre gauche, avant d explorer le sous arbre droit. Exemple: + a b - c d * - e
I. Les arbres binaires Parcours en hauteur (en profondeur) Parcours préfixé La fonction de parcours préfixé : def prefixe(arbre): if len(arbre)!=0: print(arbre[0],end=" ") prefixe(arbre[1]) prefixe(arbre[2]) Exemple d appel: arbre=['-',['*',['+',['a',[],[]],['b',[],[]]],['-',['c',[],[]],['d',[],[]]]],['e',[],[]]] prefixe(arbre)
I. Les arbres binaires Parcours en hauteur (en profondeur) Parcours préfixé La fonction de parcours préfixé : def prefixe(arbre): if len(arbre)!=0: print(arbre[0],end=" ") prefixe(arbre[1]) prefixe(arbre[2]) Exemple d appel: arbre=['-',['*',['+',['a',[],[]],['b',[],[]]],['-',['c',[],[]],['d',[],[]]]],['e',[],[]]] prefixe(arbre)
I. Les arbres binaires Parcours en hauteur (en profondeur) Parcours infixé Dans un parcours infixé, le nœud est traité lors de la deuxième visite, après avoir traité le sous-arbre gauche, mais avant de traiter le sous arbre droit. La procédure se schématise comme suit : Traitement du sous arbre gauche Traitement de la racine Traitement du sous arbre droit a + b * c - d - e
I. Les arbres binaires Parcours en hauteur (en profondeur) Parcours infixé La fonction de parcours infixé : def infixe(arbre): if len(arbre)!=0: infixe(arbre[1]) print(arbre[0],end=" ") infixe(arbre[2])
I. Les arbres binaires Parcours en hauteur (en profondeur) Parcours postfixé En parcours postfixé, le nœud est traité après le traitement du sous arbre gauche et du sous arbre droit. La procédure à suivre est donnée ci-dessous : Traitement du sous arbre gauche Traitement du sous arbre droit Traitement de la racine La fonction de parcours postfixé : def postfixe(arbre): if len(arbre)!=0: postfixe(arbre[1]) postfixe(arbre[2]) print(arbre[0],end=" ") a b + c d - * e -
I. Les arbres binaires Parcours en largeur Une autre méthode de parcours des arbres consiste à les visiter niveau par niveau. ainsi sur l arbre binaire de l expression arithmétique, le parcours en largeur est -*e+-abcd Ce parcours nécessite l utilisation d une file d attente contenant initialement la racine. On extrait l élément en tête de la file et on le remplace par ses successeurs à gauche et à droite jusqu à ce que la file soit vide. La fonction suivante effectue un parcours en largeur des nœuds de l arbre :
I. Les arbres binaires Parcours en largeur def largeur(arbre): file=[arbre] while (len(file)!=0): e=file[0] file=file[1:] #e=file.pop(0) print(e[0],end=" ") if len(e)>0: if len(e[1])!=0: file+=[e[1]] if len(e[2])!=0: file+=[e[2]]
II. Application : tri maximier ou tri en tas Définition Un arbre maximier est un arbre binaire homogène dans lequel l étiquette d un nœud interne est toujours supérieure ou égale aux étiquettes de chacun de ses fils. On appelle tas (heap en anglais) un arbre binaire homogène qui est un arbre maximier.
II. Application : tri maximier ou tri en tas Transformer un arbre maximier en un tableau On numérote les nœuds de haut en bas et de gauche à droite depuis 0 jusqu à n-1. Cette numérotation permet de stocker les éléments de l arbre dans un tableau de longueur n. les fils de l élément numéro i sont l élément numéroté 2*i+1 pour le fils gauche et 2*i+2 pour le fils droit. def trans_tas_tab(arbre): t=[] file=[arbre] while len(file)!=0: e=file.pop(0) t+=[e[0]] if len(e)>1: if len(e[1])!=0: file.append(e[1]) if len(e[2])!=0: file.append(e[2]) return(t)
II. Application : tri maximier ou tri en tas Le principe du tri par tas Deux étapes sont nécessaires : la construction du tas, puis le tri. Etape 1 : on construit un tas à partir du tableau à trier en commençant par un tas ne comportant qu un seul nombre : le premier nombre du tableau. Le tas initial est donc le tableau limité à son premier élément. On ajoute le deuxième nombre du tableau tel que le père soit supérieur à l élément ajouté.si ce n est pas le cas, on échange cet élément avec son père puis on ajoute le troisième pour former un tas et ainsi de suite jusqu à former un tas à partir de tous les éléments du tableau. Etape 2 : Pour effectuer le tri, on supprime le premier élément qui est le plus grand du tas en l échangeant avec le dernier élément du tas (le dernier). On a alors le plus grand élément en position n-1 et la partie d indice 0..n-2 du tableau subit une percolation de la racine pour créer de nouveau un tas. on répète l'opération sur le tas restreint jusqu'à l'avoir vidé et remplacé par un tableau trié. L'opération de base de ce tri est le tamisage, ou percolation, d'un élément, supposé le seul «mal placé» dans un arbre qui est presque un tas. Plus précisément, considérons un arbre dont les deux sousarbres sont des tas, tandis que la racine est éventuellement plus petite que ses fils. L'opération de tamisage consiste à échanger la racine avec le plus grand de ses fils, et ainsi de suite récursivement jusqu'à ce qu'elle soit à sa place.
tas Exemple : II. Application : tri maximier ou tri en Illustration de l étape 1 : les figures suivantes montrent la construction d un tas à partir du tableau [4,7,2,5,3]
tas Exemple : II. Application : tri maximier ou tri en La figure suivante montre le tri par tas sur le tableau [4,7,2,5,3]
tas Exemple : II. Application : tri maximier ou tri en La figure suivante montre le tri par tas sur le tableau [4,7,2,5,3]
II. Application : tri maximier ou tri en tas Nous rédigeons dans ce qui suit les fonctions nécessaires pour trier un tableau selon le principe du tri par tas. Construction du tas (insertion, rétablir la structure du tas) On représente le tas comme étant une liste T=[nb,Tas] où nb représente le nombre d éléments du tas et Tas le tableau contenant les éléments du tas. Nous rédigeons la fonction rétablis(t) qui permet de rétablir la structure du tas, la fonction insertion(t,e) où T est le tas et e l élément à insérer dans le tas enfin la fonction permettant de créer un tas à partir d un tableau.
tas def echange(t,i,j): T[i],T[j]=T[j],T[i] II. Application : tri maximier ou tri en def retablis(t): f=t[0]-1 #indice fils p=(f-1)//2 #indice père V=T[1]# V référence le tas T[1], les changements sont enregistrés dans T[1] while f>=1: if V[p]<V[f]:#si le père est inférieur au fils echange(v,p,f)#echange f=p # on remonte l'arbre le fils a l'indice du père p=(f-1)//2 # on calcule le père du nouveau fils def insertion(t,e): n=t[0]# le nombre des éléments du tas if n<=len(t[1]): T[1][n]=e #insertion de l'elt e à la fin du tas T[0]+=1 #mettre à jour le nombre des éléments du tas retablis(t) #rétablir le tas
II. Application : tri maximier ou tri en tas def trans_tab_tas(tab): T=[0,[0]*len(tab)] for e in tab: insertion(t,e) return (T) Suppression de la racine La suppression de la racine dans un tas se déroule en deux étapes : Echanger la racine avec le nœud terminal, puis supprimer ce dernier Effectuer une percolation On rappelle que la percolation consiste à faire redescendre la nouvelle racine tout au long du tas en l échangeant récursivement avec le plus grand de ses fils. Nous développerons la procédure percole(i,t) qui va rétablir la structure de tas à partir de la racine d indice i, T est le tableau contenant le nombre d éléments du tas et le tas. La fonction supprimer_racine(t) qui supprime la racine du tas et la renvoie. T est un tableau contenant le nombre d éléments du tas et le tas.
II. Application : tri maximier ou tri en tas def percole(i,t): j=2*i+1 V=T[1] f=j if j<t[0]:#le fils gauche existe? if j+1<t[0]:#le fils droit existe? if V[j]<V[j+1]:f=j+1 #f reçoit l'indice du fils le plus grand if V[f]>V[i]: #comparaison entre le père et le fils le plus grand sinon le fils gauche echange(v,i,f) #échange entre le père et le fils le plus grand percole(f,t)#percolation à partir de f. def supprime(t): if T[0]>0: V=T[1] racine=v[0] V[0]=V[T[0]-1] T[0]=T[0]-1 percole(0,t) return(racine)
II. Application : tri maximier ou tri en tas Tri en tas en recourant à un tableau auxiliaire on insère les éléments du tableau tab successivement dans un tas, puis on extrait itérativement la racine du tas qui en est toujours le plus grand élément jusqu à ce que le tas soit vide. def tri_tas(tab): T=trans_tab_tas(tab) for i in range(t[0]-1,-1,-1): tab[i]=supprime(t) Tri en tas en place Pour le tri en tas en place, la construction du tas se fera différemment, La technique d insertion dans un tas, consistera à placer le nouvel élément à la racine, ensuite rétablir la structure du tas en redescendant l élément de proche en proche en l échangeant avec l étiquette de son plus grand fils tant que cette étiquette est supérieure. Pour se faire nous réécrirons la procédure percole qui aura comme prototype : def percole(tab,taille,racine,i) où i est la position où sera placée la racine, tab est la tableau qui sera transformé en tas, taille est la taille du tableau.
II. Application : tri maximier ou tri en tas Tri en tas en place def percole1(tab, taille,racine, i): if 2*i+1<taille: j=2*i+1 f=j if j+1<taille: if tab[j]<tab[j+1]: f=j+1 if racine<tab[f]: echange(tab,i,f) percole1(tab,taille,racine,f) else: tab[i]=racine else: tab[i]=racine Nous réécrivons la procédure qui permet de transformer un tableau en tas en utilisons la procédure précédente :
II. Application : tri maximier ou tri en tas def trans_tab_tas_place(tab): for i in range((len(tab)-1)//2,-1,-1): percole1(tab,len(tab),tab[i],i) la procédure du tri par tas en place: def tri_tas1(tab): n=len(tab) trans_tab_tas_place(tab) for i in range(n-1,0,-1): tab[i],tab[0]=tab[0],tab[i] percole1(tab,i,tab[0],0)
TD 7