diff --git a/slides/cours_16.md b/slides/cours_16.md index 6286cda8b69ba09d0a2c53c26cfd8e523c907bd9..fa915f4be9b84a6de2e78ce6f30c668b690b745f 100644 --- a/slides/cours_16.md +++ b/slides/cours_16.md @@ -105,166 +105,6 @@ graph TD; ::: -# L'insertion dans un arbre binaire - -* C'est bien joli de pouvoir faire des parcours, recherches, mais si on peut - pas construire l'arbre.... - -## Pour un arbre lexicographique - -* Rechercher la position dans l'arbre où insérer. -* Créer un nœud avec la clé et le rattacher à l'arbre. - -# Exemple d'insertions - -* Clés uniques pour simplifier. -* Insertion de 5, 15, 10, 25, 2, -5, 12, 14, 11. -* Rappel: - * Plus petit que la clé courante => gauche, - * Plus grand que la clé courante => droite. -* Faisons le dessins ensemble - -``` - - - - - - - - - -``` - -## Exercice (3min, puis matrix) - -* Dessiner l'arbre en insérant 20, 30, 60, 40, 10, 15, 25, -5 - -# Pseudo-code d'insertion (1/4) - -* Deux parties: - * Recherche le parent où se passe l'insertion. - * Ajout de l'enfant dans l'arbre. - -## Recherche du parent - -``` -arbre position(arbre, clé) - si est_non_vide(arbre) - si clé < clé(arbre) - suivant = gauche(arbre) - sinon - suivant = droite(arbre) - tant que clé(arbre) != clé && est_non_vide(suivant) - arbre = suivant - si clé < clé(arbre) - suivant = gauche(arbre) - sinon - suivant = droite(arbre) - - retourne arbre -``` - -# Pseudo-code d'insertion (2/4) - -* Deux parties: - * Recherche de la position. - * Ajout dans l'arbre. - -## Ajout de l'enfant - -``` -ajout(arbre, clé) - si est_vide(arbre) - arbre = nœud(clé) - sinon - si clé < clé(arbre) - gauche(arbre) = nœud(clé) - sinon si clé > clé(arbre) - droite(arbre) = nœud(clé) - sinon - retourne -``` - -# Code d'insertion en C - -## Recherche du parent (ensemble) - -. . . - -```C -node *position(node *tree, key_t key) { - node * current = tree; - if (NULL != current) { - node *subtree = key > current->key ? current->right : - current->left; - while (key != current->key && NULL != subtree) { - current = subtree; - subtree = key > current->key ? current->right : - current->left; - } - } - return current; -} -``` - -# L'insertion (3/4) - -* Deux parties: - * Recherche de la position. - * Ajout dans l'arbre. - -## Ajout du fils (pseudo-code) - -``` -rien ajout(arbre, clé) - si est_vide(arbre) - arbre = nœud(clé) - sinon - arbre = position(arbre, clé) - si clé < clé(arbre) - gauche(arbre) = nœud(clé) - sinon si clé > clé(arbre) - droite(arbre) = nœud(clé) - sinon - retourne -``` - - - -# L'insertion (4/4) - -## Ajout du fils (code) - -\scriptsize - -* 2 cas: arbre vide ou pas. -* on retourne un pointeur vers le nœud ajouté (ou `NULL`) - -. . . - -```C -node *add_key(node **tree, key_t key) { - node *new_node = calloc(1, sizeof(*new_node)); - new_node->key = key; - if (NULL == *tree) { - *tree = new_node; - } else { - node * subtree = position(*tree, key); - if (key == subtree->key) { - return NULL; - } else { - if (key > subtree->key) { - subtree->right = new_node; - } else { - subtree->left = new_node; - } - } - } - return new_node; -} -``` - # Parcours d'arbres binaires * Appliquer une opération à tous les nœuds de l'arbre, @@ -663,4 +503,392 @@ int arbre_size(node *tree) { } ``` +# L'insertion dans un arbre binaire + +* C'est bien joli de pouvoir faire des parcours, recherches, mais si on peut + pas construire l'arbre.... + +## Pour un arbre lexicographique + +* Rechercher la position dans l'arbre où insérer. +* Créer un nœud avec la clé et le rattacher à l'arbre. + +# Exemple d'insertions + +* Clés uniques pour simplifier. +* Insertion de 5, 15, 10, 25, 2, -5, 12, 14, 11. +* Rappel: + * Plus petit que la clé courante => gauche, + * Plus grand que la clé courante => droite. +* Faisons le dessins ensemble + +``` + + + + + + + + + +``` + +## Exercice (3min, puis matrix) + +* Dessiner l'arbre en insérant 20, 30, 60, 40, 10, 15, 25, -5 + +# Pseudo-code d'insertion (1/4) + +* Deux parties: + * Recherche le parent où se passe l'insertion. + * Ajout de l'enfant dans l'arbre. + +## Recherche du parent + +``` +arbre position(arbre, clé) + si est_non_vide(arbre) + si clé < clé(arbre) + suivant = gauche(arbre) + sinon + suivant = droite(arbre) + tant que clé(arbre) != clé && est_non_vide(suivant) + arbre = suivant + si clé < clé(arbre) + suivant = gauche(arbre) + sinon + suivant = droite(arbre) + + retourne arbre +``` + +# Pseudo-code d'insertion (2/4) + +* Deux parties: + * Recherche de la position. + * Ajout dans l'arbre. + +## Ajout de l'enfant + +``` +ajout(arbre, clé) + si est_vide(arbre) + arbre = nœud(clé) + sinon + si clé < clé(arbre) + gauche(arbre) = nœud(clé) + sinon si clé > clé(arbre) + droite(arbre) = nœud(clé) + sinon + retourne +``` + +# Code d'insertion en C + +## Recherche du parent (ensemble) + +. . . + +```C +node *position(node *tree, key_t key) { + node * current = tree; + if (NULL != current) { + node *subtree = key > current->key ? current->right : + current->left; + while (key != current->key && NULL != subtree) { + current = subtree; + subtree = key > current->key ? current->right : + current->left; + } + } + return current; +} +``` + +# L'insertion (3/4) + +* Deux parties: + * Recherche de la position. + * Ajout dans l'arbre. + +## Ajout du fils (pseudo-code) + +``` +rien ajout(arbre, clé) + si est_vide(arbre) + arbre = nœud(clé) + sinon + arbre = position(arbre, clé) + si clé < clé(arbre) + gauche(arbre) = nœud(clé) + sinon si clé > clé(arbre) + droite(arbre) = nœud(clé) + sinon + retourne +``` + + + +# L'insertion (4/4) + +## Ajout du fils (code) + +\scriptsize + +* 2 cas: arbre vide ou pas. +* on retourne un pointeur vers le nœud ajouté (ou `NULL`) + +. . . + +```C +node *add_key(node **tree, key_t key) { + node *new_node = calloc(1, sizeof(*new_node)); + new_node->key = key; + if (NULL == *tree) { + *tree = new_node; + } else { + node * subtree = position(*tree, key); + if (key == subtree->key) { + return NULL; + } else { + if (key > subtree->key) { + subtree->right = new_node; + } else { + subtree->left = new_node; + } + } + } + return new_node; +} +``` + +# La suppression de clé + +::: columns + +:::: column + +## Cas simples: + +* le nœud est absent, +* le nœud est une feuille +* le nœuds a un seul fils. + +## Une feuille (le 19 p.ex.). + +```{.mermaid format=pdf width=150 loc=figs/} +flowchart TB; + 10-->20; + 10-->5 + 20-->21 + 20-->19 +``` + +:::: + +:::: column + +## Un seul fils (le 20 p.ex.). + +```{.mermaid format=pdf width=400 loc=figs/} +flowchart TB; + 10-->20; + 10-->5 + 20-->25 + 20-->18 + 25-->24 + 25-->30 + 5-->4; + 5-->8; + style 18 fill:#fff,stroke:#fff,color:#fff +``` + +## Dans tous les cas + +* Chercher le nœud à supprimer: utiliser `position()`. + +:::: + +::: + +# La suppression de clé + + +::: columns + +:::: column + +## Cas compliqué + +* Le nœud à supprimer à (au moins) deux descendants (10). + +```{.mermaid format=pdf width=400 loc=figs/} +flowchart TB; + 10-->20; + 10-->5 + 20-->25 + 20-->18 + 25-->24 + 25-->30 + 5-->4; + 5-->8; +``` + +:::: + +:::: column + +* Si on enlève 10 il se passe quoi? + +. . . + +* On peut pas juste enlever `10` et recoller... +* Proposez une solution bon sang! + +. . . + +## Solution + +* Échange de la valeur à droite dans le sous-arbre de gauche ou + ... +* de la valeur de gauche dans le sous-arbre de droite! +* Puis, on retire le nœud. + +:::: + +::: + + +# Le pseudo-code de la suppression + +## Pour une feuille ou absent (ensemble) + +``` +arbre suppression(arbre, clé) + sous_arbre = position(arbre, clé) + si est_vide(sous_arbre) ou clé(sous_arbre) != clé + retourne vide + sinon + si est_feuille(sous_arbre) et clé(sous_arbre) == clé + nouvelle_feuille = parent(arbre, sous_arbre) + si est_vide(nouvelle_feuille) + arbre = vide + sinon + si gauche(nouvelle_feuille) == sous_arbre + gauche(nouvelle_feuille) = vide + sinon + droite(nouvelle_feuille) = vide + retourne sous_arbre +``` + +# Il nous manque le code pour le `parent` + +## Pseudo-code pour trouver le parent (5min -> matrix) + +. . . + +``` +arbre parent(arbre, sous_arbre) + si est_non_vide(arbre) + actuel = arbre + parent = actuel + clé = clé(sous_arbre) + faire + si (clé != clé(actuel)) + parent = actuel + si clé < clé(actuel) + actuel = gauche(actuel) + sinon + actuel = droite(actuel) + sinon + retour parent + tant_que (actuel != sous_arbre) + retourne vide +``` + +# Le pseudo-code de la suppression + +\footnotesize + +## Pour un seul enfant (5min -> matrix) + +. . . + +``` +arbre suppression(arbre, clé) + sous_arbre = position(arbre, clé) + si est_vide(gauche(sous_arbre)) ou est_vide(droite(sous_arbre)) + parent = parent(arbre, sous_arbre) + si est_vide(gauche(sous_arbre)) + si droite(parent) == sous_arbre + droite(parent) = droite(sous_arbre) + sinon + gauche(parent) = droite(sous_arbre) + sinon + si droite(parent) == sous_arbreou est_ + droite(parent) = gauche(sous_arbre) + sinon + gauche(parent) = gauche(sous_arbre) + retourne sous_arbre +``` + + +# Le pseudo-code de la suppression + +\footnotesize + +## Pour au moins deux enfants (ensemble) + +``` +arbre suppression(arbre, clé) + sous_arbre = position(arbre, clé) # on revérifie pas que c'est bien la clé + si est_non_vide(gauche(sous_arbre)) et est_non_vide(droite(sous_arbre)) + max_gauche = position(gauche(sous_arbre), clé) + échange(clé(max_gauche), clé(sous_arbre)) + suppression(gauche(sous_arbre), clé) +``` + +# Exercices (poster sur matrix) + +1. Écrire le pseudo-code de l'insertion purement en récursif. + +. . . + +``` +arbre insertion(arbre, clé) + si est_vide(arbre) + retourne nœud(clé) + + si (clé < arbre->clé) + gauche(arbre) = insert(gauche(arbre), clé) + sinon + droite(arbre) = insert(droite(arbre), clé) + retourne arbre +``` + +# Exercices (poster sur matrix) + +2. Écrire le pseudo-code de la recherche purement en récursif. + +. . . + +``` +bool recherche(arbre, clé) + si est_vide(arbre) + retourne faux // pas trouvée + si clé(arbre) == clé + retourne vrai // trouvée + si clé < clé(arbre) + retourne recherche(gauche(arbre), clé) + sinon + retourne recherche(droite(arbre), clé) +``` + +# Exercices (à la maison) + +3. Écrire une fonction qui insère des mots dans un arbre et ensuite affiche + l'arbre. + + [^2]: Copyright cours de mathématiques pendant trop d'années.