|
@@ -0,0 +1,367 @@
|
|
1
|
+%***************************
|
|
2
|
+% Gestion d'un AVL en Prolog
|
|
3
|
+%***************************
|
|
4
|
+
|
|
5
|
+%***************************
|
|
6
|
+% INSA TOULOUSE - P.ESQUIROL
|
|
7
|
+% mars 2017
|
|
8
|
+%***************************
|
|
9
|
+
|
|
10
|
+%*************************
|
|
11
|
+% unit tests : OK
|
|
12
|
+% integration aetoile : OK
|
|
13
|
+%*************************
|
|
14
|
+
|
|
15
|
+% Les AVL sont des arbres BINAIRES DE RECHERCHE H-EQUILIBRES :
|
|
16
|
+% La hauteur de l'avl A est définie par :
|
|
17
|
+% -1, si A est vide (A=nil)
|
|
18
|
+% 1 + max( hauteur(ss_arbre_gauche(A)), hauteur(ss_arbre_droitee(A)) ) sinon
|
|
19
|
+
|
|
20
|
+% Tout noeud de l'arbre est soit :
|
|
21
|
+% - une feuille
|
|
22
|
+% - un noeud interne tel que la différence de hauteur entre le sous-arbre droit
|
|
23
|
+% et le sous-arbre gauche appartient à [-1,0,+1]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+%***********************************************
|
|
27
|
+% PREDICATS EXPORTES ET COMPLEXITE ALGORITHMIQUE
|
|
28
|
+%***********************************************
|
|
29
|
+% soit N = nombre de noeuds de l'arbre % UTILITE POUR A*
|
|
30
|
+% % ----------------
|
|
31
|
+% empty(?Avl) O(1) %<<< initialisation de P et Q
|
|
32
|
+% height(+Avl, ?Height) O(1)
|
|
33
|
+% put_flat(+Avl) O(N)
|
|
34
|
+% put_90(+Avl) O(N)
|
|
35
|
+% belongs(+Elem, +Avl) O(log N) %<<< appartenance d'un noeud à Q
|
|
36
|
+% subtree(+Elem, +Avl, Ss_Avl) O(log N)
|
|
37
|
+%insert(+Elem, +Avant, ?Apres) O(log N) %<<< insertion d'un nouveau noeud dans P ou dans Q
|
|
38
|
+% suppress(+Elem,+Avant,?Apres) O(log N) %<<< mise à jour <=> suppression puis insertion
|
|
39
|
+% suppress_min(?Min,+Avant,?Apres) O(log N) %<<< supression du noeud minimal
|
|
40
|
+% suppress_max(?Max,+Avant,?Apres) O(log N)
|
|
41
|
+
|
|
42
|
+%****************************
|
|
43
|
+% Prédicats internes (prives)
|
|
44
|
+%****************************
|
|
45
|
+
|
|
46
|
+% left_rotate(+Avant, ?Apres) O(1)
|
|
47
|
+% right_rotate(+Avant, ?Apres) O(1)
|
|
48
|
+% left_balance(+Avant, ?Apres) O(1)
|
|
49
|
+% right_balance(+Avant, ?Apres) O(1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+ %------------------------------
|
|
54
|
+ % Constructeur et test AVL vide
|
|
55
|
+ %------------------------------
|
|
56
|
+
|
|
57
|
+empty(nil).
|
|
58
|
+
|
|
59
|
+ %-----------------
|
|
60
|
+ % Hauteur d'un AVL
|
|
61
|
+ %-----------------
|
|
62
|
+ % par convention, un avl vide a une hauteur de -1
|
|
63
|
+ % sinon la hauteur est enregistree au meme niveau que la racine de l'avl
|
|
64
|
+ % elle n'est pas calculee recursivement "from scratch"
|
|
65
|
+ % elle est mise à jour de façon incrémentale, apres chaque insertion ou suppression
|
|
66
|
+ % d'ou sa complexité en O(1) :-)
|
|
67
|
+
|
|
68
|
+height(nil, -1).
|
|
69
|
+height(avl(_G,_R,_D, H), H).
|
|
70
|
+
|
|
71
|
+ %-------------------
|
|
72
|
+ % Affichage d'un AVL
|
|
73
|
+ %-------------------
|
|
74
|
+ % dans l'ordre croissant (lexicographique)
|
|
75
|
+
|
|
76
|
+put_flat(nil).
|
|
77
|
+put_flat(avl(G,R,D,_H)) :-
|
|
78
|
+ put_flat(G),
|
|
79
|
+ nl, write(R),
|
|
80
|
+ put_flat(D).
|
|
81
|
+
|
|
82
|
+ %----------------------------
|
|
83
|
+ % Affichage (couché) d'un AVL
|
|
84
|
+ %----------------------------
|
|
85
|
+
|
|
86
|
+put_90(Avl) :-
|
|
87
|
+ nl, writeln('----------------------------------'),
|
|
88
|
+ put_90(Avl,"").
|
|
89
|
+
|
|
90
|
+put_90(nil,Str) :-
|
|
91
|
+ write(Str), write('.').
|
|
92
|
+put_90(avl(G,R,D,_H),Str) :-
|
|
93
|
+ append_strings(Str, " ", Str2),
|
|
94
|
+ put_90(D,Str2),
|
|
95
|
+ nl, write(Str), write(R),nl,
|
|
96
|
+ put_90(G,Str2).
|
|
97
|
+
|
|
98
|
+ %-----------------------------------------
|
|
99
|
+ % Appartenance d'un element donne a un AVL
|
|
100
|
+ %-----------------------------------------
|
|
101
|
+
|
|
102
|
+belongs(Elem, avl(G,Racine,D,_Hauteur)) :-
|
|
103
|
+ (Elem = Racine ->
|
|
104
|
+ true
|
|
105
|
+ ;
|
|
106
|
+ (Elem @< Racine ->
|
|
107
|
+ belongs(Elem, G)
|
|
108
|
+ ;
|
|
109
|
+ belongs(Elem, D) %Racine @< Elem
|
|
110
|
+ )
|
|
111
|
+ ).
|
|
112
|
+
|
|
113
|
+ %------------------------------------------------------------
|
|
114
|
+ % Recherche du sous-arbre qui a comme racine un element donne
|
|
115
|
+ %------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+subtree(Elem, avl(G,Racine,D,H), A) :-
|
|
118
|
+ (Elem = Racine ->
|
|
119
|
+ A = avl(G,Racine,D,H)
|
|
120
|
+ ;
|
|
121
|
+ (Elem @< Racine ->
|
|
122
|
+ subtree(Elem,G,A)
|
|
123
|
+ ;
|
|
124
|
+ subtree(Elem,D,A) %Racine @< Elem
|
|
125
|
+ )
|
|
126
|
+ ).
|
|
127
|
+
|
|
128
|
+ %----------------------
|
|
129
|
+ % Rotations dans un avl
|
|
130
|
+ %----------------------
|
|
131
|
+ % Les rotations ci-dessous décrivent uniquement les cas ou la rotation est possible.
|
|
132
|
+ % Dans les autres cas, ces relations échouent ; plus précisément :
|
|
133
|
+ % a/ si l'arbre est un avl vide, alors aucune rotation n'est possible ;
|
|
134
|
+ % b/ si l'arbre est un avl non vide mais si son ss-arbre gauche est un avl vide
|
|
135
|
+ % alors la rotation droite n'est pas possible ;
|
|
136
|
+ % c/ si l'arbre est un avl non vide mais si son ss-arbre droite est un avl vide
|
|
137
|
+ % alors la rotation gauche n'est pas possible.
|
|
138
|
+
|
|
139
|
+right_rotate(avl(G,R,D,_H), A_Apres) :-
|
|
140
|
+ height(D,HD),
|
|
141
|
+ G = avl(SG,RG,SD,_HG),
|
|
142
|
+ height(SD,HSD),
|
|
143
|
+ H_Inter is 1 + max(HSD, HD),
|
|
144
|
+ Inter = avl(SD,R,D,H_Inter),
|
|
145
|
+ height(SG,HSG),
|
|
146
|
+ H_Apres is 1 + max(HSG,H_Inter),
|
|
147
|
+ A_Apres = avl(SG,RG,Inter,H_Apres).
|
|
148
|
+
|
|
149
|
+left_rotate(avl(G,R,D,_), A_Apres) :-
|
|
150
|
+ height(G,HG),
|
|
151
|
+ D = avl(SG,RD,SD,_),
|
|
152
|
+ height(SG,HSG),
|
|
153
|
+ H_Inter is 1 + max(HSG, HG),
|
|
154
|
+ Inter = avl(G,R,SG,H_Inter),
|
|
155
|
+ height(SD,HSD),
|
|
156
|
+ H_Apres is 1 + max(H_Inter,HSD),
|
|
157
|
+ A_Apres = avl(Inter,RD,SD,H_Apres).
|
|
158
|
+
|
|
159
|
+ %---------------------------------
|
|
160
|
+ % Insertion equilibree dans un avl
|
|
161
|
+ %---------------------------------
|
|
162
|
+ % On suppose que l'arbre avant insertion est equilibré (difference de hauteur
|
|
163
|
+ % entre les ss-arbres gauche et droite de 1 au maximum)
|
|
164
|
+ % L'insertion doit assurer qu'apres insertion l'arbre est toujours equilibre
|
|
165
|
+ % sinon les rotations necessaires sont effectuees.
|
|
166
|
+
|
|
167
|
+ % On suppose que les noeuds contiennent des informations que l'on peut comparer
|
|
168
|
+ % a l'aide d'une relation d'ordre lexicographique (la cle c'est l'info elle-meme)
|
|
169
|
+ % En prolog, c'est la relation '@<'
|
|
170
|
+ % On peut comparer par exemple des integer, des string, des constantes,
|
|
171
|
+ % des listes d'entiers, des listes de constantes, etc ... bref, des termes clos
|
|
172
|
+ % T1 @< T2 est vrai si T1 est lexicographiquement inférieur a T2.
|
|
173
|
+
|
|
174
|
+insert(Elem, nil, avl(nil,Elem,nil,0)).
|
|
175
|
+insert(Elem, AVL, NEW_AVL) :-
|
|
176
|
+ AVL = avl(Gauche,Racine,Droite,_Hauteur),
|
|
177
|
+ (Elem = Racine ->
|
|
178
|
+ % l'élément est déjà present, pas d'insertion possible
|
|
179
|
+ fail
|
|
180
|
+ ;
|
|
181
|
+ (Elem @< Racine ->
|
|
182
|
+ % insertion dans le ss-arbre gauche
|
|
183
|
+ insert(Elem, Gauche, New_Gauche),
|
|
184
|
+ height(New_Gauche, New_HG),
|
|
185
|
+ height(Droite, HD),
|
|
186
|
+ H_Int is 1+max(New_HG, HD),
|
|
187
|
+ AVL_INT = avl(New_Gauche, Racine, Droite, H_Int),
|
|
188
|
+ right_balance(AVL_INT, NEW_AVL)
|
|
189
|
+ ;
|
|
190
|
+ % Elem @> Racine
|
|
191
|
+ % insertion dans le ss-arbre droite
|
|
192
|
+ insert(Elem, Droite, New_Droite),
|
|
193
|
+ height(New_Droite, New_HD),
|
|
194
|
+ height(Gauche, HG),
|
|
195
|
+ H_Int is 1+max(New_HD, HG),
|
|
196
|
+ AVL_INT =avl(Gauche, Racine,New_Droite, H_Int),
|
|
197
|
+ left_balance(AVL_INT, NEW_AVL)
|
|
198
|
+ )
|
|
199
|
+ ).
|
|
200
|
+
|
|
201
|
+ %------------------------------------------------
|
|
202
|
+ % Suppression d'un element quelconque dans un avl
|
|
203
|
+ %------------------------------------------------
|
|
204
|
+ % On suppose que l'élément à supprimer appartient bien à l'AVL,
|
|
205
|
+ % sinon le predicat échoue (en particulier si l'AVL est vide).
|
|
206
|
+
|
|
207
|
+suppress(Elem, AVL, NEW_AVL) :-
|
|
208
|
+ AVL = avl(Gauche, Racine, Droite, _Hauteur),
|
|
209
|
+ (Elem = Racine ->
|
|
210
|
+ % cas de la suppression de la racine de l'avl
|
|
211
|
+ (Gauche = nil -> % cas simple d'une feuille ou d'un avl sans fils gauche
|
|
212
|
+ NEW_AVL = Droite
|
|
213
|
+ ;
|
|
214
|
+ (Droite = nil -> % cas simple d'un avl avec fils gauche mais sans fils droit
|
|
215
|
+ NEW_AVL = Gauche
|
|
216
|
+ ;
|
|
217
|
+ % cas d'un avl avec fils gauche ET fils droit
|
|
218
|
+ %Gauche \= nil
|
|
219
|
+ %Droite \= nil
|
|
220
|
+ suppress_max(Max, Gauche, New_Gauche),
|
|
221
|
+ AVL_INT = avl(New_Gauche,Max,Droite,_),
|
|
222
|
+ left_balance(AVL_INT, NEW_AVL)
|
|
223
|
+ )
|
|
224
|
+ )
|
|
225
|
+ ;
|
|
226
|
+ % cas des suppressions d'un element autre que la racine
|
|
227
|
+ (Elem @< Racine ->
|
|
228
|
+ % suppression dans le ss-arbre gauche
|
|
229
|
+ suppress(Elem, Gauche, New_Gauche),
|
|
230
|
+ AVL_INT = avl(New_Gauche, Racine, Droite,_),
|
|
231
|
+ left_balance(AVL_INT, NEW_AVL)
|
|
232
|
+ ;
|
|
233
|
+ %Racine @< Droite
|
|
234
|
+ % suppression dans le ss-arbre droite
|
|
235
|
+ suppress(Elem, Droite, New_Droite),
|
|
236
|
+ AVL_INT = avl(Gauche, Racine, New_Droite,_),
|
|
237
|
+ right_balance(AVL_INT, NEW_AVL)
|
|
238
|
+ )
|
|
239
|
+ ).
|
|
240
|
+
|
|
241
|
+ %-------------------------------------------------------
|
|
242
|
+ % Suppression du plus petit element dans un avl non vide
|
|
243
|
+ %-------------------------------------------------------
|
|
244
|
+ % Si l'avl est vide, le prédicat échoue
|
|
245
|
+
|
|
246
|
+suppress_min(Min, AVL, NEW_AVL) :-
|
|
247
|
+ AVL = avl(Gauche,Racine,Droite, _Hauteur),
|
|
248
|
+ (Gauche = nil ->
|
|
249
|
+ Min = Racine,
|
|
250
|
+ NEW_AVL = Droite
|
|
251
|
+ ;
|
|
252
|
+ % Gauche \= nil
|
|
253
|
+ suppress_min(Min, Gauche, New_Gauche),
|
|
254
|
+ AVL_INT = avl(New_Gauche, Racine, Droite,_),
|
|
255
|
+ left_balance(AVL_INT, NEW_AVL)
|
|
256
|
+ ).
|
|
257
|
+
|
|
258
|
+ %-------------------------------------------------------
|
|
259
|
+ % Suppression du plus grand element dans un avl non vide
|
|
260
|
+ %-------------------------------------------------------
|
|
261
|
+ % Si l'avl est vide, le prédicat échoue
|
|
262
|
+
|
|
263
|
+suppress_max(Max, AVL, NEW_AVL) :-
|
|
264
|
+ AVL = avl(Gauche,Racine,Droite, _Hauteur),
|
|
265
|
+ (Droite = nil ->
|
|
266
|
+ Max = Racine,
|
|
267
|
+ NEW_AVL = Gauche
|
|
268
|
+ ;
|
|
269
|
+ % Droite \= nil
|
|
270
|
+ suppress_max(Max, Droite, New_Droite),
|
|
271
|
+ AVL_INT = avl(Gauche, Racine, New_Droite,_),
|
|
272
|
+ right_balance(AVL_INT, NEW_AVL)
|
|
273
|
+ ).
|
|
274
|
+
|
|
275
|
+ %----------------------------------------
|
|
276
|
+ % Re-equilibrages d'un avl vers la gauche
|
|
277
|
+ %----------------------------------------
|
|
278
|
+ % - soit apres insertion d'un element dans le sous-arbre droite
|
|
279
|
+ % - soit apres suppression d'un élément dans le sous-arbre gauche
|
|
280
|
+ %----------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+left_balance(Avl, New_Avl) :-
|
|
283
|
+ Avl = avl(Gauche, Racine, Droite, _Hauteur),
|
|
284
|
+ height(Gauche, HG),
|
|
285
|
+ height(Droite, HD),
|
|
286
|
+ (HG is HD-2 ->
|
|
287
|
+ % le sous-arbre droite est trop haut
|
|
288
|
+ Droite = avl(G_Droite, _R_Droite, D_Droite, _HD),
|
|
289
|
+ height(G_Droite, HGD),
|
|
290
|
+ height(D_Droite, HDD),
|
|
291
|
+ (HDD > HGD ->
|
|
292
|
+ % une simple rotation gauche suffit
|
|
293
|
+ left_rotate(Avl, New_Avl)
|
|
294
|
+ ;
|
|
295
|
+ % il faut faire une rotation droite_gauche
|
|
296
|
+ right_rotate(Droite, New_Droite),
|
|
297
|
+ height(New_Droite, New_HD),
|
|
298
|
+ H_Int is 1+ max(HG, New_HD),
|
|
299
|
+ Avl_Int = avl(Gauche, Racine, New_Droite, H_Int),
|
|
300
|
+ left_rotate(Avl_Int, New_Avl)
|
|
301
|
+ )
|
|
302
|
+ ;
|
|
303
|
+ % la suppression n'a pas desequilibre l'avl
|
|
304
|
+ New_Hauteur is 1+max(HG,HD),
|
|
305
|
+ New_Avl = avl(Gauche, Racine, Droite, New_Hauteur)
|
|
306
|
+ ).
|
|
307
|
+
|
|
308
|
+ %----------------------------------------
|
|
309
|
+ % Re-equilibrages d'un avl vers la droite
|
|
310
|
+ %----------------------------------------
|
|
311
|
+ % - soit apres insertion d'un element dans le sous-arbre gauche
|
|
312
|
+ % - soit apres suppression d'un élément dans le sous-arbre droite
|
|
313
|
+ %----------------------------------------------------------------
|
|
314
|
+
|
|
315
|
+right_balance(Avl, New_Avl) :-
|
|
316
|
+ Avl = avl(Gauche, Racine, Droite, _Hauteur),
|
|
317
|
+ height(Gauche, HG),
|
|
318
|
+ height(Droite, HD),
|
|
319
|
+ (HD is HG-2 ->
|
|
320
|
+ % le sous-arbre gauche est trop haut
|
|
321
|
+ Gauche = avl(G_Gauche, _R_Gauche, D_Gauche, _HG),
|
|
322
|
+ height(G_Gauche, HGG),
|
|
323
|
+ height(D_Gauche, HDG),
|
|
324
|
+ (HGG > HDG ->
|
|
325
|
+ % une simple rotation droite suffit
|
|
326
|
+ right_rotate(Avl, New_Avl)
|
|
327
|
+ ;
|
|
328
|
+ % il faut faire une rotation gauche_droite
|
|
329
|
+ left_rotate(Gauche, New_Gauche),
|
|
330
|
+ height(New_Gauche, New_HG),
|
|
331
|
+ H_Int is 1+ max(New_HG, HD),
|
|
332
|
+ Avl_Int = avl(New_Gauche, Racine, Droite, H_Int),
|
|
333
|
+ right_rotate(Avl_Int, New_Avl)
|
|
334
|
+ )
|
|
335
|
+ ;
|
|
336
|
+ % la suppression n'a pas desequilibre l'avl
|
|
337
|
+ New_Hauteur is 1+max(HG,HD),
|
|
338
|
+ New_Avl = avl(Gauche, Racine, Droite, New_Hauteur)
|
|
339
|
+ ).
|
|
340
|
+
|
|
341
|
+%-----------------------------------------
|
|
342
|
+% Arbres utilises pour les tests unitaires
|
|
343
|
+%-----------------------------------------
|
|
344
|
+avl_test(1, nil).
|
|
345
|
+avl_test(2, avl(nil, 1, nil, 0)).
|
|
346
|
+avl_test(3, avl(nil, 1, avl(nil,2,nil,0), 1)).
|
|
347
|
+avl_test(4, avl(avl(nil,1,nil,0),2, nil, 1)).
|
|
348
|
+avl_test(5, avl(avl(nil,1,nil,0), 2, avl(nil,3,nil,0),1) ).
|
|
349
|
+avl_test(6, avl(avl(nil,5,nil,0), 6, avl(nil,7,nil,0),1) ).
|
|
350
|
+avl_test(7, avl(G,4,D,2)) :-
|
|
351
|
+ avl_test(5,G),
|
|
352
|
+ avl_test(6,D).
|
|
353
|
+avl_test(8, avl(G,5,D,2)) :-
|
|
354
|
+ D = avl(nil,6,nil,0),
|
|
355
|
+ avl_test(3,G).
|
|
356
|
+avl_test(9, avl(G,3,D,2)) :-
|
|
357
|
+ G = avl(nil,1,nil,0),
|
|
358
|
+ avl_test(4,D).
|
|
359
|
+
|
|
360
|
+/* Test uniquement valable avec ECLiPSe
|
|
361
|
+
|
|
362
|
+avl_test(10, Final) :-
|
|
363
|
+ empty(Init),
|
|
364
|
+ (for(I,1,20), fromto(Init,In,Out,Final) do
|
|
365
|
+ insert(I,In,Out)
|
|
366
|
+ ).
|
|
367
|
+*/
|