ARBRES exercices Ex 1 Dans cet exercice, on considère des graphes dont l’ensemble des sommets est S = {0, 1, . . . , n−1}. On suppose de plus que n ≥ 3. 1. Soit G = (S, A) un arbre. Montrer que G possède un sommet de degré 1 et que le graphe obtenu en supprimant un tel sommet et l’arête associée est encore un arbre. 2. A un arbre G = (S, A) on peut alors associer un n − 2 uplet d’éléments de [|0, n − 1|] par l’algorithme suivant On part d’un uplet L vide Pour k de 1 à n-2, faire Noter i le plus petit sommet de degré 1 Ajouter au uplet L le voisin de i Supprimer i de G Renvoyer L Donner le 6-uplet associé à l’arbre 4k 6k 0k A A A 2k 5k 7k 1k 3k 3. Justifier l’injectivité de l’application qui à l’arbre associe le n − 2-uplet. 4. Justifier la bijectivité en donnant un algorithme permettant de reconstruire l’arbre à partir du n − 2-uplet (on ne demande pas de preuve). Appliquer au uplet (1, 1, 0, 2, 2, 0, 3, 3). 5. Implémenter les deux algorithmes précédents en Caml. On représentera les graphes par une matrice d’adjacence et les uplets par des tableaux. 6. Déterminer le nombre des arbres dont l’ensemble des sommets est S. Ex 2 Soit G = (S, A) un graphe connexe. Pour construire un arbre couvrant ce graphe, on peut partir d’un graphe sans arête. On choisit arbitrairement un sommet. On ajoute alors une arête issue de ce sommet (il en existe une). On a alors deux sommets “couverts”. On ajoute alors une arête entre un sommet couvert et un autre non couvert (il en existe une par connexité). On poursuit le processus jusqu’à couvrir tous les sommets. On a alors ajouté n − 1 arêtes et on a un sous-graphe qui est un arbre. On choisit de représenter les graphes en Caml grâce au type suivant : type arete == int*int ;; type graphe = {N : int ; A : arete list} ;; Le champ N nous donne le nombre des sommets. Le champ A donne la liste des arêtes, une arête étant ici un couple (x, y) avec x < y. Ecrire une fonction arbrecouvrant : graphe → graphe mettant en oeuvre l’algorithme précédent. 1 Ex 3 On veut compter le nombre Cn d’arbres différents de sommets 0, . . . , n − 1 (on parle ici d’arbres non orientés ; le calcul a été effectué d’une autre manière en exercice 1). Pour cela, on va transiter par des arbres orientés (et donc aussi enracinés). Pour construire un tel arbre orienté, on doit choisir n − 1 arêtes orientées. On appelle construction un n − 1 uplet d’arêtes orientées donnant un arbre. Notez que deux constructions peuvent donner le même arbre et que tout n − 1 uplet n’est pas une construction (le choix des arêtes pouvant ne pas mener à un arbre orienté). On note Dn le nombre de constructions dans le cas de n sommets (on compte donc le nombre de uplets et pas le nombre d’arbres orientés). 1. Dans un premier compte, on choisit l’un des Cn arbres non orientés. Choisir une racine oriente chaque arête et il reste à choisir l’ordre de ces arêtes dans le uplet. En utilisant ce raisonnement, montrer que Dn = n!Cn . 2. Dans un second temps, on part du graphe vide et on ajoute une arête orientée puis une seconde puis une troisième etc. Initialement, on a n composantes connexes formant une forêt de n arbres réduits à leurs racines. L’ajout de chaque arête orientée fusionne deux composantes. Quel est le nombre de choix possibles pour la k-ième arête (lors de ce choix, on a n − k + 1 composantes connexes formées d’arbres orientés et on doit en fusionner deux en gardant des arbres orientés) ? En déduire la valeur de Dn . 3. Conclure que Cn = nn−2 . Ex 4 Ecrire une fonction arbreDFS : graphe → int → graphe qui à partir d’un graphe non orienté et d’un numéro de sommet renvoie l’arbre orienté de parcours en profondeur à partir du sommet argument. On représente ici les graphes par le tableau des listes d’adjacence. Ex 5 Ecrire une fonction compteF : arbre → int renvoyant le nombre de feuilles d’un arbre. Ex 6 On choisit ici la définition des arbres binaires sans l’arbre vide. Dans un arbre binaire t, on note i(t) la somme des profondeurs des noeuds internes et e(t) celle des profondeurs des feuilles. Démontrer que dans un arbre binaire t à n(t) noeuds internes on a e(t) = i(t) + 2n(t) Ex 7 Si a est un arbre non vide, on appelle déséquilibre de a la différence des hauteurs du fils gauche et du fils droit de a (d(a) = h(g) − h(d)). On définit récursivement les arbres AVL par - l’arbre vide est un arbre AVL - a = Noeud(g, c, d) est un arbre AVL si g et d sont des arbres AVL et si d(a) ∈ {−1, 0, +1}. 1. Montrer qu’un arbre AVL est équilibré. 2. Ecrire une fonction testant si un arbre possède la propriété AVL. On ne devra parcourir qu’une unique fois chaque noeud de l’arbre. Ex 8 On appelle arbre 0-1 un arbre binaire dont les étiquettes valent 0 ou 1 et qui vérifie les conditions suivantes : - la racine (si l’arbre est non vide) est d’étiquette 0 ; - le parent d’un noeud d’étiquette 1 est d’étiquette 0 ; - pour chaque noeud, tout les chemins de ce noeud à une feuille contiennent le même nombre de noeuds d’étiquette 0. 1. Montrer que le squelette d’arbre ci-dessous peut être étiqueté afin de devenir un arbre 0-1. 2 HH HH HH H @ @ @ @ @ @ A A A A A A A A A A A A 2. Donner un exemple de squelette d’arbre ne pouvant être étiqueté en un arbre 0-1. 3. Si a est un arbre 0-1, on note z(a) le nombre de noeuds d’étiquette 0 sur le chemin de la racine à une feuille (ce nombre ne dépend pas de la feuille). Montrer que z(a) ≤ h(a) + 1 ≤ 2z(a) et |a| ≥ 2z(a) − 1 En déduire que les arbres 0-1 sont équilibrés. 4. Ecrire une fonction testant en temps linéaire en fonction du nombre de noeuds si un arbre d’étiquettes dans {0, 1} est un arbre 0-1. Ex 9 Ecrire une fonction prenant en argument un arbre binaire et renvoyant la liste des étiquettes de l’arbre dans l’ordre préfixe (par exemple). On essayera d’écrire une fonction n’utilisant que l’opérateur :: de consage et pas celui de concaténation @. Ex 10 Implémenter l’algorithme d’affichage des noeuds d’un arbre dans un parcours en largeur (on supposera les étiquettes entières). On représentera une file à l’aide de deux listes (selon le cours de première année) ; on écrira donc une fonction auxiliaire récursive qui prendra en argument ces deux listes. Ex 11 Comment, à l’aide d’un parcours en profondeur, vérifier la structure d’ABR ? Ex 12 Ecrire une fonction efficace de création d’un abr à partir d’un tableau trié. Justifier les choix faits (en quoi est-ce efficace ?) et donner la complexité de la fonction. Ex 13 On peut utiliser une technique d’insertion dans un abr autre que celle d’insertion aux feuilles vue en cours. La méthode consiste à insérer l’étiquette x à la racine du nouvel arbre. Pour cela, on découpe l’arbre initial en deux arbres dont les étiquettes sont respectivement plus petites et plus grandes que x et à rassembler ces arbres sous une nouvelle racine. a. Ecrire une fonction Caml mettant en oeuvre cette méthode (on convient de ne pas ajouter x s’il est déjà présent). b. Quelle est la complexité de la fonction précédente. 3 Ex 14 Proposer une structure permettant de gérer des tas dont les éléments ne sont plus forcément des entiers. Ex 15 1. Ecrire une fonction arbre to vect prenant en argument un arbre parfait (pas forcément un APO) et renvoyant un tableau représentant cet arbre. 2. Ecrire la fonction réciproque vect to arbre. Ex 16 Ecrire une fonction d’insertion d’un élément dans un tas (insere : int → tas → unit). Ex 17 Ecrire une fonction supprime : tas → int qui prend en argument un tas non vide et en supprime l’élément à la racine et renvoie l’élément supprimé. Ex 18 Ecrire une fonction tri : int list → int list qui prend en argument une liste et en renvoie une version triée. On utilisera un tas comme file de priorité et la stratégie de tri décrite dans le cours. On commencera sans doute par une fonction créant un tas à partir d’une liste. Préciser la complexité de cette fonction. 4