LES ARBRES
1
1
3
2
4
5
6
7
8
9
CPGE - AGADIR MP-PSI-TSI
M.Gueroihi
ARBRES �TERMINOLOGIE DE BASE
2
.
noeuds
arcs
ARBRES �TERMINOLOGIE DE BASE
3
niveau 0
niveau 1
niveau 2
niveau 3
nœuds internes
feuilles
parent
et
fils
racine
ARBRES �EXEMPLES
4
1
3
2
4
5
6
7
8
9
ARBRES �EXEMPLES
Arbre représentant une expression arithmétique
5
((a-b)*(c/d))+e
+
e
*
a
b
-
d
c
/
ARBRES �EXEMPLES
Arbre généalogique :
6
L’arbre généalogique suivant représente quelques descendants mâles de Noé
ARBRES �DÉFINITION RÉCURSIVE
7
n
n1
n2
nk
A1
A2
Ak
ARBRES �DÉFINITION RÉCURSIVE
8
n1
n2
nk
A1
A2
Ak
n
A
ARBRES �chemins entre nœuds
9
n1, n2 ,n3 et n4 : chemin entre le nœud n1 et le nœud n4
La longueur du chemin est le nombre de nœuds - 1 (=nombre d’arcs).
La longueur du chemin entre le nœud n1 et le nœud n4 =
n1
n2
n3
n4
3
ARBRES�Ascendants et descendants�
10
Frères.
nœud interne
arbre vide.
Feuille.
a
u
w
v
n3
x
b
y
ARBRES�Ascendants et descendants�
11
1
3
2
4
5
6
7
8
9
3
7, 8 et 9
7 et 8
1
1 et 3
4, 5, 6, 8 et 9
frères
ARBRES� Hauteur et profondeur �
12
1
3
2
4
5
6
7
8
9
2
3
ARBRES�Hauteur et profondeur�
13
1
3
2
4
5
6
7
8
9
1
2
ARBRES�Ordre des nœuds �
14
Les deux arbres ci-dessous sont différents
ARBRES�Ordre des nœuds �
15
1
3
2
4
5
6
7
8
9
ARBRES BINAIRES �(AB)
16
Définition
ARBRES BINAIRES �(AB)
17
Arbre binaire Complet
Arbre binaire pas complet
Définition
ARBRES BINAIRES �Parcours d’un arbre binaire
Parcours en profondeur d’bord
18
Le parcours préfixe est décrit récursivement :
Un affichage préfixe donnerait :
12
1
67
91
45
32
50
61
7
82
40
-
-
-
-
-
-
-
-
-
-
ARBRES BINAIRES �Parcours prefixe
19
affichagePrefixe(a) :
Si non vide(a) :
afficher(val(a))
affichagePrefixe(filsGauche(a))
affichagePrefixe(filsDroit(a))
def prefixe(a):
if not vide(a):
print (val(a), end=' ')
prefixe(filsGauche(a))
prefixe(filsDroit(a))
ARBRES BINAIRES �Parcours d’un arbre binaire
Parcours en profondeur d’bord
20
Le parcours infixe est décrit récursivement :
Un affichage infixe donnerait :
32
91
67
1
50
45
12
40
61
82
7
-
-
-
-
-
-
-
-
-
-
ARBRES BINAIRES �Parcours infixe
21
affichageInfixe(a) :
Si non vide(a) :
affichageInfixe(filsGauche(a))
afficher(val(a))
affichageInfixe(filsDroit(a))
def infixe(a):
if not vide(a):
infixe (filsGauche(a))
print (val(a), end=' ')
infixe (filsDroit(a))
ARBRES BINAIRES �Parcours d’un arbre binaire
Parcours en profondeur d’bord
22
Le parcours postfixe est décrit récursivement :
Un affichage infixe donnerait :
32
91
67
45
1
50
40
82
61
12
7
-
-
-
-
-
-
-
-
-
-
ARBRES BINAIRES �Parcours postfixe
23
affichagepostfixe(a) :
Si non vide(a) :
affichagePostfixe(filsGauche(a))
affichagePostfixe(filsDroit(a))
afficher(val(a))
def postFixe(a):
if not vide(a):
postFixe(filsGauche(a))
postFixe(filsDroit(a))
print (val(a), end=' ')
ARBRES BINAIRES �Parcours d’un arbre binaire
Parcours en largeur
24
Un affichage en largeur donnerait :
12
1
67
7
61
91
82
45
32
40
50
-
-
-
-
-
-
-
-
-
-
ARBRES BINAIRES �Parcours en largeur
25
affichageLargeur(a):
F : File #File FIFO
Enfiler(a,F) #enfiler la racine a dans la file F
tantque non vide(F):
n=Défiler(F)
Afficher(val(n))
si non vide(filsGauche(n)):
Enfiler(filsGauche(n),F) #enfiler le fils gauche de n dans F
si non vide(filsDroit(n)):
Enfiler(filsDroit(n),F) #enfiler le fils droit de n dans F
26
affichageLargeur(a):
F : File #File FIFO
Enfiler(a,F) #enfiler la racine a dans la file F
tantque non vide(F):
n=Défiler(F)
Afficher(val(n))
si non vide(filsGauche(n)): Enfiler(filsGauche(n),F)
si non vide(filsDroit(n)): Enfiler(filsDroit(n),F)
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
F
ARBRES BINAIRES �Parcours en largeur
27
def parcoursLargeur(a):
F=[] #File FIFO
F.append(a) #enfiler la racine a dans la file F
while not vide(F):
n=F.pop(0) #défiler F (récupérer la tète)
print(val(n),end=' ')
if not vide(filsGauche(n)):
F.append(filsGauche(n)) #enfiler le fils gauche de n dans F
if not vide(filsDroit(n)):
F.append(filsDroit(n)) #enfiler le fils droit de n dans F
print()
ARBRES BINAIRES�Mise en œuvre des arbres binaires.
28
- un arbre vide par une liste vide [ ] .
- un arbre non vide par une liste comprenant 3 éléments :
[clé , filsGauche , filsDroit].
Exemple
A = [ ] #A est un arbre vide
A = [12, [ ], [ ] ] #A est un arbre qui contient un seul nœud (12)
12
A=[12, [ 1, [ ], [ ] ] , [ 7, [ ], [ ] ] ]
12
1
7
ARBRES�Mise en œuvre des arbres binaires.
29
12
1
7
91
67
A=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
12
1
7
91
67
82
A=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ 82,[ ], [ ] ] ] ]
ARBRES BINAIRES �FONCTIONS DE BASE�
30
#Fonction qui détermine si un arbre a est vide ou non.
def vide(a):
return a==[]
#Fonction qui retourne la valeur de la racine d’un arbre (étiquette).
def val(a):
if not vide(a):
return a[0]
else:
return None
>>> a=[12, [ 1, [ ], [ ] ] , [ 7, [ ], [ ] ] ]
>>>val(a)
12
12
1
7
ARBRES BINAIRES �FONCTIONS DE BASE�
31
#Fonction qui retourne le fils gauche de l’arbre a.
def filsGauche(a):
if not vide(a):
return a[1]
else:
return []
12
1
7
91
67
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>filsGauche(a)
[ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ]
ARBRES BINAIRES �FONCTIONS DE BASE�
32
#Fonction qui retourne le fils droit de l’arbre a.
def filsDroit(a):
if not vide(a):
return a[2]
else:
return []
12
1
7
91
67
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>filsDroit(a)
[ 7, [ ], [ ] ]
ARBRES BINAIRES �FONCTIONS DE BASE�
Fonctions de parcours.
33
#Affichage préfixe d’un arbre binaire (fonction récursive)
def prefixe(a):
if not vide(a):
print (val(a), end=' ')
prefixe(filsGauche(a))
prefixe(filsDroit(a))
12
1
7
91
67
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>prefixe(a)
12 1 91 67 7
ARBRES BINAIRES �FONCTIONS DE BASE�
Fonctions de parcours.
34
#Affichage préfixe en utilisant un traitement itératif (en utilisant une Pile)
def prefixeIter(a):
P=[] # Pile vide
P.append(a) # empiler la racine dans la pile P
while not vide(P):
n=P.pop() # dépiler p et récupérer la tète (nœud n)
print(val(n),end=' ')
if not vide(filsDroit(n)):
P.append(filsDroit(n)) # empiler le fils droit de n dans P
if not vide(filsGauche(n)):
P.append(filsGauche(n)) # empiler le fils gauche de n dans P
print()
12
1
7
91
67
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
>>> prefixeIter(a)
12 1 91 67 7
35
def prefixeIter(a):
P=[] # Pile vide
P.append(a) # empiler la racine dans la pile P
while not vide(P):
n=P.pop() # dépiler p et récupérer la tète (nœud n)
print(val(n),end=' ')
if not vide(filsDroit(n)) : P.append(filsDroit(n))
if not vide(filsGauche(n)) : P.append(filsGauche(n))
print()
1
2
3
4
5
6
7
8
9
1
2
3
5
4
8
7
6
9
1
2
4
5
7
8
3
6
9
P
ARBRES BINAIRES �FONCTIONS DE BASE�
Fonctions de parcours.
36
#Affichage infixe d’un arbre binaire (fonction récursive)
def infixe(a):
if not vide(a):
prefixe(filsGauche(a))
print (val(a), end=' ')
prefixe(filsDroit(a))
12
1
7
91
67
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>infixe(a)
91 1 67 12 7
ARBRES BINAIRES �FONCTIONS DE BASE�
Fonctions de parcours.
37
#Affichage postfixe d’un arbre binaire (fonction récursive)
def postfixe(a):
if not Vide(a):
postfixe(filsGauche(a))
postfixe(filsDroit(a))
print (val(a), end=' ')
12
1
7
91
67
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>postfixe(a)
91 67 1 7 12
ARBRES BINAIRES �FONCTIONS DE BASE�
Fonctions de parcours.
38
#Affichage en largeur d’un arbre binaire - traitement itératif (en utilisant une File)
def parcoursLargeur(a):
F=[] #File FIFO
F.append(a) #enfiler la racine a dans la file F
while not vide(F):
n=F[0] ; F=F[1:] #défiler F (récupérer la tète)
print(val(n),end=' ')
if not vide(filsGauche(n):
F.append(filsGauche(n)) #enfiler le fils gauche de n dans F
if not vide(filsDroit(n):
F.append(filsDroit(n)) #enfiler le fils droit de n dans F
print()
12
1
7
91
67
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ ],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>parcoursLargeur(a)
12 1 7 91 67
39
def parcoursLargeur(a):
F=[] #File FIFO
F.append(a) #enfiler la racine a dans la file F
while not vide(F):
n=F[0] ; F=F[1:] #défiler F (récupérer la tète)
print(val(n),end=' ')
if not vide(filsGauche(n): F.append(filsGauche(n))
if not vide(filsDroit(n) : F.append(filsDroit(n))
print()
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
F
ARBRES BINAIRES �fonctions de base
40
# déterminer si un nœud est une feuille
def estFeuille(a):
if vide(a) : return False
return a[1]==[] and a[2]==[]
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ 80,[ ],[ ]],[ ]] ] , [ 7, [ ], [ ] ] ]
>>> estFeuille(a)
False
12
1
7
91
67
80
>>> a=filsGauche(a)
>>> estFeuille(a)
False
>>> a=filsGauche(a)
>>> val(a) , estFeuille(a)
91 , True
ARBRES BINAIRES �fonctions de base
41
#hauteur d’un arbre
La hauteur d’un arbre est la longueur du plus long chemin que l’on peut mener entre la racine et une feuille de l’arbre.
La hauteur d'un arbre est très importante. En effet, c'est un repère de performance. La plupart des algorithmes que nous verrons dans la suite ont une complexité qui dépend de la hauteur de l'arbre. Ainsi plus l'arbre aura une hauteur élevée, plus l'algorithme mettra de temps à s'exécuter.
1 + la hauteur maximale entre ses fils.
def hauteur(a):
if vide(a) or estFeuille(a):
return 0
else:
return 1 + max(hauteur(filsGauche(a)),hauteur(filsDroit(a)))
ARBRES BINAIRES �fonctions de base
42
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ 80,[ ],[ ]],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>hauteur(a)
3
12
1
7
91
67
80
#hauteur d’un arbre
ARBRES BINAIRES �fonctions de base
43
#calculer le nombre de nœuds d’un arbre
def nombreNoeuds(a):
if vide(a):
return 0
else:
return 1+nombreNoeuds(filsGauche(a))+ nombreNoeuds(filsDroit(a))
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ 80,[ ],[ ]],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>nombreNoeuds(a)
6
12
1
7
91
67
80
ARBRES BINAIRES �fonctions de base
44
#calculer le nombre de feuilles dans un arbre
def nombreFeuilles(a):
if vide(a):
return 0
elif estFeuille(a):
return 1
else:
return nombreFeuilles(filsGauche(a))+ nombreFeuilles(filsDroit(a))
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ 80,[ ],[ ]],[ ]] ] , [ 7, [ ], [ ] ] ]
>>>nombreFeuilles(a)
3
12
1
7
91
67
80
ARBRES BINAIRES �fonctions de base
45
# déterminer si un nœud est un nœud interne
def estNoeudInterne(a):
if vide(a) : return False
return not estFeuille(a)
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ 80,[ ],[ ]],[ ]] ] , [ 7, [ ], [ ] ] ]
>>> estNoeudInterne(a)
True
12
1
7
91
67
80
>>> a=filsGauche(a)
>>> estNoeudInterne(a)
True
>>> a=filsGauche(a)
>>> val(a) , estNoeudInterne(a)
91 , False
ARBRES BINAIRES �fonctions de base
46
#calculer le nombre de nœuds internes dans un arbre
def nombreNoeudInternes(a):
if vide(a):
return 0
elif estFeuille(a):
return 0
else:
return 1+ nombreFeuilles(filsGauche(a))+ nombreFeuilles(filsDroit(a))
>>>a=[12, [ 1, [91,[ ],[ ] ], [67,[ 80,[ ],[ ]],[ ]] ] , [ 7, [ ], [ ] ] ]
>>> nombreNoeudInternes(a))
3
12
1
7
91
67
80
ARBRES BINAIRES �fonctions de base
47
#fonction de recherche d’une valeur x dans un arbre binaire quelconque
def existe(a, x):
if vide(a):
return False
else:
if val(a) ==x:
return True
else:
return existe(filsGauche(a),x) or existe(filsDroit(a), x)
ARBRES BINAIRES DE RECHERCHE�(ABR)
48
Définition
ARBRES BINAIRES DE RECHERCHE�(ABR)
49
Exemple
9
12
5
3
4
19
18
20
Si on fait un parcours infixe de l’arbre, on obtient la liste des valeurs des nœuds triée en ordre croissant :
Dans l’exemple : 3 – 4 – 5 – 9 – 12 – 18 – 19 – 20
ARBRES BINAIRES DE RECHERCHE�(ABR)
50
Exemple
9
12
5
3
4
19
18
20
9
12
4
3
5
19
18
20
ARBRES BINAIRES DE RECHERCHE�(ABR)
51
Intérêt
51
Insertion ………… O(log N)
Suppression ………… O(log N)
Trouver le Min ………… O(log N)
Trouver le Max ………… O(log N)
Tri ABR = ………… O(N log N)
Idée: ABR tri = (Construction de l’ABR : N insertions)
+ (Parcourir ABR)
Pourquoi?