Structures de données avancées Master d’Informatique — UPMC STL - M1. UE Algorithmique avancée Semaines 1 à 3 1 Année 2006 — 2007 Ordres de grandeur 1. Rappeler la définition de O, Θ, Ω et o. 2. Montrer que : • si f ∈ O(g) alors O(f ) ⊆ O(g) et O(max(f, g)) = O(g) • O(f + g) = O(max(f, g)). 3. Ranger dans l’ordre : O(n log n), O(2 n ), O(log n), O(1), O(n2 ), O(n3 ) et O(n). Pn croissant k k+1 4. Montrer que : ). p=1 p ∈ Θ(n 2 Coût amorti Exercice 1 On considère les deux opérations fondamentales sur les piles : Empiler(S,x) et Depiler(S), auxquelles on ajoute l’opération MultiDepiler(S,k). L’opération MultiDepiler(S,k) retire les k premiers objets du sommet de la pile si la pile contient au moins k objets et vide la pile sinon. 1. Calculer le coût amorti d’une opération en utilisant la méthode par agrégat. 2. Même question en utilisant la méthode du potentiel. Exercice 2 Aux trois opérations : Empiler(S,x), Depiler(S) et MultiDepiler(S,k), définies sur les piles, on ajoute l’opération MultiEmpiler(S,x1,x2,...,xk), qui permet d’empiler les objets x1, x2, ..., xk sur la pile. 1. En utilisant la méthode par agrégat, calculer le coût amorti d’une opération dans chacun des cas suivants : • dans l’opération MultiEmpiler(S,x1,x2,...,xk), le nombre d’objets que l’on peut empiler est borné par une constante c • dans l’opération MultiEmpiler(S,x1,x2,...,xk), le nombre d’objets que l’on peut empiler est inférieur ou égal au nombre d’objets de la pile S. 2. Même question en utilisant la méthode du potentiel. Exercice 3 Une séquence de n opérations est effectuée sur une structure de données. La i-ème opération coûte i si i est une puissance de 2 et 1 sinon. Le tableau a initialement une taille 1, on y met un élément et on fait grossir de 1 case. Si on est en case i = 2k , on alloue i cases supplémentaires et on met 0 dedans. Ici le potentiel est le nombre de cases occupées dans la seconde moitié du tableau. 1 1. Utiliser la méthode par agrégat pour calculer le coût amorti par opération. 2. Même question en utilisant la méthode du potentiel. Chercher eventuellement un exemple de cout moyen facile a calculer puis faire un cout amorti qu’on espere different. Exercice 4 Soit k un entier positif. On considère les entiers naturels inférieur à 2 k . Étant donné un tel entier x, son écriture en binaire peut être stockée dans un tableau A[0..k − 1] ne contenant que des 0 et des 1 (le bit de poids le plus faible est stocké dans A[0]). On définit l’opération Incrementer(A) comme suit : Incrementer(A) i <- 0 ; tant que i < k et A[i] = 1 faire A[i] <- 0 i <- i+1 fintantque si i < k alors A[i] <- 1 finsi fin 1. Quel est l’effet de Incrementer ? 2. Le coût de l’opération Incrementer est le nombre de bits qui changent. On considère une suite de n incrémentations à partir de 0 (on suppose n < 2 k ). Calculer le coût amorti de l’opération Incrementer : (a) en utilisant la méthode par agrégat (b) en utilisant la méthode du potentiel. 3 Files de Fibonacci Exercice 5 En désignant par Uk la famille des arbres binomiaux non ordonnés dont la racine est de degré k (i.e. a k fils), on donne la définition inductive suivante des arbres binomiaux non ordonnés : U0 = {•} Uk = {(•, Ui1 , . . . , Uik ) | (i1 , . . . , ik ) est une permutation de (0, . . . , k − 1) et U ij ∈ Uij } Uk = • c ## c c # c # c # U i1 ... U i2 U ik Montrer que, pour tout arbre Uk de Uk : 1. Uk a 2k nœuds 2. La hauteur de Uk est k 3. Il y a Cki nœuds à la profondeur i (i = 0, . . . , k) 4. La racine de Uk est de degré k, supérieur au degré de n’importe quel autre nœud. 2 Exercice 6 Une file (ou tas) de Fibonacci est une collection d’arbres binomiaux non ordonnés dont les étiquettes croissent des racines vers les feuilles. Une file de Fibonacci est dite consolidée si toutes les racines ont des degrés différents. 1. Montrer que pour tout entier n, il existe une file de Fibonacci consolidée de taille n. 2. Quelles sont les tailles des arbres binomiaux non ordonnés qui forment une file de Fibonacci de taille n ? les degrés des différentes racines ? le degré maximum d’un nœud ? Exercice 7 Structure de données : Etant donnés une file de Fibonacci H et un nœud x quelconque de H : – min (H) désigne la racine de l’arbre qui contient la plus petite clé (si H est vide alors min (H) = nil) – pere (x) est le père de x (si x est une racine alors pere (x) = nil) – fils (x) est l’un des fils de x (si x est une feuille alors fils (x) = nil) – pred (x) est le frère gauche de x et succ (x) est son frère droit (si x est une racine alors pred (x) et succ (x) sont des racines, si x est fils unique alors pred (x) = succ (x) = x) – degre (x) est le nombre de fils de x. Remarque : les racines sont reliées par une liste doublement chaı̂née ; les enfants d’un même nœud sont eux aussi reliés par une liste doublement chaı̂née. 1. On dispose de la procédure CreerFibVide de création d’une file de Fibonacci vide. Écrire les procédures – InserererFib d’insertion d’un nœud x dans une file de Fibonacci (on insérera x dans la liste des racines sans consolider la file) – ConcatenerFib d’union de deux files de Fibonacci (sans consolidation). 2. Écrire une procédure ConsoliderFib de consolidation d’une file de Fibonacci. 3. Écrire une procédure d’extraction ExtraireMinFib du minimum dans une file de Fibonacci (avec consolidation). Exercice 8 En utilisant la méthode du potentiel, calculer le coût amorti de l’extraction du minimum dans une file de Fibonacci. Exercice 9 On rappelle que les nombres de Fibonacci sont définis par : F 0 = 0, F1 = 1 et Fk+2 = Fk+1 + Fk pour k ≥ 0. 1. Calculer Fk . 2. Montrer que 1 + k X Fi = Fk+2 . i=0 √ 5 1 + (on a Φ ∼ 1, 61803). 3. Montrer que Fk+2 ≥ Φk avec Φ = 2 Exercice 10 Pour implémenter les opérations de diminution d’une clé et de suppression d’une clé, on autorise tout nœud x d’une file de Fibonacci à avoir un fils de moins qu’il ne devrait, avec un marquage de cette situation. Chaque nœud x contient un booléen marque (x) qui a la valeur vrai s’il manque un fils à x et f aux sinon. 1. Écrire une procédure DiminuerCleFib de diminution d’une clé dans une file de Fibonacci. 2. Écrire une procédure SupprimerCleFib de suppression d’une clé dans une file de Fibonacci. 3. On note D(n) le degré maximum d’un nœud dans une file de Fibonacci de taille n. Montrer que D(n) ≤ log Φ n. 4. En utilisant la méthode du potentiel, calculer le coût amorti de la suppression d’une clé dans une file de Fibonacci. 3 4 Arbres bicolores et arbres 2-3-4 4.1 4.1.1 Arbres bicolores Définition Un arbre bicolore de recherche est un arbre binaire de recherche complété dans lequel tout sommet possède une couleur (blanc ou rouge) et tout nœud interne possède une clé et qui vérifie : • les feuilles sont blanches (et ne possèdent pas de clé) • le père d’un sommet rouge est blanc • les chemins issus d’un même sommet et se terminant en une feuille ont le même nombre de sommets blancs. 4.1.2 Primitives Pour manipuler les arbres bicolores, on dispose des primitives habituelles sur les arbres binaires : ArbreVide : − > arbre binaire ArbreVide() renvoie un arbre vide ArbreBinaire : élément × arbre binaire × arbre binaire − > arbre binaire ArbreBinaire(x,G,D) renvoie l’arbre binaire dont la racine a pour contenu l’élément x et dont les sous-arbres gauche et droit sont respectivement G et D EstArbreVide : arbre binaire − > booléen EstArbreVide(T) renvoie VRAI ssi T est un arbre binaire vide Racine : arbre binaire − > élément Racine(T) renvoie le contenu de la racine de l’arbre binaire T SousArbreGauche : arbre binaire − > arbre binaire SousArbreGauche(T) renvoie une copie du sous-arbre gauche de l’arbre binaire T SousArbreDroit : arbre binaire − > arbre binaire SousArbreDroit(T) renvoie une copie du sous-arbre droit de l’arbre binaire T et de primitives spécifiques aux arbres bicolores : FeuilleBlanche : − > arbre binaire FeuilleBlanche() renvoie l’arbre binaire réduit à une feuille de couleur c blanche ArbreBinaire : clé × couleur × arbre binaire × arbre binaire − > arbre binaire ArbreBinaire(a,c,G,D) renvoie l’arbre binaire dont la racine a pour clé a et pour couleur c et dont les sous-arbres gauche et droit sont respectivement G et D EstFeuille : arbre binaire − > booléen EstFeuille(T) renvoie VRAI ssi T est un arbre binaire réduit à une feuille CleRacine : arbre binaire − > clé CleRacine(T) renvoie la clé de la racine de T CouleurRacine : arbre binaire − > couleur CouleurRacine(T) renvoie la couleur de la racine de T ModifierCouleur : arbre binaire × couleur − > Rien ModifierCouleur(T,c) remplace par c la couleur de la racine de l’arbre binaire T 4.2 Arbres 2-3-4 1. Rappeler les règles d’éclatement d’un 4-nœud dans un arbre 2-3-4, lorsque les éclatements se font à la descente. 2. Construire par adjonctions successives un arbre 2-3-4 contenant les clés 8, 3, 2, 4, 1, 15, 10, 9, 11, 7, 6, 13, 12, 5, 14, 16, 17. On nommera cet arbre A-ex1. 4 4.3 4.3.1 Arbres 2-3-4 et arbres bicolores Transformation d’un arbre 2-3-4 en arbre bicolore Principe Pour obtenir un arbre bicolore à partir d’un arbre 2-3-4, on procède de la façon suivante : • l’arbre vide est transformé en une feuille blanche (sans clé) • chaque 2-nœud prend la couleur blanche et ses deux sous-arbres sont transformés en arbres bicolores • un 3-nœud, qui compte deux clés a < b, se scinde en deux nœuds de l’arbre bicolore ; ces deux nœuds contiennent respectivement les clés a et b. Il y a deux possibilités : ou bien b est à la racine du sous-arbre droit de a ou bien a est à la racine du sous-arbre gauche de b. Le fils prend la couleur rouge et le père prend la couleur blanche. Les trois sous-arbres du 3-nœud sont eux-mêmes transformés en arbres bicolores et deviennent les sous-arbres des nœuds contenant les clés a et b • un 4-nœud, qui compte trois clés a < b < c, se scinde en trois nœuds de l’arbre bicolore ; ces trois nœuds contiennent respectivement les clés a, b et c. La clé a est à la racine du sous-arbre gauche de b et la clé c est à la racine du sous-arbre droit de b. Les fils prennent la couleur rouge et le père prend la couleur blanche. Les quatre sous-arbres du 4-nœud sont eux-mêmes transformés en arbres bicolores et deviennent les sous-arbres des nœuds contenant les clés a et c. 1. Illustrer le principe énoncé ci-dessus (au moyen de petits dessins). 2. Montrer que l’arbre ainsi obtenu est un arbre bicolore et encadrer la hauteur de cet arbre. 3. Construire un arbre bicolore, que l’on nommera B-ex1, transformé de l’arbre 2-3-4 A-ex1. 4. Écrire l’algorithme de transformation d’un arbre 2-3-4 en arbre bicolore. 4.3.2 Transformation d’un arbre bicolore en arbre 2-3-4 1. Donner le principe de la transformation d’un arbre bicolore en arbre 2-3-4. Illustrer ce principe au moyen d’un exemple. 2. Écrire l’algorithme de transformation d’un arbre bicolore en arbre 2-3-4. 4.4 4.4.1 Insertion dans un arbre bicolore Rotations dans un arbre binaire On définit les rotations simples RotationGauche et RotationDroite de spécifications : RotationGauche : arbre binaire − > arbre binaire RotationGauche(T) renvoie l’arbre obtenu en faisant basculer T vers la gauche RotationDroite : arbre binaire − > arbre binaire RotationDroite(T) renvoie l’arbre obtenu en faisant basculer T vers la droite ainsi que les rotations doubles RotationDroiteGauche et RotationGaucheDroite de spécifications : RotationDroiteGauche : arbre binaire − > arbre binaire RotationDroiteGauche(T) renvoie l’arbre obtenu en faisant basculer d’abord le sous-arbre droit de T vers la droite puis l’arbre T ainsi modifié vers la gauche RotationGaucheDroite : arbre binaire − > arbre binaire RotationGaucheDroite(T) renvoie l’arbre obtenu en faisant basculer d’abord le sous-arbre gauche de T vers la gauche puis l’arbre T ainsi modifié vers la droite 1. Écrire la définition de l’une des deux rotations simples et la définition de l’une des deux rotations doubles. 5 4.4.2 Insertion dans un arbre bicolore Principe L’insertion dans un arbre bicolore suit le principe de l’insertion dans un arbre 2-3-4. Nous travaillerons ici sur l’insertion avec éclatements à la descente. 1. Transposer dans les arbres bicolores les différents cas d’insertion d’une clé dans une feuille d’un arbre 2-3-4. 2. Transposer dans les arbres bicolores les différents cas d’éclatement d’un 4-nœud. 3. Réaliser l’insertion des clés 13.1, 13.3, 12.5, 12.8 dans l’arbre bicolore B-ex1. 4. Écrire l’algorithme d’insertion d’une clé dans un arbre bicolore. 5. Ci-dessous figure un algorithme d’insertion extrait du livre “Introduction à l’algorithmique” de Cormen, Leiserson, Rivest et Stein. Est-ce le même que le nôtre ? Algorithmes d’insertion dans un ABR et dans un arbre bicolore ARBRE-INSERER(T,z) y <- NIL x <- racine[T] tant que x <> NIL faire y <- x si cle[z] < cle[x] alors x <- gauche[x] sinon x <- droit[x] p[z] <- y si y = NIL alors racine[T] <- z sinon si cle[z] < cle[y] alors gauche[y] <- z sinon droit[y] <- z ROTATION-GAUCHE(T,x) y <- droit[x] droit[x] <- gauche[y] si gauche[y] <> NIL alors p[gauche[y]] <- x p[y] <- p[x] si p[x] = NIL alors racine[T] <- y sinon si x = gauche[p[x]] alors gauche[p[x]] <- y sinon droit[p[x]] <- y gauche[y] <- x p[x] <- y Le code de ROTATION-DROITE est similaire au code de ROTATION-GAUCHE. RN-INSERER(T,x) ARBRE-INSERER(T,x) couleur[x] <- ROUGE tant que x <> racine[T] et couleur[p[x]] = ROUGE faire si p[x] = gauche[p[p[x]]] alors y <- droit[p[p[x]]] si couleur[y] = ROUGE alors couleur[p[x]] <- NOIR couleur[y] <- NOIR couleur[p[p[x]]] <- ROUGE x <- [p[x]] sinon si x = droit[p[x]] alors x <- p[x] ROTATION-GAUCHE(T,x) couleur[p[x]] <- NOIR couleur[p[p[x]]] <- ROUGE ROTATION-DROITE(T,p[p[x]]) sinon (comme la clause alors en echangeant droit et gauche) couleur[racine[T]] <- NOIR 6 5 Hachage dynamique 5.1 Quelques exemples Dans cette section, les éléments sont les lettres de l’alphabet : a, b, . . . y, z et leurs valeurs de hachage sont h(a) = 00000, h(b) = 00001, . . . , h(y) = 11000, h(z) = 11001. 1. Réaliser le hachage dynamique des clés t, m, y, u, n, r, p, x, e, s, i, b, dans cet ordre, avec pages de taille 4. Que se passe-t-il si on modifie l’ordre d’insertion des clés ? 2. Réaliser le hachage dynamique des clés a, b, c, d, e, f, g, h, i, j, k, l, dans cet ordre, avec pages de taille 4, puis avec pages de taille 7. 5.2 Recherche et insertion 5.3 Primitives Pour travailler sur le hachage dynamique, on utilisera les primitives suivantes (vous en compléterez la spécification) : IndexArbre : index × index − > index IndexFeuille : page − > index PageVide : − > page IndexGauche : index − > index IndexDroit : index − > index PageDeFeuille : index − > page Element : page × entier − > élément EstFeuille : index − > booléen EstPagePleine : page − > booléen EstDansPage : élément × page − > booléen InsertionDansPage : page × élément − > page On suppose que l’on dispose aussi d’une fonction : BitHachage : élément × entier − > bit BitHachage(x,k) renvoie le k-ième bit de la valeur de hachage de x 5.3.1 Recherche Écrire un algorithme de recherche dans un index, avec pages de taille TaillePage. 5.3.2 Insertion Écrire un algorithme d’insertion dans un index, avec pages de taille TaillePage. 7