Files
TermNSI/Arbres/ARBRES_BINAIRES.ipynb

771 lines
22 KiB
Plaintext

{
"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",
"![fig15.png](assets/fig15.png)"
]
},
{
"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",
"![fig16.png](assets/fig16.png)\n",
"\n",
"![fig17.png](assets/fig17.png)\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",
"![fig18.png](assets/fig18.png)\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",
"![fig19.png](assets/fig19.png)\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",
"![fig20.png](assets/fig20.png)\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",
"![fig21.png](assets/fig21.png)"
]
},
{
"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",
"![fig22.png](assets/fig22.png)"
]
},
{
"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",
"![fig23.png](assets/fig23.png)\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",
"![fig25.png](assets/fig25.png)"
]
},
{
"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",
"![fig24.png](assets/fig24.png)\n",
"\n",
"Voici un algorithme permettant de réaliser ce parcours:\n",
"\n",
"![fig26.png](assets/fig26.png)"
]
},
{
"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",
"![fig27.png](assets/fig27.png)\n",
"![fig28.png](assets/fig28.png)\n",
"![fig29.png](assets/fig29.png)\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
}