View difference between Paste ID: s8id3pJg and EvA944Vw
SHOW: | | - or go back to the newest paste.
1
//
2
// Created by Julio Tentor <jtentor@fi.unju.edu.ar>
3
//
4
5
// Código basado en el capítulo 14 de
6
// Joyanes Aguilar, Luis and Zahonero Martínez, Ignacio. 2008. Estructuras de datos en Java
7
// https://drive.google.com/file/d/0B7lAHg81F3X_c2g5c2txQ1g5TTQ/view
8
9
public class AVLTree<ELEMENT extends Comparable<ELEMENT>> {
10
11
    protected class AVLNode<ELEMENT> {
12
        public ELEMENT item;
13
        public AVLNode<ELEMENT> left;
14
        public AVLNode<ELEMENT> right;
15
        public int balance;
16
17
        public AVLNode() {
18
            this(null, null, null, 0);
19
        }
20
        public AVLNode(ELEMENT item) {
21
            this(item, null, null, 0);
22
        }
23
        public AVLNode(ELEMENT item, AVLNode<ELEMENT> left, AVLNode<ELEMENT> right, int balance) {
24
            this.item = item;
25
            this.left = left;
26
            this.right = right;
27
            this.balance = balance;
28
        }
29
30
        @Override
31
        public String toString() {
32
            return this.item.toString();
33
        }
34
35
        // Método para propósitos académicos
36
        public void Visit() {
37
            System.out.printf("%s ", this.item.toString());
38
        }
39
    }
40
41
42
43
    protected AVLNode<ELEMENT> root;
44
    // atributo para propositos académicos
45
    protected boolean verbose;
46
47
    public AVLTree() {
48
        this.root = null;
49
        this.verbose = false;
50
    }
51
52
    // método para propósitos académicos
53
    public boolean setVerbose(boolean verbose) {
54
        this.verbose = verbose;
55
        return this.verbose;
56
    }
57
58
59
    @Override
60
    public String toString() {
61
        return toString(this.root);
62
    }
63
    protected String toString(AVLNode<ELEMENT> root) {
64
        StringBuilder sb = new StringBuilder();
65
        if (root != null) {
66
            sb.append(root.item.toString());
67
            //sb.append("[" + root.balance.toString() + "]");
68
            sb.append((root.balance < 0) ? "[-]" : (root.balance == 0) ? "[.]" : "[+]" );
69
            if (root.left != null) {
70
                sb.append("(" + toString(root.left));
71
                if (root.right != null) {
72
                    sb.append(", " + toString(root.right));
73
                }
74
                sb.append(")");
75
            } else {
76
                if (root.right != null) {
77
                    sb.append("(, " + toString(root.right) + ")");
78
                }
79
            }
80
        }
81
        return sb.toString();
82
    }
83
84
85
    //region Métodos para recorrer el árbol
86
    public void PreOrder() {
87
        PreOrder(this.root);
88
    }
89
    protected void PreOrder(AVLNode<ELEMENT> root) {
90
        if (root != null) {
91
            root.Visit();
92
            PreOrder(root.left);
93
            PreOrder(root.right);
94
        }
95
    }
96
97
    public void InOrder() {
98
        InOrder(this.root);
99
    }
100
    protected void InOrder(AVLNode<ELEMENT> root) {
101
        if (root != null) {
102
            InOrder(root.left);
103
            root.Visit();
104
            InOrder(root.right);
105
        }
106
    }
107
108
    public void PostOrder() {
109
        PostOrder(this.root);
110
    }
111
    protected void PostOrder(AVLNode<ELEMENT> root) {
112
        if (root != null) {
113
            PostOrder(root.left);
114
            PostOrder(root.right);
115
            root.Visit();
116
        }
117
    }
118
119
    public void DescendingOrder() {
120
        DescendingOrder(this.root);
121
    }
122
    protected void DescendingOrder(AVLNode<ELEMENT> root) {
123
        if (root != null) {
124
            DescendingOrder(root.right);
125
            root.Visit();
126
            DescendingOrder(root.left);
127
        }
128
    }
129
    //endregion
130
131
    //region Métodos para contar elementos del árbol
132
    public int NodeCount() {
133
        return NodeCount(this.root);
134
    }
135
    protected int NodeCount(AVLNode<ELEMENT> root) {
136
        if (root != null) {
137
            return 1 + NodeCount(root.left) + NodeCount(root.right);
138
        }
139
        return 0;
140
    }
141
142
143
    public int LeafCount() {
144
        return LeafCount(this.root);
145
    }
146
    protected int LeafCount(AVLNode<ELEMENT> root) {
147
        if (root != null) {
148
            if ( (root.left == null) && (root.right == null) ) {
149
                return 1;
150
            } else {
151
                return LeafCount(root.left) + LeafCount(root.right);
152
            }
153
        }
154
        return 0;
155
    }
156
157
158
    public int InternalCount() {
159
        return InternalCount(this.root);
160
    }
161
    protected int InternalCount(AVLNode<ELEMENT> root) {
162
        if (root != null) {
163
            if ( (root.left == null) && (root.right == null) ) {
164
                return 0;
165
            } else {
166
                return 1 + InternalCount(root.left) + InternalCount(root.right);
167
            }
168
        }
169
        return 0;
170
    }
171
172
173
    public int MaxLevel() {
174
        return MaxLevel(this.root);
175
    }
176
    protected int MaxLevel(AVLNode<ELEMENT> root) {
177
        if (root != null) {
178
            if ( (root.left != null) || (root.right != null) ) {
179
                int leftLevel = MaxLevel(root.left);
180
                int rightLevel = MaxLevel(root.right);
181
                return 1 + Math.max(leftLevel, rightLevel);
182
            }
183
            return 0;
184
        }
185
        return -1;
186
    }
187
188
    public int Height() {
189
        return MaxLevel() + 1;
190
    }
191
    //endregion
192
193
    //region Métodos para buscar
194
    public boolean contains(ELEMENT item) {
195
        return contains(this.root, item);
196
    }
197
    private boolean contains(AVLNode<ELEMENT> root, ELEMENT item) {
198
        if (root == null) {
199
            return false;
200
        }
201
        if (item.compareTo(root.item) < 0) {
202
            return contains(root.left, item);
203
        }
204
        if (item.compareTo(root.item) > 0) {
205
            return contains(root.right, item);
206
        }
207
        return true;
208
    }
209
    //endregion
210
211
    //region Métodos para agregar elementos al árbol
212
213
    public void add(ELEMENT item) {
214
        if (this.verbose) {
215
            System.out.printf("Agrega %s", item.toString());
216
        }
217
218
        boolean[] change = { false };
219
        this.root = addAVL(this.root, item, change);
220
221
        if (this.verbose) {
222
            System.out.printf("\t %s\n", this.toString());
223
        }
224
    }
225
226
    private AVLNode<ELEMENT> addAVL(AVLNode<ELEMENT> root, ELEMENT item, boolean[] change) {
227
        AVLNode<ELEMENT> n1;
228
229
        if (root == null) {
230
            root = new AVLNode<ELEMENT>(item);
231
            change[0] = true; // cambia el balance
232
            return root;
233
        }
234
235
        if (item.compareTo(root.item) < 0) { // el nuevo elemento es menor
236
            root.left = addAVL(root.left, item, change); // agrega por la izquierda
237
            if (change[0]) { // cambió el balance?
238
                switch (root.balance) { // balance = hD - hI
239
                    case 1: // antes izquierda < derecha
240
                        root.balance = 0; // después izquierda == derecha
241
                        change[0] = false; // balance ajustado
242
                        break;
243
                    case 0: // antes izquierda == derecha
244
                        root.balance = -1; // después izquierda > derecha
245
                        break;
246
                    case -1: // antes izquierda > derecha
247
                        n1 = root.left;
248
                        if (n1.balance == -1) { // izquierda izquierda es mayor
249
                            root = leftleftRotation(root, n1); // LR rotación doble
250
                        } else {
251
                            root = leftrightRotation(root, n1); // LL rotación simple
252
                        }
253
                        change[0] = false; // balance ajustado
254
                        break;
255
                }
256
            }
257
            return root;
258
        }
259
260
        if (item.compareTo(root.item) > 0) { // el nuevo elemento es mayor
261
            root.right = addAVL(root.right, item, change); // agregar por la derecha
262
            if (change[0]) { // cambió el balance?
263
                switch (root.balance) { // balance = hD - hI
264
                    case -1: // antes izquierda > derecha
265
                        root.balance = 0; // ahora izquierda == derecha
266
                        change[0] = false; // balance ajustado
267
                        break;
268
                    case 0: // antes izquierda == derecha
269
                        root.balance = 1; // ahora izquierda < derecha
270
                        break;
271
                    case 1: // antes izquierda < derecha
272
                        n1 = root.right;
273
                        if (n1.balance == 1) { // derecha derecha es mayor
274
                            root = rightrightRotation(root, n1); // RR rotación simple
275
                        } else {
276
                            root = rightleftRotation(root, n1); // RL rotación doble
277
                        }
278
                        change[0] = false; // balance ajustado
279
                        break;
280
                }
281
            }
282
            return root;
283
        }
284
        throw new RuntimeException("Claves repetidas");
285
    }
286
    //endregion
287
288
    //region Rotaciones LL LR RR RL
289
    private AVLNode<ELEMENT> leftleftRotation(AVLNode<ELEMENT> n, AVLNode<ELEMENT> n1) {
290
        if (this.verbose) {
291
            System.out.print(" LL ");
292
        }
293
294
        n.left = n1.right;
295
        n1.right = n;
296
        if (n1.balance == -1) {
297
            n.balance = 0;
298
            n1.balance = 0;
299
        } else {
300
            n.balance = -1;
301
            n1.balance = 1;
302
        }
303
        return n1;
304
    }
305
306
    private AVLNode<ELEMENT> leftrightRotation(AVLNode<ELEMENT> n, AVLNode<ELEMENT> n1) {
307
        if (this.verbose) {
308
            System.out.print(" LR ");
309
        }
310
311
        AVLNode<ELEMENT> n2;
312
        n2 = n1.right;
313
        n.left = n2.right;
314
        n2.right = n;
315
        n1.right = n2.left;
316
        n2.left = n1;
317
        n1.balance = (n2.balance == 1) ? -1 : 0;
318
        n.balance = (n2.balance == -1) ? 1 : 0;
319
        n2.balance = 0;
320
        return n2;
321
    }
322
323
    private AVLNode<ELEMENT> rightrightRotation(AVLNode<ELEMENT> n, AVLNode<ELEMENT> n1) {
324
        if (this.verbose) {
325
            System.out.print(" RR ");
326
        }
327
328
        n.right = n1.left;
329
        n1.left = n;
330
        if (n1.balance == 1) {
331
            n.balance = 0;
332
            n1.balance = 0;
333
        } else {
334
            n.balance = 1;
335
            n1.balance = -1;
336
        }
337
        return n1;
338
    }
339
340
341
    private AVLNode<ELEMENT> rightleftRotation(AVLNode<ELEMENT> n, AVLNode<ELEMENT> n1) {
342
        if (this.verbose) {
343
            System.out.print(" RL ");
344
        }
345
346
        AVLNode<ELEMENT> n2;
347
        n2 = n1.left;
348
        n.right = n2.left;
349
        n2.left = n;
350
        n1.left = n2.right;
351
        n2.right = n1;
352
        n.balance = (n2.balance == 1) ? -1: 0;
353
        n1.balance = (n2.balance == -1) ? 1 : 0;
354
        n2.balance = 0;
355
        return n2;
356
    }
357
    //endregion
358
359
    //region Métodos para remover elementos
360
361
    public void remove(ELEMENT item) {
362
        if (this.verbose) {
363
            System.out.printf("Extrae %s", item.toString());
364
        }
365
366
        boolean[] change = { false };
367
        this.root = removeAVL(this.root, item, change);
368
369
        if (this.verbose) {
370
            System.out.printf("\t %s\n", this.toString());
371
        }
372
    }
373
    private AVLNode<ELEMENT> removeAVL(AVLNode<ELEMENT> root, ELEMENT item, boolean[] change) {
374
375
        if (root == null) {
376
            throw new RuntimeException("No existe");
377
        }
378
379
        if (item.compareTo(root.item) < 0) { // el elemento es menor
380
            root.left = removeAVL(root.left, item, change); // borrar por la izquierda
381
            if (change[0]) { // cambió el balance?
382
                root = leftBalance(root, change); // ajustar el balance izquierdo
383
            }
384
            return root;
385
        }
386
387
        if (item.compareTo(root.item) > 0) { // el elemento es mayor
388
            root.right = removeAVL(root.right, item, change); // borrar por la derecha
389
            if (change[0]) { // cambió el balance?
390
                root = rightBalance(root, change); // ajustar el balance derecho
391
            }
392
            return root;
393
        }
394
395
396
        AVLNode<ELEMENT> q;
397
        q = root;
398
        if (q.left == null) { // no hay izquierda
399
            root = q.right; // un descendiente por la derecha u hoja
400
            change[0] = true; // cambia el balance
401
        } else {
402
            if (q.right == null) { // no hay derecha
403
                root = q.left; // un descendiente por la izquierda
404
                change[0] = true; // cambia el balance
405
            } else { // dos descendientes !!!
406
                root.left = eldestOfMinors(root, root.left, change); // mayor de los menores
407
                if (change[0]) { // cambió el balance?
408
                    root = leftBalance(root, change); // ajustar el balance izquierdo
409
                }
410
                q = null; // eliminar el nodo
411
            }
412
        }
413
        return root;
414
    }
415
416
    private AVLNode<ELEMENT> eldestOfMinors(AVLNode<ELEMENT> n, AVLNode<ELEMENT> eldest, boolean[] change) {
417
        if (eldest.right != null) { // hay algo a la derecha
418
            eldest.right = eldestOfMinors(n, eldest.right, change); // busca el mayor de los menores
419
            if (change[0]) { // cambió el balance?
420
                eldest = rightBalance(eldest, change); // ajustar el balance derecho
421
            }
422
        } else {
423
            n.item = eldest.item;
424
            n = eldest;
425
            eldest = eldest.left;
426
            n = null;
427
            change[0] = true;
428
        }
429
        return eldest;
430
    }
431
432
    private AVLNode<ELEMENT> leftBalance(AVLNode<ELEMENT> n, boolean[] change) {
433
        AVLNode<ELEMENT> n1;
434
        switch (n.balance) { // balance = hD - hI
435
            case -1 : // antes izquierda > derecha
436
                n.balance = 0; // ahora izquierda == derecha
437
                break;
438
            case 0 : // antes izquierda == derecha
439
                n.balance = 1; // ahora izquierda < derecha
440
                change[0] = false; // balance ajustado
441
                break;
442
            case 1 : // antes izquierda < derecha
443
                n1 = n.right;
444
                if (n1.balance >= 0) {
445
                    if (n1.balance == 0) {
446
                        change[0] = false; // balance ajustado
447
                    }
448
                    n = rightrightRotation(n, n1);
449
                } else {
450
                    n = rightleftRotation(n, n1);
451
                }
452
                break;
453
        }
454
        return n;
455
    }
456
457
    private AVLNode<ELEMENT> rightBalance(AVLNode<ELEMENT> n, boolean[] change) {
458
        AVLNode<ELEMENT> n1;
459
        switch (n.balance) { // balance = hD - hI
460
            case -1 : // antes izquiera > derecha
461
                n1 = n.left;
462
                if (n1.balance <= 0) {
463
                    if (n1.balance == 0) {
464
                        change[0] = false; // balance ajustado
465
                    }
466
                    n = leftleftRotation(n, n1);
467
                } else {
468
                    n = leftrightRotation(n, n1);
469
                }
470
                break;
471
            case 0 : // antes izquierda == derecha
472
                n.balance = -1; // ahora izquierda > derecha
473
                change[0] = false; // balance ajustado
474
                break;
475
            case 1 : // antes izquierda < derecha
476
                n.balance = 0;
477
                break;
478
        }
479
        return n;
480
    }
481
    //endregion
482
483
}
484