ajout de tous les cours et TP préparés cet été
This commit is contained in:
419
Arbres/ABR.md
Normal file
419
Arbres/ABR.md
Normal file
@@ -0,0 +1,419 @@
|
||||
# Arbres Binaires de Recherche (ABR)
|
||||
|
||||
> Les arbres binaires de recherche sont une structure de données fondamentale en informatique. Ils permettent d'effectuer des opérations de recherche, d'insertion et de suppression de manière efficace, avec une complexité moyenne en O(log n).
|
||||
|
||||
---
|
||||
|
||||
## 1. Définition
|
||||
|
||||
Un **Arbre Binaire de Recherche** (ABR) est un arbre binaire qui vérifie la propriété suivante pour **tout nœud N** :
|
||||
|
||||
- Toutes les valeurs du **sous-arbre gauche** sont **strictement inférieures** à la valeur de N
|
||||
- Toutes les valeurs du **sous-arbre droit** sont **strictement supérieures** à la valeur de N
|
||||
|
||||
### Exemple d'ABR
|
||||
|
||||
```
|
||||
8
|
||||
/ \
|
||||
3 10
|
||||
/ \ \
|
||||
1 6 14
|
||||
/ \ /
|
||||
4 7 13
|
||||
```
|
||||
|
||||
Cet arbre est un ABR car :
|
||||
- Pour le nœud 8 : tous les nœuds à gauche (3, 1, 6, 4, 7) sont < 8, tous ceux à droite (10, 14, 13) sont > 8
|
||||
- Pour le nœud 3 : 1 < 3 et 6 > 3
|
||||
- Et ainsi de suite pour tous les nœuds...
|
||||
|
||||
### Contre-exemple
|
||||
|
||||
```
|
||||
8
|
||||
/ \
|
||||
3 10
|
||||
/ \
|
||||
1 9 ← 9 > 8, il devrait être à droite de 8 !
|
||||
```
|
||||
|
||||
Cet arbre n'est **pas** un ABR car 9 est dans le sous-arbre gauche de 8 alors que 9 > 8.
|
||||
|
||||
---
|
||||
|
||||
## 2. Intérêt des ABR
|
||||
|
||||
### Pourquoi utiliser un ABR plutôt qu'une liste ?
|
||||
|
||||
| Opération | Liste non triée | Liste triée | ABR (équilibré) |
|
||||
|-----------|-----------------|-------------|-----------------|
|
||||
| Recherche | O(n) | O(log n) | **O(log n)** |
|
||||
| Insertion | O(1) | O(n) | **O(log n)** |
|
||||
| Suppression | O(n) | O(n) | **O(log n)** |
|
||||
|
||||
L'ABR combine les avantages :
|
||||
- Recherche rapide (comme une liste triée avec dichotomie)
|
||||
- Insertion rapide (pas besoin de décaler des éléments)
|
||||
|
||||
---
|
||||
|
||||
## 3. Implémentation en Python
|
||||
|
||||
Nous utilisons une classe `Noeud` pour représenter chaque nœud de l'arbre :
|
||||
|
||||
```python
|
||||
class Noeud:
|
||||
def __init__(self, valeur):
|
||||
self.valeur = valeur
|
||||
self.gauche = None
|
||||
self.droit = None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Recherche dans un ABR
|
||||
|
||||
### Principe
|
||||
|
||||
Pour rechercher une valeur `v` dans un ABR :
|
||||
1. Si l'arbre est vide → la valeur n'est pas présente
|
||||
2. Si `v == valeur du nœud courant` → trouvé !
|
||||
3. Si `v < valeur du nœud courant` → chercher dans le sous-arbre gauche
|
||||
4. Si `v > valeur du nœud courant` → chercher dans le sous-arbre droit
|
||||
|
||||
### Algorithme récursif
|
||||
|
||||
```python
|
||||
def rechercher(noeud, valeur):
|
||||
"""
|
||||
Recherche une valeur dans un ABR.
|
||||
Retourne True si trouvée, False sinon.
|
||||
"""
|
||||
# Cas de base : arbre vide
|
||||
if noeud is None:
|
||||
return False
|
||||
|
||||
# Valeur trouvée
|
||||
if valeur == noeud.valeur:
|
||||
return True
|
||||
|
||||
# Recherche dans le sous-arbre approprié
|
||||
if valeur < noeud.valeur:
|
||||
return rechercher(noeud.gauche, valeur)
|
||||
else:
|
||||
return rechercher(noeud.droit, valeur)
|
||||
```
|
||||
|
||||
### Exemple de déroulement
|
||||
|
||||
Recherche de 7 dans l'ABR :
|
||||
```
|
||||
8 8 > 7 → aller à gauche
|
||||
/ \
|
||||
3 10 3 < 7 → aller à droite
|
||||
/ \
|
||||
1 6 6 < 7 → aller à droite
|
||||
/ \
|
||||
4 7 7 == 7 → TROUVÉ !
|
||||
```
|
||||
|
||||
### Complexité
|
||||
|
||||
- **Meilleur cas** : O(1) — la valeur est à la racine
|
||||
- **Cas moyen** (arbre équilibré) : **O(log n)** — on divise par 2 à chaque étape
|
||||
- **Pire cas** (arbre dégénéré/filiforme) : O(n) — l'arbre ressemble à une liste
|
||||
|
||||
---
|
||||
|
||||
## 5. Insertion dans un ABR
|
||||
|
||||
### Principe
|
||||
|
||||
Pour insérer une valeur `v` dans un ABR :
|
||||
1. Si l'arbre est vide → créer un nouveau nœud avec `v`
|
||||
2. Si `v < valeur du nœud courant` → insérer dans le sous-arbre gauche
|
||||
3. Si `v > valeur du nœud courant` → insérer dans le sous-arbre droit
|
||||
4. Si `v == valeur du nœud courant` → ne rien faire (pas de doublons)
|
||||
|
||||
### Algorithme récursif
|
||||
|
||||
```python
|
||||
def inserer(noeud, valeur):
|
||||
"""
|
||||
Insère une valeur dans un ABR.
|
||||
Retourne la racine de l'arbre (éventuellement modifiée).
|
||||
"""
|
||||
# Cas de base : arbre vide, on crée un nouveau nœud
|
||||
if noeud is None:
|
||||
return Noeud(valeur)
|
||||
|
||||
# Insertion récursive
|
||||
if valeur < noeud.valeur:
|
||||
noeud.gauche = inserer(noeud.gauche, valeur)
|
||||
elif valeur > noeud.valeur:
|
||||
noeud.droit = inserer(noeud.droit, valeur)
|
||||
# Si valeur == noeud.valeur, on ne fait rien (pas de doublons)
|
||||
|
||||
return noeud
|
||||
```
|
||||
|
||||
### Exemple : construction d'un ABR
|
||||
|
||||
Insertion successive de : 8, 3, 10, 1, 6, 14, 4, 7, 13
|
||||
|
||||
```
|
||||
Étape 1: 8 Étape 2: 8 Étape 3: 8
|
||||
/ / \
|
||||
3 3 10
|
||||
|
||||
Étape 4: 8 Étape 5: 8 ... Résultat final:
|
||||
/ \ / \
|
||||
3 10 3 10 8
|
||||
/ / \ / \
|
||||
1 1 6 3 10
|
||||
/ \ \
|
||||
1 6 14
|
||||
/ \ /
|
||||
4 7 13
|
||||
```
|
||||
|
||||
### Complexité
|
||||
|
||||
- **Cas moyen** : **O(log n)**
|
||||
- **Pire cas** : O(n) — si on insère des valeurs déjà triées, l'arbre devient filiforme
|
||||
|
||||
---
|
||||
|
||||
## 6. Suppression dans un ABR
|
||||
|
||||
La suppression est l'opération la plus complexe. Il y a trois cas à considérer :
|
||||
|
||||
### Cas 1 : Le nœud à supprimer est une feuille
|
||||
|
||||
On le supprime simplement.
|
||||
|
||||
```
|
||||
Supprimer 4 :
|
||||
6 6
|
||||
/ \ → / \
|
||||
4 7 7
|
||||
```
|
||||
|
||||
### Cas 2 : Le nœud a un seul enfant
|
||||
|
||||
On le remplace par son unique enfant.
|
||||
|
||||
```
|
||||
Supprimer 10 :
|
||||
8 8
|
||||
/ \ → / \
|
||||
3 10 3 14
|
||||
\ /
|
||||
14 13
|
||||
/
|
||||
13
|
||||
```
|
||||
|
||||
### Cas 3 : Le nœud a deux enfants
|
||||
|
||||
On le remplace par :
|
||||
- Son **successeur** : le plus petit élément du sous-arbre droit
|
||||
- Ou son **prédécesseur** : le plus grand élément du sous-arbre gauche
|
||||
|
||||
Puis on supprime récursivement ce successeur/prédécesseur.
|
||||
|
||||
```
|
||||
Supprimer 3 (avec successeur = 4) :
|
||||
8 8
|
||||
/ \ / \
|
||||
3 10 → 4 10
|
||||
/ \ \ / \ \
|
||||
1 6 14 1 6 14
|
||||
/ \ / \ /
|
||||
4 7 13 7 13
|
||||
```
|
||||
|
||||
### Algorithme récursif
|
||||
|
||||
```python
|
||||
def trouver_min(noeud):
|
||||
"""Trouve le nœud avec la valeur minimale (le plus à gauche)."""
|
||||
while noeud.gauche is not None:
|
||||
noeud = noeud.gauche
|
||||
return noeud
|
||||
|
||||
def supprimer(noeud, valeur):
|
||||
"""
|
||||
Supprime une valeur d'un ABR.
|
||||
Retourne la racine de l'arbre (éventuellement modifiée).
|
||||
"""
|
||||
# Cas de base : arbre vide
|
||||
if noeud is None:
|
||||
return None
|
||||
|
||||
# Recherche du nœud à supprimer
|
||||
if valeur < noeud.valeur:
|
||||
noeud.gauche = supprimer(noeud.gauche, valeur)
|
||||
elif valeur > noeud.valeur:
|
||||
noeud.droit = supprimer(noeud.droit, valeur)
|
||||
else:
|
||||
# Nœud trouvé, on le supprime
|
||||
|
||||
# Cas 1 et 2 : 0 ou 1 enfant
|
||||
if noeud.gauche is None:
|
||||
return noeud.droit
|
||||
elif noeud.droit is None:
|
||||
return noeud.gauche
|
||||
|
||||
# Cas 3 : 2 enfants
|
||||
# On remplace par le successeur (min du sous-arbre droit)
|
||||
successeur = trouver_min(noeud.droit)
|
||||
noeud.valeur = successeur.valeur
|
||||
noeud.droit = supprimer(noeud.droit, successeur.valeur)
|
||||
|
||||
return noeud
|
||||
```
|
||||
|
||||
### Complexité
|
||||
|
||||
- **Cas moyen** : **O(log n)**
|
||||
- **Pire cas** : O(n)
|
||||
|
||||
---
|
||||
|
||||
## 7. Parcours infixe et tri
|
||||
|
||||
Une propriété remarquable des ABR : le **parcours infixe** donne les éléments **dans l'ordre croissant**.
|
||||
|
||||
```python
|
||||
def parcours_infixe(noeud):
|
||||
"""Affiche les éléments de l'ABR dans l'ordre croissant."""
|
||||
if noeud is not None:
|
||||
parcours_infixe(noeud.gauche)
|
||||
print(noeud.valeur, end=" ")
|
||||
parcours_infixe(noeud.droit)
|
||||
```
|
||||
|
||||
Pour l'ABR d'exemple :
|
||||
```
|
||||
Parcours infixe : 1 3 4 6 7 8 10 13 14
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Vérifier si un arbre est un ABR
|
||||
|
||||
Attention : il ne suffit pas de vérifier localement que `gauche < noeud < droit`. Il faut vérifier que **toutes** les valeurs du sous-arbre gauche sont inférieures.
|
||||
|
||||
```python
|
||||
def est_ABR(noeud, mini=float('-inf'), maxi=float('inf')):
|
||||
"""
|
||||
Vérifie si l'arbre est un ABR valide.
|
||||
mini et maxi définissent l'intervalle autorisé pour la valeur du nœud.
|
||||
"""
|
||||
if noeud is None:
|
||||
return True
|
||||
|
||||
# La valeur doit être dans l'intervalle ]mini, maxi[
|
||||
if not (mini < noeud.valeur < maxi):
|
||||
return False
|
||||
|
||||
# Vérification récursive avec mise à jour des bornes
|
||||
return (est_ABR(noeud.gauche, mini, noeud.valeur) and
|
||||
est_ABR(noeud.droit, noeud.valeur, maxi))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Problème des arbres dégénérés
|
||||
|
||||
Si on insère des valeurs dans l'ordre croissant (1, 2, 3, 4, 5...), l'ABR devient une liste chaînée :
|
||||
|
||||
```
|
||||
1
|
||||
\
|
||||
2
|
||||
\
|
||||
3
|
||||
\
|
||||
4
|
||||
\
|
||||
5
|
||||
```
|
||||
|
||||
Dans ce cas, toutes les opérations deviennent en O(n) au lieu de O(log n).
|
||||
|
||||
### Solution : les arbres équilibrés
|
||||
|
||||
Pour garantir O(log n), on utilise des **arbres équilibrés** :
|
||||
- **AVL** : après chaque insertion/suppression, on rééquilibre si nécessaire
|
||||
- **Arbres rouge-noir** : utilisés dans beaucoup de bibliothèques standard
|
||||
|
||||
> Ces structures sont hors programme en NSI, mais il est important de comprendre pourquoi elles existent.
|
||||
|
||||
---
|
||||
|
||||
## 10. Résumé des complexités
|
||||
|
||||
| Opération | ABR équilibré | ABR dégénéré |
|
||||
|-----------|---------------|--------------|
|
||||
| Recherche | O(log n) | O(n) |
|
||||
| Insertion | O(log n) | O(n) |
|
||||
| Suppression | O(log n) | O(n) |
|
||||
| Parcours complet | O(n) | O(n) |
|
||||
|
||||
---
|
||||
|
||||
## 11. Exercices
|
||||
|
||||
### Exercice 1 : Construction d'un ABR
|
||||
Dessiner l'ABR obtenu après insertion successive des valeurs : 50, 30, 70, 20, 40, 60, 80, 35
|
||||
|
||||
### Exercice 2 : Recherche
|
||||
Sur l'ABR de l'exercice 1, combien de comparaisons faut-il pour :
|
||||
- Trouver 35 ?
|
||||
- Constater que 45 n'est pas présent ?
|
||||
|
||||
### Exercice 3 : Suppression
|
||||
Sur l'ABR de l'exercice 1, dessiner l'arbre après suppression de :
|
||||
1. 20 (feuille)
|
||||
2. 30 (nœud avec 2 enfants)
|
||||
|
||||
### Exercice 4 : Implémentation
|
||||
Compléter la classe suivante avec les méthodes manquantes :
|
||||
|
||||
```python
|
||||
class ABR:
|
||||
def __init__(self):
|
||||
self.racine = None
|
||||
|
||||
def inserer(self, valeur):
|
||||
"""Insère une valeur dans l'ABR."""
|
||||
pass
|
||||
|
||||
def rechercher(self, valeur):
|
||||
"""Retourne True si valeur est dans l'ABR."""
|
||||
pass
|
||||
|
||||
def supprimer(self, valeur):
|
||||
"""Supprime une valeur de l'ABR."""
|
||||
pass
|
||||
|
||||
def afficher_trie(self):
|
||||
"""Affiche toutes les valeurs dans l'ordre croissant."""
|
||||
pass
|
||||
```
|
||||
|
||||
### Exercice 5 : Analyse
|
||||
On insère n valeurs aléatoires dans un ABR initialement vide.
|
||||
1. Quelle est la hauteur moyenne de l'arbre obtenu ?
|
||||
2. Que se passe-t-il si les valeurs sont insérées dans l'ordre croissant ?
|
||||
|
||||
---
|
||||
|
||||
Auteur : Florian Mathieu
|
||||
|
||||
Licence CC BY NC
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|
||||
770
Arbres/ARBRES_BINAIRES.ipynb
Normal file
770
Arbres/ARBRES_BINAIRES.ipynb
Normal file
@@ -0,0 +1,770 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Les arbres binaires\n",
|
||||
"\n",
|
||||
"> Parmi la forêt d'arbres possibles, on s'intéressera essentiellement aux arbres dit binaires.\n",
|
||||
"> Ceux ci sont principalement utilisés pour les bases de données (indexation), les moteurs de recherche, et les compilateurs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Définition\n",
|
||||
"\n",
|
||||
"Un arbre **binaire** est un arbre de degré 2 (dont les noeuds sont de degré 2 au plus).\n",
|
||||
"\n",
|
||||
"Les enfants d'un noeud sont lus de **gauche à droite** et sont appelés **fils gauche** et **fils droit**.\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 1\n",
|
||||
"\n",
|
||||
"Parmi les arbres du cours précédent, lesquels sont binaires ?\n",
|
||||
"\n",
|
||||
"**Réponse :**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Remarque\n",
|
||||
"\n",
|
||||
"Les arbres binaires forment une structure de données qui peut se définir de façon récursive.\n",
|
||||
"\n",
|
||||
"Un arbre binaire est:\n",
|
||||
"- soit vide\n",
|
||||
"- 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.\n",
|
||||
"\n",
|
||||
"Les arbres binaires sont utilisés dans de très nombreuses activités informatiques, nous allons étudier et implanter cette structure de données."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Comment représenter un arbre binaire... à la main...\n",
|
||||
"\n",
|
||||
"L'idée est de représenter l'arbre avec un tableau.\n",
|
||||
"\n",
|
||||
"`[r, a, b]` La racine r suivie de ses fils gauche et droit.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Puis on rajoute dans l'ordre les fils gauche et droit de a, puis ceux de b.\n",
|
||||
"\n",
|
||||
"`[r, a, b, c, d, e, f]`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Remarque\n",
|
||||
"\n",
|
||||
"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*.\n",
|
||||
"\n",
|
||||
"**Exemple:** **b** est d'indice 2, son fils gauche se trouve alors à l'indice 5 et son fils droit à l'indice 6.\n",
|
||||
"\n",
|
||||
"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:\n",
|
||||
"\n",
|
||||
"`[r, a, b, c, d, e, f, None, None, None, None, None, None, None, None]`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Quelle taille doit avoir le tableau ?\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Cet arbre est complet (tous les noeuds internes ont deux fils).\n",
|
||||
"\n",
|
||||
"Il possède 3 niveaux, sa hauteur ou profondeur est donc de 2. La taille du tableau sera de:\n",
|
||||
"\n",
|
||||
"$$2^0 + 2^1 + 2^2 + 2^3 = 2^4 -1 = 15$$\n",
|
||||
"\n",
|
||||
"*Il faut compter le nombre de noeuds, y compris les noeuds \"fantômes\" des feuilles.*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 2\n",
|
||||
"\n",
|
||||
"1. Quelle est la taille du tableau qui permet de représenter cet arbre ?\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"2. Écrire le tableau représentant cet arbre et le stocker dans une variable **t**\n",
|
||||
"\n",
|
||||
"3. Quelle propriété ont les indices des fils gauches et droits ?\n",
|
||||
"\n",
|
||||
"4. Voici un tableau représentant un arbre binaire. Le dessiner. Que peut-il représenter ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Tableau de la question 4\n",
|
||||
"arbre_expression = ['*', '-', 5, 2, 6, None, None, None, None, None, None, None, None, None, None]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 2 : Écrire le tableau représentant l'arbre\n",
|
||||
"t = # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Code Python pour créer un arbre\n",
|
||||
"\n",
|
||||
"Voici un code Python qui crée la liste représentant l'arbre de l'exercice 2 :"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def creation_arbre(r, profondeur):\n",
|
||||
" \"\"\"r : la racine (str ou int). la profondeur de l'arbre (int)\"\"\"\n",
|
||||
" Arbre = [r] + [None for i in range(2**(profondeur+1)-2)]\n",
|
||||
" return Arbre\n",
|
||||
"\n",
|
||||
"def insertion_noeud(arbre, n, fg, fd):\n",
|
||||
" \"\"\"Insère les noeuds et leurs enfants dans l'arbre\"\"\"\n",
|
||||
" indice = arbre.index(n)\n",
|
||||
" arbre[2*indice+1] = fg\n",
|
||||
" arbre[2*indice+2] = fd\n",
|
||||
"\n",
|
||||
"# création de l'arbre\n",
|
||||
"arbre = creation_arbre(\"r\", 5)\n",
|
||||
"# ajout des noeuds par niveau de gauche à droite\n",
|
||||
"insertion_noeud(arbre, \"r\", \"a\", \"b\")\n",
|
||||
"insertion_noeud(arbre, \"a\", \"c\", \"d\")\n",
|
||||
"insertion_noeud(arbre, \"b\", \"e\", \"f\")\n",
|
||||
"insertion_noeud(arbre, \"c\", None, \"h\")\n",
|
||||
"insertion_noeud(arbre, \"d\", \"i\", \"j\")\n",
|
||||
"insertion_noeud(arbre, \"e\", \"k\", None)\n",
|
||||
"insertion_noeud(arbre, \"f\", None, None)\n",
|
||||
"insertion_noeud(arbre, \"h\", None, None)\n",
|
||||
"insertion_noeud(arbre, \"i\", None, None)\n",
|
||||
"insertion_noeud(arbre, \"j\", \"m\", None)\n",
|
||||
"insertion_noeud(arbre, \"k\", None, None)\n",
|
||||
"insertion_noeud(arbre, \"m\", None, None)\n",
|
||||
"\n",
|
||||
"# pour vérifier\n",
|
||||
"print(\"Taille du tableau :\", len(arbre))\n",
|
||||
"print(\"Arbre :\", arbre)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 3\n",
|
||||
"\n",
|
||||
"1. Écrire une fonction qui retourne le parent d'un noeud s'il est dans l'arbre et None sinon.\n",
|
||||
"2. Écrire une fonction qui retourne **True** si l'arbre est vide\n",
|
||||
"3. Écrire une fonction qui retourne les enfants d'un noeud.\n",
|
||||
"4. Une fonction qui renvoie le fils gauche d'un noeud s'il existe et None sinon\n",
|
||||
"5. Même question pour le fils droit\n",
|
||||
"6. Écrire une fonction qui renvoie **True** si le noeud est la racine de l'arbre\n",
|
||||
"7. Écrire une fonction qui renvoie **True** si le noeud est une feuille\n",
|
||||
"8. Écrire une fonction qui renvoie **True** si le noeud comporte un frère gauche ou droit.\n",
|
||||
"\n",
|
||||
"**Rappel : dans un tableau, si on note i l'indice d'un noeud, alors ses fils auront comme indice 2i+1 et 2i+2.**\n",
|
||||
"\n",
|
||||
"**Inversement, pour trouver le parent d'un noeud, il faut calculer : (i-1) // 2**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 1 : Parent d'un noeud\n",
|
||||
"def parent(arbre, noeud):\n",
|
||||
" \"\"\"Retourne le parent du noeud s'il existe, None sinon\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 2 : Arbre vide\n",
|
||||
"def est_vide(arbre):\n",
|
||||
" \"\"\"Retourne True si l'arbre est vide\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 3 : Enfants d'un noeud\n",
|
||||
"def enfants(arbre, noeud):\n",
|
||||
" \"\"\"Retourne les enfants du noeud\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 4 : Fils gauche\n",
|
||||
"def fils_gauche(arbre, noeud):\n",
|
||||
" \"\"\"Retourne le fils gauche s'il existe, None sinon\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 5 : Fils droit\n",
|
||||
"def fils_droit(arbre, noeud):\n",
|
||||
" \"\"\"Retourne le fils droit s'il existe, None sinon\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 6 : Est racine\n",
|
||||
"def est_racine(arbre, noeud):\n",
|
||||
" \"\"\"Retourne True si le noeud est la racine\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 7 : Est feuille\n",
|
||||
"def est_feuille(arbre, noeud):\n",
|
||||
" \"\"\"Retourne True si le noeud est une feuille\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Question 8 : A un frère\n",
|
||||
"def a_frere(arbre, noeud):\n",
|
||||
" \"\"\"Retourne True si le noeud a un frère\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Une seconde façon de faire...\n",
|
||||
"\n",
|
||||
"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.\n",
|
||||
"\n",
|
||||
"L'idée est de représenter l'arbre avec un tableau contenant des tableaux.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Cet arbre se représente par le tableau:\n",
|
||||
"\n",
|
||||
"`['r', ['a', [], []], ['b', [], []]]`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 4\n",
|
||||
"\n",
|
||||
"Écrire le tableau représentant l'arbre ci-dessous et le stocker dans une variable **t**\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Exercice 4 : Représentation avec tableaux imbriqués\n",
|
||||
"t = # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Construction récursive avec dictionnaires\n",
|
||||
"\n",
|
||||
"Le code Python ci-dessous construit l'arbre de manière récursive.\n",
|
||||
"\n",
|
||||
"* Les noeuds sont représentés par un dictionnaire.\n",
|
||||
"* L'arbre se construit depuis la racine en construisant les sous-arbres des fils gauche et droit."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def noeud(nom, fg=None, fd=None):\n",
|
||||
" return {'racine': nom, 'fg': fg, 'fd': fd}\n",
|
||||
"\n",
|
||||
"# création des noeuds\n",
|
||||
"k = noeud('k')\n",
|
||||
"f = noeud('f')\n",
|
||||
"e = noeud('e', k, None)\n",
|
||||
"b = noeud('b', e, f)\n",
|
||||
"m = noeud('m')\n",
|
||||
"j = noeud('j', m, None)\n",
|
||||
"i = noeud('i')\n",
|
||||
"d = noeud('d', i, j)\n",
|
||||
"h = noeud('h')\n",
|
||||
"c = noeud('c', None, h)\n",
|
||||
"a = noeud('a', c, d)\n",
|
||||
"racine = noeud('r', a, b)\n",
|
||||
"\n",
|
||||
"# création de l'arbre sous forme de liste imbriquée\n",
|
||||
"def construit(arbre):\n",
|
||||
" if arbre is None:\n",
|
||||
" return []\n",
|
||||
" else:\n",
|
||||
" return [arbre['racine'], construit(arbre['fg']), construit(arbre['fd'])]\n",
|
||||
"\n",
|
||||
"arbre1 = construit(racine)\n",
|
||||
"print(arbre1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 5\n",
|
||||
"\n",
|
||||
"Écrire toutes les fonctions de l'exercice 3 dans le cas de cette implémentation de l'arbre (tableaux imbriqués)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Exercice 5 : Fonctions pour tableaux imbriqués\n",
|
||||
"# À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Hauteur et parcours d'un arbre\n",
|
||||
"\n",
|
||||
"Nous allons maintenant nous intéresser à la hauteur de l'arbre, ainsi qu'aux différentes façons de le parcourir:\n",
|
||||
"* Le parcours en largeur\n",
|
||||
"* Le parcours en profondeur (parcours préfixe, parcours infixe et parcours suffixe)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Calcul de la hauteur de l'arbre\n",
|
||||
"\n",
|
||||
"L'idée est la suivante:\n",
|
||||
"* Si l'arbre est vide, la hauteur vaut -1\n",
|
||||
"* Sinon la hauteur vaut 1 auquel il faut ajouter le maximum entre les hauteurs des sous-arbres gauche et droit.\n",
|
||||
"* Ces sous-arbres sont eux-mêmes des arbres dont il faut calculer la hauteur.\n",
|
||||
"\n",
|
||||
"Une méthode **récursive** semble donc tout à fait adaptée à la situation.\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 6\n",
|
||||
"\n",
|
||||
"Écrire cette fonction pour l'arbre précédent et vérifier que sa profondeur est de 4."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Exercice 6 : Hauteur de l'arbre\n",
|
||||
"def hauteur(arbre):\n",
|
||||
" \"\"\"Retourne la hauteur de l'arbre\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
"\n",
|
||||
"# Test\n",
|
||||
"# print(hauteur(arbre1)) # Doit afficher 4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Les parcours\n",
|
||||
"\n",
|
||||
"### Le parcours en largeur\n",
|
||||
"\n",
|
||||
"Le parcours en largeur d'un arbre consiste à partir de la racine. On visite ensuite son fils gauche puis son fils droit, puis le fils gauche du fils gauche etc... Comme le montre le schéma ci-dessous:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"L'idée est la suivante:\n",
|
||||
"\n",
|
||||
"On utilise une **File**.\n",
|
||||
"* On met l'arbre dans la file.\n",
|
||||
"* Puis tant que la file n'est pas vide:\n",
|
||||
" * On défile la file\n",
|
||||
" * On récupère sa racine\n",
|
||||
" * On enfile son fils gauche s'il existe\n",
|
||||
" * On enfile son fils droit s'il existe\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 7\n",
|
||||
"\n",
|
||||
"1. Rappeler les fonctions permettant de définir une structure de file en Python.\n",
|
||||
"2. Implémenter alors cette fonction et l'essayer sur l'arbre précédent."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Exercice 7 : Parcours en largeur\n",
|
||||
"def parcours_largeur(arbre):\n",
|
||||
" \"\"\"Parcours en largeur de l'arbre\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Les parcours en profondeur\n",
|
||||
"\n",
|
||||
"On se balade autour de l'arbre en suivant les pointillés.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Voici un algorithme permettant de réaliser ce parcours:\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 8\n",
|
||||
"\n",
|
||||
"Proposer une fonction permettant de réaliser le parcours en profondeur d'un arbre."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Exercice 8 : Parcours en profondeur\n",
|
||||
"def parcours_profondeur(arbre):\n",
|
||||
" \"\"\"Parcours en profondeur de l'arbre\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Les trois types de parcours en profondeur\n",
|
||||
"\n",
|
||||
"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:\n",
|
||||
"* une fois par la gauche\n",
|
||||
"* une fois par en dessous\n",
|
||||
"* une fois par la droite\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"**Définition - Parcours préfixe :**\n",
|
||||
"Dans un parcours **préfixe**, on liste le noeud la **première fois** qu'on le rencontre.\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"**Définition - Parcours infixe :**\n",
|
||||
"Dans un parcours **infixe**, on liste le noeud la **seconde fois** qu'on le rencontre.\n",
|
||||
"\n",
|
||||
"Ce qui correspond à:\n",
|
||||
"* On liste chaque noeud ayant un fils gauche la seconde fois qu'on le voit\n",
|
||||
"* On liste chaque noeud sans fils gauche la première fois qu'on le voit\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"**Définition - Parcours suffixe :**\n",
|
||||
"Dans un parcours **suffixe**, on note le noeud la **dernière fois** qu'on le rencontre."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 9\n",
|
||||
"\n",
|
||||
"Écrire les sommets dans l'ordre d'un parcours préfixe."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse :**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 10\n",
|
||||
"\n",
|
||||
"Écrire les sommets dans l'ordre d'un parcours infixe."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse :**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 11\n",
|
||||
"\n",
|
||||
"Écrire les sommets dans l'ordre d'un parcours suffixe."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse :**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 12\n",
|
||||
"\n",
|
||||
"1. Voici trois algorithmes récursifs, dire pour chacun d'entre eux à quel parcours il correspond.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"2. Implémenter ces trois fonctions en Python et confirmer les réponses des questions précédentes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Exercice 12 : Implémentation des trois parcours\n",
|
||||
"\n",
|
||||
"def parcours_1(arbre):\n",
|
||||
" \"\"\"Premier algorithme - À identifier\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
"\n",
|
||||
"def parcours_2(arbre):\n",
|
||||
" \"\"\"Deuxième algorithme - À identifier\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
"\n",
|
||||
"def parcours_3(arbre):\n",
|
||||
" \"\"\"Troisième algorithme - À identifier\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Une troisième façon de faire... en programmation objet\n",
|
||||
"\n",
|
||||
"Nous allons créer une classe **Noeud** dont les attributs d'instances seront:\n",
|
||||
"* le nom (ou valeur) de la racine\n",
|
||||
"* son fils gauche (None par défaut) ou de type Noeud\n",
|
||||
"* son fils droit (None par défaut) ou de type Noeud"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercice 13\n",
|
||||
"\n",
|
||||
"1. Implémentez cette classe Noeud\n",
|
||||
"2. Stocker l'arbre comme étant l'objet de type \"Noeud\" correspondant à la racine.\n",
|
||||
"3. Écrire une méthode `est_feuille` qui renvoie True si le noeud est une feuille et False sinon\n",
|
||||
"4. Définir une fonction `hauteur` prenant comme argument un arbre permettant de renvoyer la hauteur de cet arbre\n",
|
||||
"5. Écrire une méthode `hauteur` dans la classe Noeud permettant de faire la même chose\n",
|
||||
"6. Proposer des fonctions pour le parcours infixe, préfixe et suffixe pour cette façon de coder un arbre.\n",
|
||||
"7. Transformer alors vos fonctions en méthodes de la classe Noeud"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Exercice 13 : Classe Noeud\n",
|
||||
"\n",
|
||||
"class Noeud:\n",
|
||||
" def __init__(self, valeur, gauche=None, droit=None):\n",
|
||||
" \"\"\"Constructeur de la classe Noeud\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
" \n",
|
||||
" def est_feuille(self):\n",
|
||||
" \"\"\"Retourne True si le noeud est une feuille\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
" \n",
|
||||
" def hauteur(self):\n",
|
||||
" \"\"\"Retourne la hauteur de l'arbre\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
" \n",
|
||||
" def parcours_prefixe(self):\n",
|
||||
" \"\"\"Parcours préfixe de l'arbre\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
" \n",
|
||||
" def parcours_infixe(self):\n",
|
||||
" \"\"\"Parcours infixe de l'arbre\"\"\"\n",
|
||||
" pass # À compléter\n",
|
||||
" \n",
|
||||
" def parcours_suffixe(self):\n",
|
||||
" \"\"\"Parcours suffixe de l'arbre\"\"\"\n",
|
||||
" pass # À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Construction de l'arbre avec la classe Noeud\n",
|
||||
"# À compléter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"\n",
|
||||
"### Sources\n",
|
||||
"\n",
|
||||
"- [Cours de Stephan Van Zulen](https://www.nsi-ljm.fr/NSI-TLE/co/section_chapitre3.html)\n",
|
||||
"- [Cours de Jeremy Camponovo](https://github.com/jcamponovo)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
### Définition:
|
||||
Un arbre **binaire** est un arbre de degré 2 (dont lesnoeuds sont de degré 2 au plus).
|
||||
Un arbre **binaire** est un arbre de degré 2 (dont les noeuds 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**.
|
||||
|
||||
@@ -72,30 +72,16 @@ Il possède 3 niveaux, sa hauteur ou profondeur est donc de 2. La taille du tabl
|
||||
|
||||
4. Voici un tableau représentant un arbre binaire:
|
||||
|
||||
```python
|
||||
['*','-',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
|
||||
|
||||
@@ -152,53 +138,13 @@ print(arbre)
|
||||
|
||||
**<u>Inversement, pour trouver le parent d'un noeud, il faut vérifier : i - 1 //2</u>**
|
||||
|
||||
|
||||
```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
|
||||
L'idée est de représenter l'arbre avec un tableau contenant des tableaux
|
||||
|
||||

|
||||
|
||||
@@ -251,52 +197,14 @@ print(arbre1)
|
||||
### 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).
|
||||
* Le parcours en profondeur (parcours préfixe, parcours infixe et parcours suffixe).
|
||||
|
||||
**Calcul de la hauteur de l'arbre:**
|
||||
L'idée ets la suivante:
|
||||
L'idée est la suivante:
|
||||
* Si l'arbre est 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.
|
||||
@@ -318,7 +226,7 @@ Voici l'algorithme:
|
||||
## 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:
|
||||
Le parcours en largeur d'un arbre consiste à partir de la racine. On visite ensuite son fils gauche puis son fils droit, puis le fils gauche du fils gauche etc... Comme le montre le schéma ci-dessous:
|
||||
|
||||

|
||||
|
||||
@@ -344,14 +252,6 @@ Voici l'algorithme:
|
||||
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
|
||||
@@ -374,8 +274,8 @@ Dans le schéma ci-dessus, on a rajouté des "noeuds fantômes" pour montrer que
|
||||
* 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.
|
||||
### Définition:
|
||||
Dans un parcours **préfixe**, on liste le noeud la première fois qu'on le rencontre.
|
||||
|
||||
|
||||
### Exercice
|
||||
@@ -429,9 +329,6 @@ Nous allons créer une classe **Noeud** dont les attributs d'instances seront:
|
||||
7. Transformer alors vos fonctions en méthodes de la classe Noeud
|
||||
|
||||
|
||||
```python
|
||||
|
||||
```
|
||||
|
||||
-------
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Structures Arborescentes
|
||||
# Structures Arborescentes
|
||||
|
||||
> Structure présente partout dans le monde numérique, des répertoires de fichiers sur un ordinateur aux bases de données et algorithmes de recherche, les abres sont un pan essentiel de l'informatique. Ils permettent de structurer efficacement des données hiérarchiques et d’optimiser certaines opérations comme la recherche ou le tri. En NSI, nous allons explorer une version particulière de ces structures : les arbres binaires.
|
||||
> Structure présente partout dans le monde numérique, des répertoires de fichiers sur un ordinateur aux bases de données et algorithmes de recherche, les arbres sont un pan essentiel de l'informatique. Ils permettent de structurer efficacement des données hiérarchiques et d’optimiser certaines opérations comme la recherche ou le tri. En NSI, nous allons explorer une version particulière de ces structures : les arbres binaires.
|
||||
|
||||
---------------------------------
|
||||
|
||||
|
||||
12
Arbres/TD.md
12
Arbres/TD.md
@@ -3,13 +3,13 @@
|
||||
Soit les 4 arbres binaires suivant :
|
||||
|
||||
<div style="display: flex; justify-content: space-around;">
|
||||
<img src="assets/img2.png" alt="Méthodes_natives" style="zoom: 80%;" />
|
||||
|
||||
<img src="assets/img3.png" alt="Méthodes_natives" style="zoom: 80%;" />
|
||||
<img src="assets/img2.png" alt="Arbre 1" style="zoom: 80%;" />
|
||||
<img src="assets/img3.png" alt="Arbre 2" style="zoom: 80%;" />
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: space-around;">
|
||||
<img src="assets/img4.png" alt="Méthodes_natives" style="zoom: 80%;" />
|
||||
<img src="assets/img5.png" alt="Méthodes_natives" style="zoom: 80%;" />
|
||||
<img src="assets/img4.png" alt="Arbre 3" style="zoom: 80%;" />
|
||||
<img src="assets/img5.png" alt="Arbre 4" style="zoom: 80%;" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Question 1 - Compléter le tableau :
|
||||
|arbre 3| | | | | |
|
||||
|arbre 4| | | | | |
|
||||
|
||||
Question 2 - En utilisant la classe **BinnaryTree** du module ```arbres_binary_tree```, donner la définition :
|
||||
Question 2 - En utilisant la classe **BinaryTree** du module ```arbres_binary_tree```, donner la définition :
|
||||
|
||||
- en plusieurs affectations (plusieurs lignes) de l'arbre ayant pour racine le noeud **4** ;
|
||||
- en une seule affectation (une ligne) de l'arbre ayant pour racine le noeud **1**.
|
||||
|
||||
281
Arbres/TP_Recommandation.md
Normal file
281
Arbres/TP_Recommandation.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# TP : L'algorithme de recommandation — Comment TikTok/YouTube vous connaît
|
||||
|
||||
> **Thème** : Arbres de décision et algorithmes de recommandation
|
||||
|
||||
---
|
||||
|
||||
## Contexte
|
||||
|
||||
En 2026, les algorithmes de recommandation sont omniprésents : TikTok, YouTube, Spotify, Netflix... Ces plateformes utilisent des **arbres de décision** pour prédire vos goûts et vous proposer du contenu personnalisé.
|
||||
|
||||
Dans ce TP, vous allez construire un mini-système de recommandation de films basé sur un **arbre binaire de décision**.
|
||||
|
||||
---
|
||||
|
||||
## Partie 1 : Comprendre l'arbre de décision
|
||||
|
||||
Un arbre de décision pose des questions binaires (oui/non) pour classifier des données.
|
||||
|
||||
### Exemple
|
||||
|
||||
```
|
||||
Aimes-tu l'action ?
|
||||
/ \
|
||||
OUI NON
|
||||
| |
|
||||
Aimes-tu la SF ? Aimes-tu la romance ?
|
||||
/ \ / \
|
||||
OUI NON OUI NON
|
||||
| | | |
|
||||
"Matrix" "Die Hard" "Titanic" "Le Parrain"
|
||||
```
|
||||
|
||||
### Questions préliminaires
|
||||
|
||||
1. Quel film sera recommandé à quelqu'un qui aime l'action mais pas la SF ?
|
||||
|
||||
2. Combien de questions faut-il poser au maximum pour recommander un film ?
|
||||
|
||||
3. Si on veut recommander 16 films différents, combien de questions faudra-t-il poser au maximum ? (Indice : pensez à la hauteur de l'arbre)
|
||||
|
||||
---
|
||||
|
||||
## Partie 2 : Modélisation avec une classe
|
||||
|
||||
Nous allons créer une classe `NoeudDecision` pour représenter notre arbre.
|
||||
|
||||
### Structure d'un nœud
|
||||
|
||||
Un nœud peut être :
|
||||
- **Un nœud interne** : il contient une question et deux sous-arbres (oui/non)
|
||||
- **Une feuille** : elle contient une recommandation (un film)
|
||||
|
||||
### Exercice 1 : Compléter la classe
|
||||
|
||||
```python
|
||||
class NoeudDecision:
|
||||
def __init__(self, question=None, film=None):
|
||||
"""
|
||||
Crée un nœud de l'arbre de décision.
|
||||
|
||||
Si film is not None : c'est une feuille (recommandation)
|
||||
Sinon : c'est un nœud interne avec une question
|
||||
|
||||
Paramètres:
|
||||
question (str): La question à poser (None si feuille)
|
||||
film (str): Le film à recommander (None si nœud interne)
|
||||
"""
|
||||
self.question = question
|
||||
self.film = film
|
||||
self.oui = None # Sous-arbre si réponse = oui
|
||||
self.non = None # Sous-arbre si réponse = non
|
||||
|
||||
def est_feuille(self):
|
||||
"""Retourne True si ce nœud est une feuille (recommandation)."""
|
||||
# À compléter
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
"""Représentation textuelle du nœud."""
|
||||
if self.est_feuille():
|
||||
return f"Film: {self.film}"
|
||||
else:
|
||||
return f"Question: {self.question}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Partie 3 : Construction de l'arbre
|
||||
|
||||
### Exercice 2 : Construire l'arbre d'exemple
|
||||
|
||||
Complétez la fonction suivante pour construire l'arbre de décision présenté en partie 1.
|
||||
|
||||
```python
|
||||
def construire_arbre_films():
|
||||
"""Construit et retourne l'arbre de recommandation de films."""
|
||||
|
||||
# Création des feuilles (recommandations)
|
||||
matrix = NoeudDecision(film="Matrix")
|
||||
die_hard = NoeudDecision(film="Die Hard")
|
||||
titanic = NoeudDecision(film="Titanic")
|
||||
parrain = NoeudDecision(film="Le Parrain")
|
||||
|
||||
# Création des nœuds internes (questions)
|
||||
# À compléter...
|
||||
|
||||
# Retourner la racine de l'arbre
|
||||
pass
|
||||
```
|
||||
|
||||
### Exercice 3 : Tester la construction
|
||||
|
||||
```python
|
||||
# Créer l'arbre
|
||||
arbre = construire_arbre_films()
|
||||
|
||||
# Vérifier la structure
|
||||
print(arbre) # Doit afficher la question racine
|
||||
print(arbre.oui) # Doit afficher la question sur la SF
|
||||
print(arbre.oui.oui) # Doit afficher "Film: Matrix"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Partie 4 : Parcours et recommandation
|
||||
|
||||
### Exercice 4 : Fonction de recommandation interactive
|
||||
|
||||
Complétez la fonction suivante qui parcourt l'arbre en posant les questions à l'utilisateur :
|
||||
|
||||
```python
|
||||
def recommander(noeud):
|
||||
"""
|
||||
Parcourt l'arbre de décision en posant des questions à l'utilisateur.
|
||||
Retourne le film recommandé.
|
||||
"""
|
||||
# Cas de base : on est sur une feuille
|
||||
if noeud.est_feuille():
|
||||
return noeud.film
|
||||
|
||||
# Afficher la question et récupérer la réponse
|
||||
print(noeud.question)
|
||||
reponse = input("Votre réponse (oui/non) : ").lower().strip()
|
||||
|
||||
# Suivre la branche correspondante
|
||||
# À compléter...
|
||||
pass
|
||||
```
|
||||
|
||||
### Exercice 5 : Programme principal
|
||||
|
||||
```python
|
||||
def main():
|
||||
print("=== Système de recommandation de films ===\n")
|
||||
|
||||
arbre = construire_arbre_films()
|
||||
film = recommander(arbre)
|
||||
|
||||
print(f"\n>>> Je vous recommande : {film}")
|
||||
|
||||
# Lancer le programme
|
||||
main()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Partie 5 : Enrichir l'arbre
|
||||
|
||||
### Exercice 6 : Ajouter des films
|
||||
|
||||
Modifiez la fonction `construire_arbre_films()` pour ajouter au moins **4 films supplémentaires** en créant de nouvelles questions.
|
||||
|
||||
Suggestions de questions :
|
||||
- "Préférez-vous les films récents (après 2010) ?"
|
||||
- "Aimez-vous les films avec beaucoup d'humour ?"
|
||||
- "Préférez-vous les films européens ?"
|
||||
|
||||
### Exercice 7 : Affichage de l'arbre
|
||||
|
||||
Écrivez une fonction qui affiche l'arbre de manière indentée :
|
||||
|
||||
```python
|
||||
def afficher_arbre(noeud, niveau=0):
|
||||
"""
|
||||
Affiche l'arbre de décision de manière indentée.
|
||||
|
||||
Exemple de sortie attendue:
|
||||
Aimes-tu l'action ?
|
||||
├── OUI: Aimes-tu la SF ?
|
||||
│ ├── OUI: Matrix
|
||||
│ └── NON: Die Hard
|
||||
└── NON: Aimes-tu la romance ?
|
||||
├── OUI: Titanic
|
||||
└── NON: Le Parrain
|
||||
"""
|
||||
# À compléter
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Partie 6 : Statistiques sur l'arbre
|
||||
|
||||
### Exercice 8 : Hauteur de l'arbre
|
||||
|
||||
Écrivez une fonction récursive qui calcule la hauteur de l'arbre de décision.
|
||||
|
||||
```python
|
||||
def hauteur(noeud):
|
||||
"""Retourne la hauteur de l'arbre."""
|
||||
# À compléter
|
||||
pass
|
||||
```
|
||||
|
||||
### Exercice 9 : Nombre de films
|
||||
|
||||
Écrivez une fonction qui compte le nombre de films (feuilles) dans l'arbre.
|
||||
|
||||
```python
|
||||
def nombre_films(noeud):
|
||||
"""Retourne le nombre de films (feuilles) dans l'arbre."""
|
||||
# À compléter
|
||||
pass
|
||||
```
|
||||
|
||||
### Exercice 10 : Liste de tous les films
|
||||
|
||||
Écrivez une fonction qui retourne la liste de tous les films de l'arbre.
|
||||
|
||||
```python
|
||||
def tous_les_films(noeud):
|
||||
"""Retourne la liste de tous les films de l'arbre."""
|
||||
# À compléter
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Partie 7 : Pour aller plus loin (Bonus)
|
||||
|
||||
### Réflexion : vers le Machine Learning
|
||||
|
||||
En réalité, les algorithmes de recommandation modernes **apprennent** automatiquement l'arbre à partir de données.
|
||||
|
||||
**Question de réflexion :**
|
||||
|
||||
Imaginez que vous avez une base de données de 1000 utilisateurs avec :
|
||||
- Leurs réponses aux questions (oui/non)
|
||||
- Le film qu'ils ont finalement aimé
|
||||
|
||||
Comment pourriez-vous automatiquement construire le "meilleur" arbre de décision ?
|
||||
|
||||
*Indice : Il faudrait choisir à chaque nœud la question qui "sépare" le mieux les utilisateurs selon leurs préférences.*
|
||||
|
||||
### Recherche
|
||||
|
||||
L'algorithme **ID3** (Iterative Dichotomiser 3) est un algorithme classique pour construire des arbres de décision.
|
||||
|
||||
1. Faites une recherche sur cet algorithme.
|
||||
2. Qu'est-ce que l'**entropie** dans ce contexte ?
|
||||
3. Comment ID3 choisit-il la meilleure question à poser à chaque nœud ?
|
||||
|
||||
---
|
||||
|
||||
## Résumé des notions travaillées
|
||||
|
||||
| Notion | Application dans ce TP |
|
||||
|--------|------------------------|
|
||||
| Arbre binaire | Structure de l'arbre de décision |
|
||||
| Nœud / Feuille | Questions / Recommandations |
|
||||
| Parcours d'arbre | Fonction `recommander()` |
|
||||
| Récursivité | Hauteur, comptage, affichage |
|
||||
| POO | Classe `NoeudDecision` |
|
||||
|
||||
---
|
||||
|
||||
Auteur : Florian Mathieu
|
||||
|
||||
Licence CC BY NC
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|
||||
@@ -91,7 +91,7 @@ class BinaryTree():
|
||||
|
||||
def size(self):
|
||||
"""
|
||||
:return: (int) numbre of Nodes in Tree
|
||||
:return: (int) number of Nodes in Tree
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user