1 File de priorité - implantation par Tas

publicité
1
1.
File de priorité - implantation par Tas
Une le de priorité permet de représenter un ensemble d'éléments avec les méthodes suivantes: accès
à l'élément de priorité maximum (accesMax); l'insertion d'un nouvel élément (inserer).
Un élément d'une le de priorité a deux accesseurs pour lire sa priorité
accesValeur.
accesPriorite
et sa valeur
De plus un mutateur permet de changer la valeur de priorité d'un élément (changePriority).
Spécier la classe FilePriorite.
Dans toute la suite on implante la le de priorité par un arbre binaire partiellement ordonné: pour
tout n÷ud, sa valeur est supérieure à celle de ses ls.
2.
On implémente ici avec un arbre binaire naïf: un noeud de l'arbre stocke une valeur et les deux
noeuds
filsgauche
et
filsdroit,
1. Dénir les classes
Noeud
et
dont la valeur peut être le
noeud-vide.
ArbreBinaire.
2. Dénir la classe le de pirorité construite à partir d'ArbreBinaire.
Donner les prototypes des
méthodes:
inserer, accesMax, muterValPriorite
mais pas de les implementer.
3. Donner l'implantation de accesMaxPriorite.
4. Ecrire un itérateur qui parcourt tout l'arbre en eectuant un traitement sur chaque valeur qui ne
change pas l'arbre.
5. Que pensez-vous du coût (en moyenne) si vous deviez implanter insertion, suppression?
3.
1 et partiellement
On considère une représentation optimisée, appelée "tas" (heap) d'un arbre parfait
ordonné. On se limite ici à un arbre binaire.
Supposons que l'arbre contient
1 ≤ i ≤ n, T [i]
n
éléments: il est stocké dans un tableau
•
si
•
T[1] stocke la racine;
•
Si ils existent, les ls gauche et droit de
Pour
•
si
•
si
2 ≤ i ≤ n,
i > n/2, T [i]
T
indicé de
T [1]
à
T [n]:
stocke un élément de l'arbre;
le père de
T [i]
est
T [i]
T [i/2].
sont stockés respectivement aux indices
2i
et
2i + 1.
est une feuille;
n
2 est un entier, alors T [n/2] représente l'unique noeud unaire de l'arbre. Sinon tout noeud est
soit un noeud interne binaire, soit une feuille.
Ajout dans un arbre parfait partiellement ordonné :
•
On ajoute l'élément sur la première feuille libre, de manière à ce que l'arbre reste parfait.
•
On rétablit la propriété partiellement ordonné en échangeant l'élément avec son père si la valeur
de ce dernier est inférieure, et en propageant ce type d'échange aussi loin qu'il le faut vers la racine
(voir Figure 1).
Suppression du maximum :
•
On remplace l'élément racine, qui contient le maximum, par le dernier élément (dernière feuille)
de manière à ce que l'arbre reste parfait.
•
On rétablit la propriété partiellement ordonné en eectuant si nécessaire des échanges de pères
avec le plus grand de leurs ls, en partant de la racine de l'arbre et en descendant (voir gure 2).
1
18
8
11
7
6
9
5
4
8
11
3
15
2
18
18
3
6
2
5
4
7
15
9
8
15
3
6
2
5
4
7
11
9
Figure 1: Ajout de 15 dans l'arbre de la gure précédente.
6
8
15
7
11
9
5
4
15
15
8
6
3
2
4
7
11
9
5
8
11
3
2
4
7
6
9
3
2
5
Figure 2: Suppression du maximum dans l'arbre précédent.
1. Quels sont les coûts de l'insertion et de la suppression (en nombre de comparaisons ou d'échanges):
en pire cas ? et en moyenne ?
2. Implanter une le de priorité avec un tas, en utilisant TableauInni pour stocker le tableau. On
pourra implanter deux méthodes privées:
•
taslagmite( int i ): this est un tas sauf en
T [i]
dont la valeur est supérieure à celle de son père
T [i/2].
•
taslactite( int i ): this est un tas sauf en
à
T [i] dont un des ls au moins a une valeur supérieure
T [i].
3. Réorganisation d'un tableau en tas: écrire un constructeur prenant en entrée un tableau et construisant un tas:
(a) Version naive:
Indication:
temps
en insérant les éléments un à un dans un nouveau tas:
Remarquer que le premier élément est inséré en
O(1)
quel est le coût ?
mais les
n/2
feuilles en
O(log n).
(b) Version ecace: donner un algorithme de cout
O(n).
Indications: on pourra chercher à inverser le fonctionnement de la version naive, en inserant
les n/2 feuilles en O(1) et l'unique racine en O(1). Etant donné deux tas A et B contenant
2i − 1 élements et une nouvelle valeur, remmarquer que l'on peut construire un tas contenant
i+1
les 2
−1 éléments (A, B et v ) en eectuant O(i) comparaisons; en dédui expliquer comment
naive: en insérant les éléments un à un dans un nouveau tas;
(c) le tri par tas fonctionne comme suit: étant donné un tableau, on le réorganise en tas; puis on
extrait le maximum pour l'écrire sur la sortie. Quel est le coût de cet algorithme (en nombre
de comparaisons)?
Quel est son avantage et son inconvénient par rapport au quicksort ?
1 Un
arbre est
parfait ssi:
(i) les feuilles sont sur deux niveaux seulement; (ii) l'avant dernier niveau est complet; (iii) les
feuilles du dernier niveau sont groupées le plus à gauche possible.
2
2
Tableau trié avec insertion et recherche en coût logarithmique
Une classe comparable contient des objets dont on peut comparer les valeurs (ordre total). La méthode
int compareTo(E b) retourne un entier négatif (resp.
resp. supérieur) à b.
nul, resp. positif ) si this est inférieur (resp. égal,
Un dictionnaire est un container d'objets comparables qui supporte les méthodes:
•
void insertion(E val): insère val dans le dictionnaire;
•
E recherche(E val) qui lève une exception si l'objet n'est pas présent dans le dictionnaire, et sinon
retourne une référence sur un objet de valeur val dans le dictionnaire.
NB On ne considère pas la suppression dans un premier temps.
La recherche dichotomique dans un tableau trié de
n
éléments prend un temps logarithmique, mais
l'insertion un temps linéaire. Pour diminuer le coût de l'insertion, on représente le tableau de la façon
suivante.
Pk−1
k = dlog2 (n + 1)e et n = i=0 ni 2i la décompostion de n en base 2 (i.e. ni vaut 0 ou 1).
i
Les n éléments triés sont stockés dans k tableaux A0 , . . . , Ak−1 . Le tableau Ai est de longueur 2 si
ni = 1; il est vide si ni = 0. Chaque tableau Ai est trié; mais il n'y a pas d'ordre entre deux tableaux.
Soit
Exemple : pour 13 caractères dans l'ordre alphabétique, on a 4 tableaux :
A0 = [e] A1 = [, ] A2 = [a, c, i, l] A3 = [b, d, f, g, h, j, k]
Pour l'implantation, les
T
de taille
2k − 1,
k
sous-tabbelaux
donc inférieure à
2n.
Ai
sont stockés consécutivement dans un tableau dynamique
On demande d'écrire les algorithmes en Java.
1. Donner un algorithme de recherche; analyser le coût en pire cas.
2. Analyser le coût en moyenne lors de la recherche d'un élément qui est dans le tableau.
3. Donner un algorithme d'insertion. Analyser le coût en pire cas et le coût amorti.
4. Etudier la destruction d'un élément.
5. On propose l'algorithme de tri suivant, inspiré d'un tri par insertion; il utilise cette structure de
données
T
qui stocke de manière contigüe
T = [A0 , A1 , . . .],
chaque sous-tableau
Ai
de taille
2i
étant toujours soit vide soit trié :
T = vide ; // initialisation de T
// Insertion des éléments dans la structure
for (int i=0; i < n; i++) T.insert ( x(i)
) ;
// Fin de l'insertion; on finalise en fusionnant les tableaux 2 à 2 pour terminer.
int
k = dlog2 (n + 1)e ;
for (int j=1; j < k ; j++ ) merge( T[0 . . . 2j−1 − 1 ], T[ 2j . . . 2j − 1] ) ;
T.resize( n) ; // pour réajuster la taille de T
return T ;
merge( T[0 . . . 2j−1 − 1 ], T[ 2j . . . 2j − 1] ) est la fusion classique de deux
j−1
j
(non nécessairement pleins) avec m1 ≤ 2
et m2 ≤ 2 . En résultat, les m1 + m2
sont stockés triés dans le sous tableau T [0 . . . m1 + m2 − 1].
L'opération tableaux
éléments
(a) Quel est le coût de la phase d'insertion ?
(b) Quel est le coût de la phase de fusion ?
O(n log n). La recheche
O(log n), c'est le décalage des éléements dans un tableau
2
de taille n qui entraînent un coût O(n ) pour le tri par insertion naïf. La représentation cidessus permet de conserver un coût total O(n log n) grâce à un tableau de taille raisonnable,
inférieure à 2n. Cependant, le tableau T ne restant pas trié tout au long de l'algorithme, il ne
Remarque : Ce tri, assez proche d'un tri par insertion, est donc de coût
dichotmique dans un tableau trié étant en
peut pas être considéré comme un vrai tri par insertion. D'autres représentations d'un tableau, qui
utilisent comme ci-dessus des emplacememnts vides pour amortir le coût des décalages nécessaires,
permettent d'implanter un tri par insertion dans un tableau de taille
3
O(n) en coût amorti O(n log n).
Correction:
1. Recherche: on recherche successivement dans les
1, . . . , 0,
log2 n
tableaux
Ai
pour
i = k−
en commençant par le tableau de plus grande taille;
dans chaque tableau
Ai
avec
ni 6= 0, on eectue une recherche dichitomique de coût log2 kAi k =
i.
Le coût en pire cas est majoré par
Plog n
i=0
i = O(log2 n).
2. Analyse en moyenne de la recherche d'un élément dans le tableau. On se place dans le pire
k
cas où n = 2 − 1, i.e. on a ni = 1 pour tout i.
On suppose que l'élément recherché est choisi uniformément parmi les n éléments; la probai
Ai est alors 2n ≤ 2i−k . D'où le coût cR (n = 2k − 1)
en moyenne:
bilité que l'élément soit dans le tableau
1
+ .cR (2k−1 − 1)
2
1
1
1
≤ log2 2k−1 + log2 2k−2 + . . . + i log2 2k−i−1 + . . . + k−1
2
2
2
≤ 2 log2 n.
cR (n) ≤
CoûtRechDicho (Ak−1 )
(1)
(2)
(3)
Θ(log2 n) si l'on fait la recherche dans l'ordre : d'abord dans
etc jusqu'à trouver l'élément.
Le coût en moyenne est donc
Ak−1 ,
puis dans
Ak−2
ni = 1 pour tout i = 0 . . . j − 1. L'insertion consiste
Aj ; puis à réorganniser la structure de donnée en
supprimant les tableaux A0 , ..., Aj−1 pour les déplacer dans Aj .
Pour cela, on procède comme suit: on fusionne Aj successivement avec A0 puis A1 , etc jusqu'à
Aj−1 . Le coût en comparaison de la fusion de deux tableaux de taille 2i est majoré par 2i+1 ;
Pj−1 i+1
le coût de ces fusions successives est donc manjoré par
≤ 2j − 1.
i=0 2
On incréménte alors n de 1: ainsi on a ni = 0 pour 0 ≤ i < j et nj = 1; on détruit donc
virtuellement les tableaux Ai devenus inutiles.
Le pire cas est lorsque j = k − 1 : dans ce cas, la réorganisation est de coût O(n).
Mais alors, on a ensuite n0 = 0 et la prochaine insertion se fera avec 0 comparaisons, en coût
O(1). Plus précisément, le coût pour n insertions consécutives est le suivant:
j tel que nj = 0 et
alors à insérer le nouvel élément dans
3. Pour l'insertion: soit
• n2 fois, on a j = 0 et l'insertion se fait avec 20 − 1 = 0 comparaisons;
• 2n2 fois, on a j = 1 et l'insertion se fait avec 21 − 1 = 1 comparaisons;
• 2n3 fois, on a j = 2 et l'insertion se fait avec 22 − 1 = 3 comparaisons;
• 2ni fois, on a j = i et l'insertion se fait avec 2i − 1 comparaisons;
• ...
• 1 fois seulement on a j = log2 n et l'insertion se fait avec n − 1 comparaisons;
n
On en déduit le cout amorti pour
insertions (consécutives):
Plog2 n
c(n) ≤
j=0
n
j
2j+1 .2
n
= O(log2 n).
Le coût amorti de l'insertion est alors logarithmique, comme le coût moyen de la recherche.
4. Pour analyser la destruction, supposons que l'élément détruit est dans le tableau
plus
j≤d
l'indice tel que
nj = 1
et
ni = 0
pour
Ad .
Soit de
0≤i<j
Pour détruire l'élément, on procède comme suit.
•
Étape 1 : Réorganisation de Aj : on enlève le dernier élément y dans Aj et on déplace les
2j − 1 éléments restants de Aj dans les tableaux A0 , . . . , Aj − 1. Le coût est O(2j ).
•
Étape 2 : Insertion dans
Ad : on remplace l'élément x par y et on rétablit l'ordre dans le
en permutant l'élément y soit à gauche, soit à droite jusqu'à ce qu'il soit en
d
place. Le coût est O(2 ).
tableau
•
Ad
Finalement, on décrémente
n
de 1 : ainsi
Le coût de la destruction est donc linéaire
ni = 1
O(n)
pour
et
nj = 0.
en pire cas.
Analyse amortie : le coût de la réorganisation du tableau
4
0≤i<j
Aj
peut être amorti : il est nul lorsque
j = 0,
Ad trié est de coût linéaire
et l'insertion est de coût moyen n/4. Donc, le
ie une fois sur deux. Cependant, l'insertion dans le tableau
d=k
O(n).
et n'est pas amortie : une fois sur deux,
coût amorti de la destruction est aussi
En conclusion, cette structure est bien adaptée à la recherche et l'insertion (coût amorti
O(log n));
mais elle n'est pas adaptée à la destruction.
Les structures de dictionnaire (chapitre ??) permettent de garantir un temps d'insertion, de
recherche et de suppression de coût
Θ(log n).
i étant de coût amorti O(log i), le coût de la phase d'insertion
= [t log t − t]n1 ≡ n log n. Donc O(n log n).
j−1
(b) Quel est le coût de la phase de fusion ? Le coût de la fusion merge( T[0 . . . 2
−1
j
j
j
j
], T[ 2 . . . 2 − 1] ) est 2 comparaisons, donc Θ(2 ). La phase de fusion est donc de
Plog2 n j
coût total
j=1 2 ≡ 2n, donc Θ(n).
Le coût de cette variante du tri par insertion est donc Θ(n log n).
5. (a) L'insertion de l'élément
Rn
Pn
est
i=1 log i ≡ 1 log t
5
Téléchargement