-
Notifications
You must be signed in to change notification settings - Fork 3
/
AVLTree.java
530 lines (501 loc) · 17.9 KB
/
AVLTree.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
/** Self-balancing binary search tree using the algorithm defined
* by Adelson-Velskii and Landis.
* @author Koffman and Wolfgang
*/
public class AVLTree < E
extends Comparable < E >>
extends BinarySearchTreeWithRotate < E > {
// Insert nested class AVLNode<E> here.
// Data Fields
/** Flag to indicate that height of tree has increased. */
private boolean increase;
/** Flag to indicate that height of tree has decreased */
private boolean decrease;
/** Class to represent an AVL Node. It extends the
BinaryTree.Node by adding the balance field. */
private static class AVLNode < E > extends Node < E > {
/** Constant to indicate left-heavy */
public static final int LEFT_HEAVY = -1;
/** Constant to indicate balanced */
public static final int BALANCED = 0;
/** Constant to indicate right-heavy */
public static final int RIGHT_HEAVY = 1;
/** balance is right subtree height left subtree height */
private int balance;
// Methods
/** Construct a node with the given item as the data field.
@param item The data field
*/
public AVLNode(E item) {
super(item);
balance = BALANCED;
}
/** Return a string representation of this object.
The balance value is appended to the contents.
@return String representation of this object
*/
public String toString() {
return balance + ": " + super.toString();
}
}
/** add starter method.
pre: the item to insert implements the Comparable interface.
@param item The item being inserted.
@return true if the object is inserted; false
if the object already exists in the tree
@throws ClassCastException if item is not Comparable
*/
public boolean add(E item) {
increase = false;
root = add( (AVLNode < E > ) root, item);
return addReturn;
}
/** Recursive add method. Inserts the given object into the tree.
post: addReturn is set true if the item is inserted,
false if the item is already in the tree.
@param localRoot The local root of the subtree
@param item The object to be inserted
@return The new local root of the subtree with the item
inserted
*/
private AVLNode < E > add(AVLNode < E > localRoot, E item) {
if (localRoot == null) {
addReturn = true;
increase = true;
return new AVLNode < E > (item);
}
if (item.compareTo(localRoot.data) == 0) {
// Item is already in the tree.
increase = false;
addReturn = false;
return localRoot;
}
else if (item.compareTo(localRoot.data) < 0) {
// item < data
localRoot.left = add( (AVLNode < E > ) localRoot.left, item);
if (increase) {
decrementBalance(localRoot);
if (localRoot.balance < AVLNode.LEFT_HEAVY) {
increase = false;
return rebalanceLeft(localRoot);
}
}
return localRoot; // Rebalance not needed.
}
else {
// item > data
localRoot.right = add( (AVLNode < E > ) localRoot.right, item);
if (increase) {
incrementBalance(localRoot);
if (localRoot.balance > AVLNode.RIGHT_HEAVY) {
return rebalanceRight(localRoot);
}
else {
return localRoot;
}
}
else {
return localRoot;
}
}
}
/**** BEGIN EXERCISE ****/
/** Delete starter method. Removes the given object
from the AVL tree.
@post The object is not in the tree
@param item - The object to be removed.
@return The object from the tree that was removed
or null if the object was not in the tree.
*/
public E delete(E item) {
decrease = false;
root = delete( (AVLNode < E > ) root, item);
return deleteReturn;
}
/** Recursive delete method. Removes the given object
from the AVL tree.
@post The object is not in the tree and removeReturn
is set to the object that was removed, otherwise
it is set false.
@param localRoot The root of the local subtree
@param item The item to be removed
@return The new root of the local subtree with the item
removed.
*/
private AVLNode < E > delete(AVLNode < E > localRoot, E item) {
if (localRoot == null) { // item is not in tree
deleteReturn = null;
return localRoot;
}
if (item.compareTo(localRoot.data) == 0) {
// item is in the tree -- need to remove it
deleteReturn = localRoot.data;
return findReplacementNode(localRoot);
}
else if (item.compareTo(localRoot.data) < 0) {
// item is < localRoot.data
localRoot.left = delete( (AVLNode < E > ) localRoot.left, item);
if (decrease) {
incrementBalance(localRoot);
if (localRoot.balance > AVLNode.RIGHT_HEAVY) {
return rebalanceRightL( (AVLNode < E > ) localRoot);
}
else {
return localRoot;
}
}
else {
return localRoot;
}
}
else {
// item is > localRoot.data
localRoot.right = delete( (AVLNode < E > ) localRoot.right, item);
if (decrease) {
decrementBalance(localRoot);
if (localRoot.balance < AVLNode.LEFT_HEAVY) {
return rebalanceLeftR(localRoot);
}
else {
return localRoot;
}
}
else {
return localRoot;
}
}
}
/** Function to find a replacement for a node that is being
deleted from a binary search tree. If the node has a null
child, then the replacement is the other child. If neither
are null, then the replacement is the largest value less
than the item being removed.
@pre node is not null
@post a node is deleted from the tree
@param node The node to be deleted or replaced
@return null if both of node's children are null
node.left if node.right is null
node.right if node.left is null
modified copy of node with its data field changed
*/
private AVLNode < E > findReplacementNode(AVLNode < E > node) {
if (node.left == null) {
decrease = true;
return (AVLNode < E > ) node.right;
}
else if (node.right == null) {
decrease = true;
return (AVLNode < E > ) node.left;
}
else {
if (node.left.right == null) {
node.data = node.left.data;
node.left = node.left.left;
incrementBalance(node);
return node;
}
else {
node.data = findLargestChild( (AVLNode < E > ) node.left);
if ( ( (AVLNode < E > ) node.left).balance < AVLNode.LEFT_HEAVY) {
node.left = rebalanceLeft( (AVLNode < E > ) node.left);
}
if (decrease) {
incrementBalance(node);
}
return node;
}
}
}
/** Find the node such that parent.right.right == null
@post The found node is removed from the tree and replaced
by its left child (if any)
@param parent - The possible parent
@return the value of the found node
*/
private E findLargestChild(AVLNode < E > parent) {
if (parent.right.right == null) {
E returnValue = parent.right.data;
parent.right = parent.right.left;
decrementBalance(parent);
return returnValue;
}
else {
E returnValue = findLargestChild( (AVLNode < E > ) parent.right);
if ( ( (AVLNode < E > ) parent.right).balance < AVLNode.LEFT_HEAVY) {
parent.right = rebalanceLeft( (AVLNode < E > ) parent.right);
}
if (decrease) {
decrementBalance(parent);
}
return returnValue;
}
}
/** Method to increment the balance field and to reset the value of
increase or decrease.
@pre The balance field was correct prior to an insertion or
removal, and an item is either been added to the right or removed
from the left.
@post The balance is incremented and the increase and decrease
flags are set to false if the overall height of this subtree
has not changed.
@param node The AVL node whose balance is to be incremented
*/
private void incrementBalance(AVLNode < E > node) {
node.balance++;
if (node.balance > AVLNode.BALANCED) {
/* if now right heavy, the overall height has increased, but
it has not decreased */
increase = true;
decrease = false;
}
else {
/* if now balanced, the overall height has not increased, but
it has decreased. */
increase = false;
decrease = true;
}
}
/** rebalanceRight
@pre localRoot is the root of an AVL subtree that is
more than one right heavy.
@post balance is restored and increase is set false
@param localRoot Root of the AVL subtree that needs rebalancing
@return a new localRoot
*/
private AVLNode < E > rebalanceRight(AVLNode < E > localRoot) {
// Obtain reference to right child
AVLNode < E > rightChild = (AVLNode < E > ) localRoot.right;
// See if right-left heavy
if (rightChild.balance < AVLNode.BALANCED) {
// Obtain reference to right-left child
AVLNode < E > rightLeftChild = (AVLNode < E > ) rightChild.left;
/* Adjust the balances to be their new values after
the rotates are performed.
*/
if (rightLeftChild.balance > AVLNode.BALANCED) {
rightChild.balance = AVLNode.BALANCED;
rightLeftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.LEFT_HEAVY;
}
else if (rightLeftChild.balance < AVLNode.BALANCED) {
rightChild.balance = AVLNode.RIGHT_HEAVY;
rightLeftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
else {
rightChild.balance = AVLNode.BALANCED;
rightLeftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
/** After the rotates the overall height will be
reduced thus increase is now false, but
decrease is now true.
*/
increase = false;
decrease = true;
// Perform double rotation
localRoot.right = rotateRight(rightChild);
return (AVLNode < E > ) rotateLeft(localRoot);
}
else {
/* In this case both the rightChild (the new root)
and the root (new left child) will both be balanced
after the rotate. Also the overall height will be
reduced, thus increase will be fasle, but decrease
will be true.
*/
rightChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
increase = false;
decrease = true;
// Now rotate the
return (AVLNode < E > ) rotateLeft(localRoot);
}
}
/** rebalanceLeftR
@pre localRoot is the root of an AVL subtree that is
more than one left heavy.
@post balance is restored and increase is set false
@param localRoot Root of the AVL subtree that needs rebalancing
@return a new localRoot
*/
private AVLNode < E > rebalanceLeftR(AVLNode < E > localRoot) {
// Obtain reference to left child
AVLNode < E > leftChild = (AVLNode < E > ) localRoot.left;
// See if left-right heavy
if (leftChild.balance > AVLNode.BALANCED) {
// Obtain reference to left-right child
AVLNode < E > leftRightChild = (AVLNode < E > ) leftChild.right;
/* Adjust the balances to be their new values after
the rotates are performed.
*/
if (leftRightChild.balance < AVLNode.BALANCED) {
leftChild.balance = AVLNode.LEFT_HEAVY;
leftRightChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
else if (leftRightChild.balance > AVLNode.BALANCED) {
leftChild.balance = AVLNode.BALANCED;
leftRightChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.RIGHT_HEAVY;
}
else {
leftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
/** After the rotates the overall height will be
reduced thus increase is now false, but
decrease is now true.
*/
increase = false;
decrease = true;
// Perform double rotation
localRoot.left = rotateLeft(leftChild);
return (AVLNode < E > ) rotateRight(localRoot);
}
if (leftChild.balance < AVLNode.BALANCED) {
/* In this case both the leftChild (the new root)
and the root (new right child) will both be balanced
after the rotate. Also the overall height will be
reduced, thus increase will be fasle, but decrease
will be true.
*/
leftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
increase = false;
decrease = true;
}
else {
/* After the rotate the leftChild (new root) will
be right heavy, and the local root (new right child)
will be left heavy. The overall height of the tree
will not change, thus increase and decrease remain
unchanged.
*/
leftChild.balance = AVLNode.RIGHT_HEAVY;
localRoot.balance = AVLNode.LEFT_HEAVY;
}
// Now rotate the
return (AVLNode < E > ) rotateRight(localRoot);
}
/** rebalanceRightL
@pre localRoot is the root of an AVL subtree that is
more than one right heavy.
@post balance is restored and increase is set false
@param localRoot Root of the AVL subtree that needs rebalancing
@return a new localRoot
*/
private AVLNode < E > rebalanceRightL(AVLNode < E > localRoot) {
// Obtain reference to right child
AVLNode < E > rightChild = (AVLNode < E > ) localRoot.right;
// See if right-left heavy
if (rightChild.balance < AVLNode.BALANCED) {
// Obtain reference to right-left child
AVLNode < E > rightLeftChild = (AVLNode < E > ) rightChild.left;
/* Adjust the balances to be their new values after
the rotates are performed.
*/
if (rightLeftChild.balance > AVLNode.BALANCED) {
rightChild.balance = AVLNode.RIGHT_HEAVY;
rightLeftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
else if (rightLeftChild.balance < AVLNode.BALANCED) {
rightChild.balance = AVLNode.BALANCED;
rightLeftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.LEFT_HEAVY;
}
else {
rightChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
/** After the rotates the overall height will be
reduced thus increase is now false, but
decrease is now true.
*/
increase = false;
decrease = true;
// Perform double rotation
localRoot.right = rotateRight(rightChild);
return (AVLNode < E > ) rotateLeft(localRoot);
}
if (rightChild.balance > AVLNode.BALANCED) {
/* In this case both the rightChild (the new root)
and the root (new left child) will both be balanced
after the rotate. Also the overall height will be
reduced, thus increase will be fasle, but decrease
will be true.
*/
rightChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
increase = false;
decrease = true;
}
else {
/* After the rotate the rightChild (new root) will
be left heavy, and the local root (new left child)
will be right heavy. The overall height of the tree
will not change, thus increase and decrease remain
unchanged.
*/
rightChild.balance = AVLNode.LEFT_HEAVY;
localRoot.balance = AVLNode.RIGHT_HEAVY;
}
// Now rotate the
return (AVLNode < E > ) rotateLeft(localRoot);
}
/**** END EXERCISE ****/
/** Method to rebalance left.
pre: localRoot is the root of an AVL subtree that is
critically left-heavy.
post: Balance is restored.
@param localRoot Root of the AVL subtree
that needs rebalancing
@return a new localRoot
*/
private AVLNode < E > rebalanceLeft(AVLNode < E > localRoot) {
// Obtain reference to left child.
AVLNode < E > leftChild = (AVLNode < E > ) localRoot.left;
// See whether left-right heavy.
if (leftChild.balance > AVLNode.BALANCED) {
// Obtain reference to left-right child.
AVLNode < E > leftRightChild = (AVLNode < E > ) leftChild.right;
/** Adjust the balances to be their new values after
the rotations are performed.
*/
if (leftRightChild.balance < AVLNode.BALANCED) {
leftChild.balance = AVLNode.BALANCED;
leftRightChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.RIGHT_HEAVY;
}
else {
leftChild.balance = AVLNode.LEFT_HEAVY;
leftRightChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
// Perform left rotation.
localRoot.left = rotateLeft(leftChild);
}
else { //Left-Left case
/** In this case the leftChild (the new root)
and the root (new right child) will both be balanced
after the rotation.
*/
leftChild.balance = AVLNode.BALANCED;
localRoot.balance = AVLNode.BALANCED;
}
// Now rotate the local root right.
return (AVLNode < E > ) rotateRight(localRoot);
}
/**
* @param node
*/
private void decrementBalance(AVLNode < E > node) {
// Decrement the balance.
node.balance--;
if (node.balance == AVLNode.BALANCED) {
/** If now balanced, overall height has not increased. */
increase = false;
}
}
}