ajout cours sur les arbres binaires avec les images
443
arbres/ARBRES_BINAIRES.md
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## Les arbres binaires
|
||||||
|
|
||||||
|
> Parmi la forêt d'arbres possibles, on s'intéressera essentiellement aux arbres dit binaires.
|
||||||
|
> Ceux ci sont principalement utilisés pour les bases de données (indexation), les moteurs de recherche, et les compilateurs.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Définition:
|
||||||
|
Un arbre **binaire** est un arbre de degré 2 (dont lesnoeuds sont de degré 2 au plus).
|
||||||
|
|
||||||
|
Les enfants d'un noeud sont lus de **gauche à droite** et sont appelés **fils gauche** et **fils droit**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Exercice 1:
|
||||||
|
Parmi les arbres du cours précédent, lesquels sont binaires ?
|
||||||
|
|
||||||
|
Réponse
|
||||||
|
|
||||||
|
### Remarque:
|
||||||
|
Les arbres binaires forment une structure de données qui peut se définir de façon récursive.
|
||||||
|
|
||||||
|
Un arbre binaire est:
|
||||||
|
- soit vide
|
||||||
|
- soit composé d'une racine portant une étiquette (clé) et d'une paire d'arbres binaires, appelés sous-arbre gauche et sous-arbre droit.
|
||||||
|
|
||||||
|
Les arbres binaires sont utilisés dans de très nombreuses activités informatiques, nous allons étudier et implanter cette structure de données.
|
||||||
|
|
||||||
|
## Comment représenter un arbre binaire... à la main...
|
||||||
|
|
||||||
|
L'idée est de représenter l'arbre avec un tableau.
|
||||||
|
|
||||||
|
[r, a, b] La racine r suivie de ses fils gauche et droit.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Puis on rajoute dans l'ordre les fils gauche et droit de a, puis ceux de b.
|
||||||
|
|
||||||
|
[r, a, b, c, d, e, f]
|
||||||
|
|
||||||
|
### Remarque:
|
||||||
|
Chaque noeud se repère par son indice *n* dans la liste, son fils gauche se trouvant alors à l'indice *2n + 1* et son fils droit à l'indice *2n + 2*.
|
||||||
|
|
||||||
|
Exemple: **b** est d'indice 2, son fils gauche se trouve alors à l'indice 5 et son fils droit à l'indice 6.
|
||||||
|
|
||||||
|
Si un noeud n'a pas de fils, on le précise en mettant *None* à sa place. Notre arbre est alors représenté par le tableau:
|
||||||
|
|
||||||
|
[r, a, b, c, d, e, f, None, None, None, None, None, None, None, None]
|
||||||
|
|
||||||
|
**Quelle taille doit avoir le tableau ?**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Cet arbre est complet (tous les noeuds internes ont deux fils).
|
||||||
|
|
||||||
|
Il possède 3 niveaux, sa hauteur ou profondeur est donc de 2. La taille du tableau sera de: $$2^0 + 2^1 + 2^2 + 2^3 = 2^4 -1 = 15$$
|
||||||
|
|
||||||
|
*Il faut compter le nombre de noeuds, y compris les noeuds "fantômes" des feuilles.*
|
||||||
|
|
||||||
|
### Exercice :
|
||||||
|
1. Quelle est la taille du tableau qui permet de représenter cet arbre ?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. Ecrire le tableau représentant cet arbre et le stocker dans une variable **t**
|
||||||
|
|
||||||
|
3. Quelle propriété ont les indices des fils gauches et droits ?
|
||||||
|
|
||||||
|
4. Voici un tableau représentant un arbre binaire:
|
||||||
|
|
||||||
|
['*','-',5,2,6,None,None,None,None,None,None,None,None,None,None]
|
||||||
|
|
||||||
|
|
||||||
|
Le dessiner. Que peut-il représenter ?
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
> Voici un code python Python qui crée la liste représentant l'arbre de l'exercice 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
def creation_arbre(r,profondeur):
|
||||||
|
"""r : la racine (str ou int). la profondeur de l’arbre (int)"""
|
||||||
|
Arbre = [r]+[None for i in range(2**(profondeur+1)-2)]
|
||||||
|
return Arbre
|
||||||
|
|
||||||
|
def insertion_noeud(arbre,n,fg,fd):
|
||||||
|
"""Insére les noeuds et leurs enfants dans l’arbre"""
|
||||||
|
indice = arbre.index(n)
|
||||||
|
arbre[2*indice+1] = fg
|
||||||
|
arbre[2*indice+2] = fd
|
||||||
|
|
||||||
|
# création de l’arbre
|
||||||
|
arbre = creation_arbre("r",5)
|
||||||
|
# ajout des noeuds par niveau de gauche à droite
|
||||||
|
insertion_noeud(arbre,"r","a","b")
|
||||||
|
insertion_noeud(arbre,"a","c","d")
|
||||||
|
insertion_noeud(arbre,"b","e","f")
|
||||||
|
insertion_noeud(arbre,"c",None,"h")
|
||||||
|
insertion_noeud(arbre,"d","i","j")
|
||||||
|
insertion_noeud(arbre,"e","k",None)
|
||||||
|
insertion_noeud(arbre,"f",None,None)
|
||||||
|
insertion_noeud(arbre,"h",None,None)
|
||||||
|
insertion_noeud(arbre,"i",None,None)
|
||||||
|
insertion_noeud(arbre,"j","m",None)
|
||||||
|
insertion_noeud(arbre,"k",None,None)
|
||||||
|
insertion_noeud(arbre,"m",None,None)
|
||||||
|
|
||||||
|
#pour vérifier
|
||||||
|
print(len(arbre))
|
||||||
|
print(arbre)
|
||||||
|
```
|
||||||
|
|
||||||
|
63
|
||||||
|
['r', 'a', 'b', 'c', 'd', 'e', 'f', None, 'h', 'i', 'j', 'k', None, None, None, None, None, None, None, None, None, 'm', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]
|
||||||
|
|
||||||
|
|
||||||
|
### Exercice 3:
|
||||||
|
1. Ecrire une fonction qui retourne le parent d'un noeud s'il est dans l'arbre et None sinon.
|
||||||
|
2. Ecrire une fonction qui retourne **True** si l'arbre est vide
|
||||||
|
3. Ecrire une fonction qui retourne les enfants d'un noeud.
|
||||||
|
4. Un fonction qui renvoie le fils gauche d'un noeud s'il existe et None sinon
|
||||||
|
5. Même question pour le fils droit
|
||||||
|
6. Ecrire une fonction qui renvoie **True** si le noeud est la racine de l'arbre
|
||||||
|
7. Ecrire une fonction qui renvoie **True** si le noeud est une feuille
|
||||||
|
8. Ecrire une fonction qui renvoie **true** si le noeud comporte un frère gauche ou droit.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---------
|
||||||
|
|
||||||
|
## Une seconde façon de faire...
|
||||||
|
|
||||||
|
Comme vous l'avez sans doute constaté, il est assez fastidieux de représenter un arbre avec un unique tableau surtout pour un arbre très profond.
|
||||||
|
|
||||||
|
L'idée est de représenter l'arbre ave cun tableau contenant des tableaux
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Cet arbre se représente par le tableau:
|
||||||
|
|
||||||
|
['r', ['a', [ ], [ ]], ['b', [ ], [ ]]]
|
||||||
|
|
||||||
|
### Exercice :
|
||||||
|
Ecrire le tableau représentant l'arbre ci-dessous et le stocker dans une variable **t**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Le code Python ci-dessous, construit l'arbre de l'exercice 1 de manière récursive.**
|
||||||
|
|
||||||
|
* Les noeuds sont représentés par un dictionnaire.
|
||||||
|
* L'arbre se construit depuis la racine en construisant les sous-arbres des fils gauche et droit.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
def noeud(nom, fg =None, fd =None):
|
||||||
|
return{'racine': nom, 'fg' : fg, 'fd': fd}
|
||||||
|
|
||||||
|
# création des noeuds
|
||||||
|
k = noeud('k')
|
||||||
|
f = noeud('f')
|
||||||
|
e = noeud('e', k,None)
|
||||||
|
b = noeud('b', e, f)
|
||||||
|
m = noeud('m')
|
||||||
|
j = noeud('j', m,None)
|
||||||
|
i = noeud('i')
|
||||||
|
d = noeud('d', i, j)
|
||||||
|
h = noeud('h')
|
||||||
|
c = noeud('c',None, h)
|
||||||
|
a = noeud('a', c, d)
|
||||||
|
racine = noeud('r', a, b)
|
||||||
|
# création de l’arbre
|
||||||
|
def construit(arbre):
|
||||||
|
if arbre is None:
|
||||||
|
return [ ]
|
||||||
|
else:
|
||||||
|
return [arbre['racine'],construit(arbre['fg']),construit(arbre['fd'])]
|
||||||
|
|
||||||
|
arbre1 = construit(racine)
|
||||||
|
print(arbre1)
|
||||||
|
```
|
||||||
|
|
||||||
|
['r', ['a', ['c', [], ['h', [], []]], ['d', ['i', [], []], ['j', ['m', [], []], []]]], ['b', ['e', ['k', [], []], []], ['f', [], []]]]
|
||||||
|
|
||||||
|
|
||||||
|
### Exercice :
|
||||||
|
Ecrire toutes les fonctions de l'exercice 3 dans le cas de cette implémentation de l'arbre.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Nous allons maintenant nous intéresser à la hauteur de l'arbre, ainsi qu'aux différentes façons de le parcourir:
|
||||||
|
* Le parcours en largeur
|
||||||
|
* Le parcours en profondeur (parcours fixe, parcours infixe et parcours suffixe).
|
||||||
|
|
||||||
|
**Calcul de la hauteur de l'arbre:**
|
||||||
|
L'idée ets la suivante:
|
||||||
|
* Si l'arbre ets vide, la hauteur vaut -1
|
||||||
|
* Sinon la hauteur vaut 1 auquel il faut ajouter le maximum entre les hauteurs des sous arbres gauche et droit.
|
||||||
|
* Ces sous-arbres sont eux même des arbres dont il faut calculer la hauteur.
|
||||||
|
|
||||||
|
Une méthode **récursive** semble donc tout à fait adaptée à la situation.
|
||||||
|
|
||||||
|
Voici l'algorithme:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Exercice :
|
||||||
|
1. Ecrire cette fonction pour l'arbre précédent et vérifier que sa profondeur est de 4.
|
||||||
|
2. Reprenez l'arbre d ela question 4 de l'exercice 2. Stockez le dans une variable avec cette nouvelle méthode puis calculez sa profondeur.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Les parcours
|
||||||
|
|
||||||
|
**Le parcours en largeur:**
|
||||||
|
Le parcours en largeur d'un arbre consiste à partir de la racine. On visite ensuite son fils gauche puis sont fils droit, puis le fils gauche du fils gauche etc... Comme le montre le schéma ci-dessous:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
L'idée est la suivante:
|
||||||
|
|
||||||
|
On utilise une File.
|
||||||
|
* On met l'arbre dans la file.
|
||||||
|
* Puis tant que la file n'est pas vide:
|
||||||
|
* On défile la file
|
||||||
|
* On récupère sa racine
|
||||||
|
* On enfile son fils gauche s'il existe
|
||||||
|
* on enfile son fils droit s'il existe
|
||||||
|
|
||||||
|
|
||||||
|
Voici l'algorithme:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Exercice 7
|
||||||
|
|
||||||
|
1. Rappeler les fonction permettant de définir une structure de file en python.
|
||||||
|
2. Implémenter alors cette fonction et l'essayer sur l'arbre précédent.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**Les parcours en profondeur**
|
||||||
|
On se balade autour de l'arbre en suivant les pointillés
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Voici un algorithme permettant de réaliser ce parcours:
|
||||||
|

|
||||||
|
|
||||||
|
### Exercice
|
||||||
|
1. Proposer une fonction permettant de réaliser le parcours en profondeur d'un arbre
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Dans le schéma ci-dessus, on a rajouté des "noeuds fantômes" pour montrer que l'on peut considérer que chaque noeud est visité trois fois:
|
||||||
|
* une fois par la gauche
|
||||||
|
* une fois par en dessous
|
||||||
|
* une fois par la droite
|
||||||
|
|
||||||
|
### Definition:
|
||||||
|
Dans un parcours **prefixe**, on liste le noeud la première fois qu'on le rencontre.
|
||||||
|
|
||||||
|
|
||||||
|
### Exercice
|
||||||
|
1. Ecrire les sommets dans l'ordre d'un parcours préfixe
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Definition:
|
||||||
|
Dans un parcours **infixe**, on liste le noeud la seconde fois qu'on le rencontre.
|
||||||
|
|
||||||
|
Ce qui correspond à:
|
||||||
|
* On liste chaque noeud ayant un fils gauche la seconde fois qu'on le voit
|
||||||
|
* on liste chaque noeud sans fils gauche la première fois qu'on le voit
|
||||||
|
|
||||||
|
### Exercice
|
||||||
|
1. Ecrire les sommets dans l'ordre d'un parcours infixe
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Definition:
|
||||||
|
Dans un parcours **suffixe**, on note le noeud la dernière fois qu'on le rencontre.
|
||||||
|
|
||||||
|
Exercice 11
|
||||||
|
1. Ecrire les sommets dans l'ordre d'un parcours suffixe
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Exercice
|
||||||
|
1. Voici trois algorithmes récursifs, dire pour chacun d'entre eux à quel parcours il correspond.
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
2. Implémenter ces trois fonctions en python et confirmer les réponses des questions précédentes
|
||||||
|
|
||||||
|
-------
|
||||||
|
|
||||||
|
## Une troisième façon de faire.... en programmation objet
|
||||||
|
|
||||||
|
Nous allons créer une classe **Noeud** dont les attributs d'instances seront:
|
||||||
|
* le nom (ou valeur) de la racine
|
||||||
|
* son fils gauche (None par défaut) ou de type Noeud
|
||||||
|
* son fils droit (None par défaut) ou de type Noeud
|
||||||
|
|
||||||
|
### Exercice
|
||||||
|
1. Implémentez cette classe Noeud
|
||||||
|
2. Stocker l'arbre comme étant l'objet de type "Noeud" correspondant à la racine.
|
||||||
|
3. Ecrire une méthode estFeuille qui renvoie True si le noeud est une feuille et False sinon
|
||||||
|
4. Définir une fonction hauteur prenant comme argument un arbre permettant de renvoyer la hauteur de cet arbre
|
||||||
|
5. Ecrire une méthode hauteur dans la classe Noeud permettant de faire la même chose
|
||||||
|
6. Proposer des fonctions pour le parcours infixe, prefixe et suffixe pour cette façon de coder un arbre.
|
||||||
|
7. Transformer alors vos fonctions en méthodes de la classe Noeud
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
-------
|
||||||
|
|
||||||
|
### Sources
|
||||||
|
|
||||||
|
- [Cours de Stephan Van Zulen](https://www.nsi-ljm.fr/NSI-TLE/co/section_chapitre3.html)
|
||||||
|
- [Cours de Jeremy Camponovo](https://github.com/jcamponovo)
|
||||||
BIN
arbres/assets/fig15.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
arbres/assets/fig16.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
arbres/assets/fig17.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
arbres/assets/fig18.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
arbres/assets/fig19.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
arbres/assets/fig20.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
arbres/assets/fig21.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
arbres/assets/fig22.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
arbres/assets/fig23.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
arbres/assets/fig24.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
arbres/assets/fig25.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
arbres/assets/fig26.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
arbres/assets/fig27.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
arbres/assets/fig28.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
arbres/assets/fig29.png
Normal file
|
After Width: | Height: | Size: 38 KiB |