File Coverage

blib/lib/Tree/AVL.pm
Criterion Covered Total %
statement 293 582 50.3
branch 127 290 43.7
condition 17 36 47.2
subroutine 26 47 55.3
pod 14 40 35.0
total 477 995 47.9


line stmt bran cond sub pod time code
1             ##############################################################################
2             #
3             # AVL.pm
4             #
5             # An implementation of an AVL tree for storing comparable objects.
6             #
7             # AVL Trees are balanced binary trees, first introduced
8             # in "An Algorithm for the Organization of Data" by
9             # Adelson-Velskii and Landis in 1962.
10             #
11             # Balance is kept in an AVL tree during insertion and
12             # deletion by maintaining a 'balance' factor in each node.
13             #
14             # If the subtree below any node in the tree is evenly balanced,
15             # the balance factor for that node will be 0.
16             #
17             # When the right-subtree below a node is taller than the left-subtree,
18             # the balance factor will be 1. For the opposite case, the balance
19             # factor will be -1.
20             #
21             # If the either subtree is heavier (taller by more than 2 levels) than the
22             # other, the balance factor within the node will be set to (+-)2,
23             # and the subtree below that node will be rebalanced.
24             #
25             # Re-balancing is done via 'single' or 'double' rotations, each of which
26             # takes constant-time.
27             #
28             # Insertion into an AVL tree will require at most 1 rotation.
29             #
30             # Deletion from an AVL tree may take as much as log(n) rotations
31             # in order to restore balance.
32             #
33             # Balanced trees can save time in your programs
34             # when used instead of regular flat data-structures. For example, if
35             # you are processing as much as 1,125,899,906,842,624 (a quadrillion) ordered
36             # objects, the time (number of comparisons) required to access one of those
37             # objects will be on the order of 1,125,899,906,842,624 in the worst case
38             # if you keep them in a flat data structure. However, using a balanced
39             # tree such as a 2-3 tree, a Red-Black tree or an AVL tree, the worst-case
40             # time (comparisons) required will 50.
41             #
42             ##############################################################################
43              
44             package Tree::AVL;
45              
46 1     1   19739 use Carp;
  1         2  
  1         72  
47 1     1   4 use strict;
  1         1  
  1         25  
48 1     1   3 use warnings;
  1         5  
  1         4377  
49              
50             our $VERSION = '1.077';
51              
52              
53             ##################################################
54             #
55             # AVL tree constructor
56             #
57             ##################################################
58             sub new {
59 1     1 1 10 my $invocant = shift;
60 1   33     6 my $class = ref($invocant) || $invocant;
61 1         8 my $self = {
62             _node => {
63             _obj => undef, # Object to store in AVL tree
64             _left_node => undef,
65             _right_node => undef,
66             _height => 0,
67             _balance => 0, # (abs(balance) < 2) <-> AVL property
68             },
69              
70             fcompare => undef, # comparison function
71             fget_key => undef, # function to get key from obj
72             fget_data => undef, # function to get data from obj
73            
74             acc_lookup_hash => undef,
75             @_, # Override previous attributes
76             };
77              
78 1         2 $self = bless $self, $class;
79              
80 1 50       6 if(!$self->{fcompare}){
81 1         4 $self->{fcompare} = \&default_cmp_func;
82             }
83 1 50       2 if(!$self->{fget_key}){
84 1     33   6 $self->{fget_key} = sub{ return $_[0]; };
  33         40  
85             }
86 1 50       3 if(!$self->{fget_data}){
87 1     0   6 $self->{fget_data} = sub{ return $_[0]; };
  0         0  
88             }
89              
90 1         15 return $self;
91             }
92              
93              
94             #
95             # insert
96             #
97             # usage: $tree->insert($object);
98             #
99             sub insert
100             {
101 7     7 1 3427 my ($self, $object) = @_;
102 7 50       18 if(!defined($object)){
103 0         0 croak "Error: cannot insert uninitialized object into AVL tree.\n";
104             }
105 7         13 $self->avl_insert($object);
106 7         15 return;
107             }
108              
109              
110             #
111             # avl_insert
112             #
113             # usage: $tree_object->avl_insert($object);
114             #
115             sub avl_insert
116             {
117 12     12 0 15 my ($self, $object, $node, $depth) = @_;
118              
119 12 100       19 if(!$depth){
120 7         8 $depth = 0;
121             }
122 12 100       18 if(!$node){
123 7         11 $node = \$self->{_node};
124             }
125            
126 12         15 my $get_key_func = $self->{fget_key};
127 12         74 my $key = $get_key_func->($object);
128            
129 12         20 my $node_obj = $$node->{_obj};
130 12         30 my $own_key;
131            
132 12         10 my $increase = 0;
133 12         12 my $change = 0;
134              
135 12 100       24 if( !defined($self->{_node}->{_obj}) ) # no root data yet, so populate with $object
136             {
137 2         3 $self->{_node}->{_obj} = $object;
138              
139 2         3 return;
140             }
141             else # need to insert object if object is not already in tree
142             {
143 10         12 $own_key = $node_obj->$get_key_func();
144 10 50       19 if(!defined($own_key)){
145 0         0 croak "Error: get_key() method provided to Tree::AVL object returned a null value\n";
146             }
147              
148 10         10 my $cmpfunc = $self->{fcompare};
149 10         40 my $result = $cmpfunc->($node_obj, $object);
150            
151 10 50       42 if($result == 0){ #element is already in tree, do nothing.
    100          
152 0         0 return 0;
153             }
154             elsif($result < 0){ # insert into right subtree
155 7 100       40 if (!defined($$node->{_right_node})){ # Need to create a new node.
156              
157              
158 3         9 my $new_node = {
159             _obj => $object,
160             _balance => 0,
161             _right_node => undef,
162             _left_node => undef,
163             };
164            
165 3         4 $$node->{_right_node} = $new_node;
166 3         5 $increase = 1;
167             }
168             else{ # descend and insert into right subtree
169 4         35 $change = $self->avl_insert($object, \$$node->{_right_node}, $depth+1);
170 4         9 $increase = 1 * $change;
171             }
172             }
173             else{ # insert into left subtree
174 3 100       8 if (!defined($$node->{_left_node})){ # Need to create a new node.
175              
176 2         8 my $new_node = {
177             _obj => $object,
178             _balance => 0,
179             _right_node => undef,
180             _left_node => undef,
181             };
182            
183 2         3 $$node->{_left_node} = $new_node;
184 2         4 $increase = -1;
185             }
186             else{ # descend and insert into left subtree
187 1         7 $change = $self->avl_insert($object, \$$node->{_left_node}, $depth+1);
188 1         2 $increase = -1 * $change;
189             }
190             }
191             } # end else determine whether need to insert into left or right subtree
192              
193 10         15 $$node->{_balance} = $$node->{_balance} + $increase;
194              
195 10 100 100     36 if($increase && $$node->{_balance}){
196 8         16 my $height_change = $self->rebalance($node);
197 8         9 $change = 1 - $height_change;
198             }
199             else{
200 2         5 $change = 0;
201             }
202              
203 10 100       37 if($depth == 0){
204 5         28 $self->{_node} = $$node;
205             }
206              
207 10         18 return $change;
208             }
209              
210              
211             #
212             # remove
213             #
214             # usage: my $found_obj = $avltree->remove($object);
215             #
216             # remove an object from tree.
217             #
218             #
219             sub remove
220             {
221 3     3 1 1299 my ($self, $object) = @_;
222 3         10 my ($obj) = $self->delete($object);
223 3         10 return $obj;
224             }
225              
226              
227             #
228             # avl_delete
229             #
230             # usage: ($found_obj) = $tree->delete($object);
231             #
232             #
233             sub delete
234             {
235            
236 5     5 0 10 my ($self, $object, $node, $depth) = @_;
237            
238 5 100       10 if(!$node){
239 3         437 $node = \$self->{_node};
240             }
241 5 100       9 if(!$depth){
242 3         4 $depth = 0;
243             }
244            
245 5         6 my $deleted_node;
246 5         6 my $change = 0;
247 5         3 my $decrease = 0;
248              
249 5 50       12 if(!defined($$node->{_obj})){ # no root data yet
250 0         0 return;
251             }
252             else{
253 5         6 my $node_obj = $$node->{_obj};
254 5         7 my $get_key_func = $self->{fget_key};
255 5         7 my $own_key = $get_key_func->($node_obj);
256 5         7 my $cmpfunc = $self->{fcompare};
257            
258 5         7 my $result = $cmpfunc->($node_obj, $object);
259 5 100       25 if($result > 0){ # look into left subtree
    100          
    50          
260 1 50       4 if (!defined($$node->{_left_node})){
261 0         0 return;
262             }
263             else{
264 1         6 ($deleted_node, my $new_ref, $change) = Tree::AVL::delete($self, $object, \$$node->{_left_node}, $depth+1);
265 1 50       3 if($deleted_node){
266 0         0 $$node->{_left_node} = $new_ref;
267 0         0 $decrease = -1 * $change;
268             }
269             else{
270 1         4 return;
271             }
272             }
273             }
274             elsif($result < 0){ # look into right subtree
275 2 100       6 if (!defined($$node->{_right_node})){
276 1         4 return;
277             }
278             else{
279 1         5 ($deleted_node, my $new_ref, $change) = Tree::AVL::delete($self, $object, \$$node->{_right_node}, $depth+1);
280 1 50       3 if($deleted_node){
281 0         0 $$node->{_right_node} = $new_ref;
282 0         0 $decrease = 1 * $change;
283             }
284             else{
285 1         4 return;
286             }
287             }
288             }
289             elsif($result == 0){ # this the node we want to delete FOUND THE NODE.
290 2         4 $deleted_node = $$node->{_obj};
291              
292 2 100 66     21 if(!$$node->{_left_node} && !$$node->{_right_node}){ # this is the node to delete, and it is a leaf node.
    50 33        
    50          
    50          
293            
294 1 50       2 if($depth == 0){ # this is also the root node.
295            
296 1         5 $$node = {
297             _obj => undef,
298             _balance => 0,
299             _right_node => undef,
300             _left_node => undef,
301             };
302            
303             }
304             else{
305             # this is the node to delete. It is not the root node and it has no children (it is a leaf)
306 0         0 $$node = undef;
307            
308 0         0 $change = 1;
309 0         0 return ($deleted_node, $$node, $change);
310             }
311             }
312             elsif(!$$node->{_left_node}){
313 0         0 $$node = $$node->{_right_node};
314 0         0 $change = 1;
315            
316 0         0 return ($deleted_node, $$node, $change);
317             }
318             elsif(!$$node->{_right_node}){
319 0         0 $$node = $$node->{_left_node};
320 0         0 $change = 1;
321              
322 0         0 return ($deleted_node, $$node, $change);
323             }
324             elsif($$node->{_right_node} && $$node->{_left_node}){
325 1         5 (my $new_root_obj, $$node->{_right_node}, $change) = $self->delete_smallest(\$$node->{_right_node});
326 1 50       31 if($self->is_empty($$node->{_right_node})){
327 0         0 delete $$node->{_right_node};
328             }
329 1         3 $$node->{_obj} = $new_root_obj;
330             }
331            
332 2         3 $decrease = $change;
333            
334             } # end else determine whether need to look into left or right subtree, or neither
335             } # end else() there was root data.
336              
337 2         6 $$node->{_balance} = $$node->{_balance} - $decrease;
338 2 100       4 if($decrease){
339 1 50       2 if($$node->{_balance}){
340 1         6 $change = $self->rebalance($node);
341             }
342             else{
343 0         0 $change = 1;
344             }
345             }
346             else{
347 1         1 $change = 0;
348             }
349              
350 2         5 return ($deleted_node, $$node, $change);
351             }
352              
353              
354             #
355             # delete_smallest
356             #
357             # usage: $tree_object->delete_smallest();
358             #
359             sub delete_smallest
360             {
361 4     4 0 4 my ($self,
362             $node,
363             $depth) = @_;
364              
365 4 100       8 if(!$node){
366 1         2 $node = \$self->{_node};
367             }
368              
369 4         5 my $node_obj = $$node->{_obj};
370 4         4 my $get_key_func = $self->{fget_key};
371 4         7 my $own_key = $get_key_func->($node_obj);
372 4         6 my $decrease = 0;
373 4         5 my $change = 0;
374              
375 4 100       7 if(!$$node->{_left_node}){
376 2         3 my $obj = $$node->{_obj};
377 2 50 66     8 if(!$$node->{_right_node} && !$depth){
378 0         0 $$node = {
379             _obj => undef,
380             _balance => 0,
381             _right_node => undef,
382             _left_node => undef,
383             };
384 0         0 $change = 1;
385             }
386             else{
387 2 100       4 if($$node->{_right_node}){
388 1         2 $$node = $$node->{_right_node};
389             }
390             else{
391 1         2 $$node = undef;
392             }
393 2 100       6 if($$node){
394 1         2 $$node->{_balance} = 0;
395             }
396 2         2 $change = 1;
397             }
398 2         6 return ($obj, $$node, $change);
399             }
400             else{
401 2         7 my ($obj, $newleft, $change) = Tree::AVL::delete_smallest($self, \$$node->{_left_node}, 1);
402 2         3 $decrease = -1 * $change;
403 2         3 $$node->{_left_node} = $newleft;
404 2         3 $$node->{_balance} = $$node->{_balance} - $decrease;
405 2 100       4 if($decrease){
406 1 50       9 if($$node->{_balance}){
407 1         3 $change = $self->rebalance($node);
408             }
409             else{
410 0         0 $change = 1;
411             }
412             }
413 2         4 return ($obj, $$node, $change);
414             }
415             }
416              
417              
418             #
419             # delete_largest
420             #
421             # usage: $tree_object->delete_largest();
422             #
423             sub delete_largest
424             {
425 2     2 0 3 my ($self,
426             $node,
427             $depth) = @_;
428              
429 2 100       4 if(!$node){
430 1         2 $node = \$self->{_node};
431             }
432              
433 2         3 my $node_obj = $$node->{_obj};
434 2         3 my $get_key_func = $self->{fget_key};
435 2         3 my $own_key = $get_key_func->($node_obj);
436 2         2 my $decrease = 0;
437 2         1 my $change = 0;
438              
439 2 100       6 if(!$$node->{_right_node}){
440 1         1 my $obj = $$node->{_obj};
441 1 50 33     6 if(!$$node->{_left_node} && !$depth){
442 0         0 $$node = {
443             _obj => undef,
444             _balance => 0,
445             _right_node => undef,
446             _left_node => undef,
447             };
448 0         0 $change = 1;
449             }
450             else{
451 1 50       2 if($$node->{_left_node}){
452 0         0 $$node = $$node->{_left_node};
453             }
454             else{
455 1         2 $$node = undef;
456             }
457 1 50       2 if($$node){
458 0         0 $$node->{_balance} = 0;
459             }
460 1         2 $change = 1;
461             }
462 1         2 return ($obj, $$node, $change);
463             }
464             else{
465 1         5 my ($obj, $newright, $change) = Tree::AVL::delete_largest($self, \$$node->{_right_node}, 1);
466 1         2 $decrease = 1 * $change;
467 1         2 $$node->{_right_node} = $newright;
468 1         2 $$node->{_balance} = $$node->{_balance} - $decrease;
469 1 50       2 if($decrease){
470 1 50       2 if($$node->{_balance}){
471 1         2 $change = $self->rebalance($node);
472             }
473             else{
474 0         0 $change = 1;
475             }
476             }
477 1         5 return ($obj, $$node, $change);
478             }
479             }
480              
481              
482              
483              
484             #
485             # rebalance
486             #
487             # Determines what sort of, if any, imbalance exists in the subtree
488             # rooted at $node, and calls the correct rotation subroutine.
489             #
490             sub rebalance
491             {
492 11     11 0 12 my ($self, $node) = @_;
493 11         9 my $height_change = 0;
494              
495 11 100       31 if($$node->{_balance} < -1){ # left heavy
    100          
496 1 50       3 if($$node->{_left_node}){
497 1 50       4 if($$node->{_left_node}->{_balance} == 1){ # right heavy
498 1         6 $height_change = $self->double_rotate_right($node);
499             }
500             else{
501 0         0 $height_change = $self->rotate_right($node);
502             }
503             }
504             }
505             elsif($$node->{_balance} > 1){ # right heavy
506 2 50       5 if($$node->{_right_node}){
507 2 100       7 if($$node->{_right_node}->{_balance} == -1){ # left heavy
508 1         7 $height_change = $self->double_rotate_left($node);
509             }
510             else{
511 1         4 $height_change = $self->rotate_left($node);
512             }
513             }
514             }
515 11         18 return $height_change;
516             }
517              
518              
519              
520             #
521             # rotate_right
522             #
523             # A single right-rotation. Yes, this is *very* similar code for right and left operations,
524             # but these subroutines have not been merged to one in the interest of clarity. After all, according to
525             # Abelson and Sussman, programs are for humans to read above all, and only incidentally for machines
526             # to run. Not that this code is as readable as it could be, of course.
527             #
528             sub rotate_right
529             {
530 2     2 0 4 my ($self, $node) = @_;
531 2         2 my $height_change = 0;
532 2         3 my $lr_grandchild;
533             my $lnode;
534              
535 2 50       4 if($$node->{_left_node}){
536 2         4 $lnode = $$node->{_left_node};
537             }
538            
539             # determine height_change
540 2 100 66     7 if($$node->{_right_node} && $$node->{_left_node}){
541 1 50       3 $height_change = $$node->{_left_node}->{_balance} == 0 ? 0 : 1;
542             }
543             else{
544 1         2 $height_change = 1;
545             }
546            
547             # do the rotation
548 2 50       5 if(defined($$node->{_left_node})){
549 2 50       5 if($$node->{_left_node}->{_right_node}){
550 0         0 $lr_grandchild = $$node->{_left_node}->{_right_node}; # becomes left child's new right child
551             }
552             }
553 2         3 $$node->{_left_node} = $lr_grandchild;
554 2 50       4 if($lnode){
555 2         2 $lnode->{_right_node} = $$node;
556 2         2 $$node = $lnode;
557             }
558              
559             # update balances
560 2 50       6 if($$node->{_right_node}){
561 2         4 $$node->{_right_node}->{_balance} = $$node->{_right_node}->{_balance} + (1 - min($$node->{_balance}, 0));
562 2         5 $$node->{_balance} = $$node->{_balance} + (1 + max($$node->{_right_node}->{_balance}, 0));
563             }
564              
565 2         3 return $height_change;
566             }
567              
568             #
569             # rotate_left
570             #
571             # A single left-rotation. Yes, this is *very* similar code for right and left operations,
572             # but these subroutines have not been merged to one in the interest of clarity. After all, according to
573             # Abelson and Sussman, programs are for humans to read above all, and only incidentally for machines
574             # to run. Not that this code is as readable as it could be, of course.
575             #
576             sub rotate_left
577             {
578 3     3 0 5 my ($self, $node) = @_;
579 3         2 my $height_change = 0;
580 3         3 my $rl_grandchild;
581             my $rnode;
582            
583 3 50       8 if($$node->{_right_node}){
584 3         3 $rnode = $$node->{_right_node};
585             }
586              
587             # determine height_change
588 3 100 66     31 if($$node->{_left_node} && $$node->{_right_node}){
589 1 50       4 $height_change = $$node->{_right_node}->{_balance} == 0 ? 0 : 1;
590             }
591             else{
592 2         2 $height_change = 1;
593             }
594              
595 3 50       6 if(defined($$node->{_right_node})){
596 3 100       8 if($$node->{_right_node}->{_left_node}){
597 1         2 $rl_grandchild = $$node->{_right_node}->{_left_node}; # becomes left child's new right child
598             }
599             }
600 3         3 $$node->{_right_node} = $rl_grandchild;
601              
602 3 50       7 if($rnode){
603 3         4 $rnode->{_left_node} = $$node;
604 3         34 $$node = $rnode;
605             }
606              
607             # update balances
608 3 50       8 if($$node->{_left_node}){
609 3         10 $$node->{_left_node}->{_balance} = $$node->{_left_node}->{_balance} - (1 + max($$node->{_balance}, 0));
610 3         7 $$node->{_balance} = $$node->{_balance} - (1 - min($$node->{_left_node}->{_balance}, 0));
611             }
612              
613 3         4 return $height_change;
614             }
615              
616              
617             #
618             # double_rotate_right
619             #
620             # A double right-rotation. Yes, this is *very* similar code for right and left operations,
621             # but these subroutines have not been merged to one in the interest of clarity. After all, according to
622             # Abelson and Sussman, programs are for humans to read above all, and only incidentally for machines
623             # to run. Not that this code is as readable as it could be, of course.
624             #
625             sub double_rotate_right
626             {
627 1     1 0 1 my ($self, $node) = @_;
628            
629 1         2 my $old_balance = $$node->{_balance};
630 1         2 my $old_l_balance = 0;
631 1         1 my $old_r_balance = 0;
632            
633 1 50       12 if($$node->{_left_node}){
634 1         3 $old_l_balance = $$node->{_left_node}->{_balance};
635             }
636 1 50       2 if($$node->{_right_node}){
637 0         0 $old_r_balance = $$node->{_right_node}->{_balance};
638             }
639              
640 1 50       3 if($$node->{_left_node}){
641 1         3 $self->rotate_left(\$$node->{_left_node});
642             }
643              
644 1         4 $self->rotate_right($node);
645            
646 1 50       57 if($$node->{_left_node}){
647 1         3 $$node->{_left_node}->{_balance} = -1 * max($old_r_balance, 0);
648             }
649 1 50       4 if($$node->{_right_node}){
650 1         2 $$node->{_right_node}->{_balance} = -1 * min($old_r_balance, 0);
651             }
652 1         2 $$node->{_balance} = 0;
653            
654            
655 1         2 return 1;
656             }
657              
658              
659             #
660             # double_rotate_left
661             #
662             # A double left-rotation. Yes, this is *very* similar code for right and left operations,
663             # but these subroutines have not been merged to one in the interest of clarity. After all, according to
664             # Abelson and Sussman, programs are for humans to read above all, and only incidentally for machines
665             # to run. Not that this code is as readable as it could be, of course.
666             #
667             sub double_rotate_left
668             {
669 1     1 0 2 my ($self, $node) = @_;
670 1         2 my $old_balance = $$node->{_balance};
671 1         1 my $old_l_balance = 0;
672 1         1 my $old_r_balance = 0;
673            
674 1 50       3 if($$node->{_left_node}){
675 1         2 $old_l_balance = $$node->{_left_node}->{_balance};
676             }
677 1 50       3 if($$node->{_right_node}){
678 1         1 $old_r_balance = $$node->{_right_node}->{_balance};
679             }
680            
681 1 50       3 if($$node->{_right_node}){
682 1         5 $self->rotate_right(\$$node->{_right_node});
683             }
684 1         2 $self->rotate_left($node);
685            
686 1 50       24 if($$node->{_left_node}){
687 1         3 $$node->{_left_node}->{_balance} = -1 * max($old_l_balance, 0);
688             }
689 1 50       3 if($$node->{_right_node}){
690 1         3 $$node->{_right_node}->{_balance} = -1 * min($old_l_balance, 0);
691             }
692 1         1 $$node->{_balance} = 0;
693            
694 1         2 return 1;
695             }
696              
697              
698             sub is_empty{
699 1     1 0 2 my ($self, $node) = @_;
700            
701 1 50       3 if(!$node){
702 0         0 $node = $self->{_node};
703             }
704            
705 1 50       3 if(!defined($node->{_obj})){
706 0         0 return 1;
707             }
708 1         2 return 0;
709             }
710              
711              
712              
713              
714             #
715             # smallest
716             #
717             # usage:
718             #
719             # my $largest_obj = $avltree->smallest()
720             #
721             # Returns the smallest-valued object in the tree
722             #
723             sub smallest
724             {
725 0     0 1 0 my ($self, $node) = @_;
726 0         0 return $self->extremum($node, 0);
727             }
728              
729             #
730             # largest
731             #
732             # usage:
733             #
734             # my $largest_obj = $avltree->largest()
735             #
736             # Returns the largest-valued object in the tree
737             #
738             # Fixed 07/11/09 for version 1.05 by Robert Lehr:
739             # recursive invocation was called incorrectly
740             #
741             sub largest
742             {
743 0     0 1 0 my ($self, $node) = @_;
744 0         0 return $self->extremum($node, 1);
745             }
746              
747              
748              
749             sub extremum
750             {
751 0     0 0 0 my ($self, $node, $which_extreme) = @_;
752            
753 0         0 my $node_dir;
754            
755 0 0       0 if($which_extreme eq 0){
    0          
756 0         0 $node_dir = "_left_node";
757             }
758             elsif($which_extreme == 1){
759 0         0 $node_dir = "_right_node";
760             }
761             else{
762 0         0 croak("Bad extreme type supplied: must be 0 or 1\n");
763             }
764              
765 0 0       0 if(!$node){
766 0         0 $node = $self->{_node};
767             }
768 0         0 my $obj = $node->{_obj};
769 0         0 my $next_node = $node->{$node_dir};
770 0 0       0 if(!$next_node){
771 0         0 return $obj;
772             }
773             else{
774 0         0 my $obj = Tree::AVL::extremum($self, $next_node, $which_extreme);
775 0         0 return $obj;
776             }
777             }
778              
779              
780              
781             #
782             # pop_largest
783             #
784             # usage:
785             #
786             # my $largest_obj = $avltree->pop_largest()
787             #
788             # Removes and returns the largest-valued object in the tree
789             #
790             sub pop_largest
791             {
792 1     1 1 2 my ($self) = @_;
793 1         3 my ($obj) = $self->delete_largest();
794 1         5 return $obj;
795             }
796              
797             #
798             # pop_smallest
799             #
800             # usage:
801             #
802             # my $largest_obj = $avltree->pop_smallest()
803             #
804             # Removes and returns the smallest-valued object in the tree
805             #
806             sub pop_smallest
807             {
808 1     1 1 3 my ($self) = @_;
809 1         3 my ($obj) = $self->delete_smallest();
810 1         3 return $obj;
811             }
812              
813              
814             sub get_key
815             {
816 0     0 0 0 my ($self, $node) = @_;
817 0         0 my $get_key_func = $self->{fget_key};
818 0         0 my $obj = $node->{_obj};
819 0         0 my $key = $get_key_func->($obj);
820 0         0 return $key;
821             }
822              
823              
824             sub get_data
825             {
826 0     0 0 0 my ($self, $node) = @_;
827 0         0 my $get_data_func = $self->{fget_data};
828 0         0 my $obj = $node->{_obj};
829 0         0 my $data = $get_data_func->($obj);
830 0         0 return $data;
831             }
832              
833             sub get_height
834             {
835 7     7 0 677 my ($self, $node) = @_;
836            
837 7         10 my $depth_left = 0;
838 7         3 my $depth_right = 0;
839              
840 7 100       13 if(!$node){
841 2         3 $node = $self->{_node};
842             }
843              
844 7 100 100     25 if(!$node->{_left_node} && !$node->{_right_node}){
845 4         9 return 0;
846             }
847             else
848             {
849 3 100       6 if($node->{_left_node}){
850 2         13 $depth_left = 1 + $self->get_height($node->{_left_node});
851             }
852 3 50       6 if($node->{_right_node}){
853 3         5 $depth_right = 1 + $self->get_height($node->{_right_node});
854             }
855              
856 3 100       7 return $depth_left < $depth_right ? $depth_right : $depth_left;
857             }
858             }
859              
860              
861             #
862             # lookup
863             #
864             # usage: $data = $tree_ref->lookup($object)
865             #
866             sub lookup
867             {
868 0     0 1 0 my ($self,
869             $object,
870             $cmpfunc) = @_;
871              
872 0         0 my $node = $self->{_node};
873              
874 0 0       0 if(!defined($node->{_obj})){ # no root data yet
875 0         0 return;
876             }
877             else{
878            
879 0         0 while($node){
880 0         0 my $node_obj = $node->{_obj};
881 0         0 my $get_key_func = $self->{fget_key};
882 0         0 my $key = $get_key_func->($node_obj);
883              
884 0 0       0 if(!$cmpfunc){
885 0         0 $cmpfunc = $self->{fcompare};
886             }
887 0         0 my $result = $cmpfunc->($node_obj, $object);
888 0 0       0 if($result == 0){ # element is already in tree- return the key.
    0          
889 0         0 return $key;
890             }
891             elsif($result < 0){ # look into right subtree
892 0         0 $node = $node->{_right_node};
893             }
894             else{ # look into left subtree
895 0         0 $node = $node->{_left_node};
896             }
897             } # end while
898 0         0 return;
899             } # end else
900             }
901              
902              
903             #
904             # lookup_obj
905             #
906             # usage: $object = $tree_ref->lookup($object)
907             #
908             sub lookup_obj
909             {
910 0     0 1 0 my ($self,
911             $object,
912             $cmpfunc) = @_;
913              
914 0         0 my $node = $self->{_node};
915              
916 0 0       0 if(!defined($node->{_obj})) # no root data yet
917             {
918 0         0 return;
919             }
920             else
921             {
922 0         0 while($node){
923 0         0 my $node_obj = $node->{_obj};
924            
925 0 0       0 if(!$cmpfunc){
926 0         0 $cmpfunc = $self->{fcompare};
927             }
928 0         0 my $result = $cmpfunc->($node_obj, $object);
929 0 0       0 if($result == 0){ # element is already in tree- return the key.
    0          
930 0         0 return $node_obj;
931             }
932             elsif($result < 0){ # look into right subtree
933 0         0 $node = $node->{_right_node};
934             }
935             else{ # look into left subtree
936 0         0 $node = $node->{_left_node};
937             }
938             } # end while
939 0         0 return;
940             } # end else
941             }
942              
943              
944             #
945             # lookup_node
946             #
947             # usage: $node_hash = $tree_ref->lookup($object)
948             #
949             sub lookup_node
950             {
951 0     0 1 0 my ($self,
952             $object,
953             $cmpfunc) = @_;
954              
955 0         0 my $node = $self->{_node};
956              
957 0 0       0 if(!defined($node->{_obj})) # no root data yet
958             {
959 0         0 return;
960             }
961             else
962             {
963 0         0 while($node){
964 0         0 my $node_obj = $node->{_obj};
965            
966 0 0       0 if(!$cmpfunc){
967 0         0 $cmpfunc = $self->{fcompare};
968             }
969 0         0 my $result = $cmpfunc->($node_obj, $object);
970 0 0       0 if($result == 0){ # element is in tree- return the node.
    0          
971 0         0 return $node;
972             }
973             elsif($result < 0){ # look into right subtree
974 0         0 $node = $node->{_right_node};
975             }
976             else{ # look into left subtree
977 0         0 $node = $node->{_left_node};
978             }
979             } # end while
980 0         0 return;
981             } # end else
982             }
983              
984              
985              
986             #
987             # acc_lookup
988             #
989             # usage: $tree_ref->acc_lookup($object, $partial_cmp_func, $exact_cmp_func)
990             #
991             # accumulative lookup, returns a list of all
992             # items whose keys satisfy the match function for the key for $object.
993             #
994             # For example, if used with a relaxed compare function such as:
995             #
996             # $word->compare_up_to($arg_word);
997             #
998             # which returns true if the argument word is a proper 'superstring' of $word
999             # (meaning that it contains $word followed by one or more characters)
1000             # this will return a list of all the words that are superstrings of
1001             # $word.
1002             #
1003             sub acc_lookup
1004             {
1005 0     0 1 0 my ($self,
1006             $object,
1007             $partial_cmpfunc, # partial comparison function to use
1008             $exact_cmpfunc, # exact comparison function to use
1009             $node,
1010             $acc_results) = @_;
1011            
1012 0 0       0 if(!$node){
1013 0         0 $node = $self->{_node};
1014             }
1015            
1016             # the list of accumulated results
1017 0 0       0 if(!$acc_results){
1018 0         0 $acc_results = ();
1019             }
1020            
1021 0 0 0     0 if(!$partial_cmpfunc || !$exact_cmpfunc){
1022 0         0 return ();
1023             }
1024            
1025 0 0       0 if(!defined($node->{_obj})){ # no root data yet
1026 0         0 return ();
1027             }
1028             else
1029             {
1030 0         0 while($node){
1031 0         0 my $node_obj = $node->{_obj};
1032 0         0 my $get_key_func = $self->{fget_key};
1033 0         0 my $node_key = $get_key_func->($node_obj);
1034 0         0 my $partial_cmp = $partial_cmpfunc->($node_obj, $object);
1035 0         0 my $exact_cmp = $exact_cmpfunc->($node_obj, $object);
1036              
1037 0 0       0 if($partial_cmp == 0){ # found a match on partial cmp
    0          
1038            
1039 0 0       0 if(!$acc_results){
1040 0         0 @$acc_results = ();
1041             }
1042 0         0 push(@$acc_results, $node_key);
1043            
1044 0 0       0 if($exact_cmp == 0){ # any other partial matches will be in right subtree
1045 0         0 $node = $node->{_right_node};
1046             }
1047             else{
1048              
1049 0 0 0     0 if ($node->{_right_node} && $node->{_left_node}){
    0          
    0          
1050 0         0 my $rightnode = $node->{_right_node};
1051 0         0 my $leftnode = $node->{_left_node};
1052            
1053 0         0 return @$acc_results = (Tree::AVL::acc_lookup($self, $object, $partial_cmpfunc,
1054             # do not pass in acc_results here
1055             $exact_cmpfunc, $rightnode),
1056             Tree::AVL::acc_lookup($self, $object, $partial_cmpfunc,
1057             $exact_cmpfunc, $leftnode, \@$acc_results));
1058             }
1059             elsif($node->{_right_node}){
1060 0         0 my $rightnode = $node->{_right_node};
1061 0         0 @$acc_results = (Tree::AVL::acc_lookup($self, $object, $partial_cmpfunc,
1062             $exact_cmpfunc, $rightnode, \@$acc_results));
1063             }
1064             elsif($node->{_left_node}){
1065 0         0 my $leftnode = $node->{_left_node};
1066 0         0 @$acc_results = (Tree::AVL::acc_lookup($self, $object, $partial_cmpfunc,
1067             $exact_cmpfunc, $leftnode, \@$acc_results));
1068             }
1069 0         0 return @$acc_results;
1070             }
1071             }
1072             elsif($partial_cmp < 0){ # look into right subtree
1073 0         0 $node = $node->{_right_node};
1074             }
1075             else{ # look into left subtree
1076 0         0 $node = $node->{_left_node};
1077             }
1078             } # end while
1079 0 0       0 if(scalar @{$acc_results} > 0){
  0         0  
1080 0         0 return @$acc_results;
1081             }
1082 0         0 return;
1083             } # end else determine whether need to look into left or right subtree
1084             }
1085              
1086              
1087             #
1088             # acc_lookup_memo
1089             #
1090             # memoized call to acc_lookup
1091             #
1092             sub acc_lookup_memo
1093             {
1094 0     0 0 0 my ($self,
1095             $object,
1096             $partial_cmpfunc, # partial comparison function to use
1097             $exact_cmpfunc # exact comparison function to use
1098             ) = @_;
1099            
1100 0         0 my $get_key_func = $self->{fget_key};
1101            
1102 0         0 my $obj_key = $get_key_func->($object);
1103 0         0 my $acc_lookup_hash_key = $obj_key . $partial_cmpfunc . $exact_cmpfunc;
1104            
1105            
1106 0 0       0 if($self->{acc_lookup_hash}->{$acc_lookup_hash_key}){
1107 0         0 my $list = $self->{acc_lookup_hash}->{$acc_lookup_hash_key};
1108 0         0 return @$list;
1109             }
1110             else{
1111 0         0 my @results = $self->acc_lookup($object, $partial_cmpfunc, $exact_cmpfunc);
1112 0         0 $self->{acc_lookup_hash}->{$acc_lookup_hash_key} = \@results;
1113 0         0 return @results;
1114             }
1115             }
1116              
1117              
1118             #
1119             # get_list_recursive
1120             #
1121             # usage: @list = $tree_ref->get_list_recursive()
1122             #
1123             # returns an array (list) containing all elements in the tree (in-order).
1124             #
1125             sub get_list_recursive
1126             {
1127 0     0 0 0 my ($self, $node, $lst) = @_;
1128              
1129 0 0       0 if(!$node){
1130 0         0 $node = $self->{_node};
1131             }
1132 0 0       0 if(!$lst){
1133 0         0 $lst = [];
1134             }
1135 0 0       0 if($node->{_left_node}){
1136 0         0 @$lst = Tree::AVL::get_list_recursive($self, $node->{_left_node}, $lst);
1137             }
1138 0         0 my $obj = $node->{_obj};
1139 0 0       0 if($obj){
1140 0         0 push(@$lst, $obj);
1141             }
1142 0 0       0 if($node->{_right_node}){
1143 0         0 Tree::AVL::get_list_recursive($self, $node->{_right_node}, $lst);
1144             }
1145              
1146 0         0 return @$lst;
1147             }
1148              
1149              
1150             #
1151             # get_list
1152             #
1153             # usage: @list = $tree_ref->get_list()
1154             #
1155             # returns an array (list) containing all elements in the tree (in-order).
1156             #
1157             sub get_list
1158             {
1159 0     0 0 0 my ($self) = @_;
1160              
1161 0         0 my $i = 0;
1162 0         0 my @stack;
1163 0         0 my $node = $self->{_node};
1164              
1165 0         0 my @objs = ();
1166              
1167 0         0 while(1){
1168 0         0 while($node){
1169 0         0 $stack[$i] = $node;
1170 0         0 $i++;
1171 0         0 $node = $node->{_left_node};
1172             }
1173 0 0       0 if($i == 0){
1174 0         0 last;
1175             }
1176 0         0 --$i;
1177 0 0       0 if(defined($stack[$i]->{_obj})){
1178 0         0 push(@objs, $stack[$i]->{_obj});
1179             }
1180 0         0 $node = $stack[$i];
1181              
1182 0         0 $node = $node->{_right_node};
1183             }
1184              
1185 0         0 return @objs;
1186             }
1187              
1188             #
1189             # get_root
1190             #
1191             # returns reference to object at root node.
1192             #
1193             #
1194             sub get_root
1195             {
1196 1     1 1 2 my ($self) = @_;
1197 1         3 return $self->{_node}->{_obj};
1198             }
1199              
1200             #
1201             # get_size
1202             #
1203             # returns number of objects in the tree
1204             #
1205             #
1206             sub get_size
1207             {
1208 0     0 0 0 my ($self) = @_;
1209 0         0 my @list = $self->get_list();
1210 0         0 my $size = @list;
1211            
1212 0         0 return $size;
1213             }
1214              
1215             #
1216             # iterator
1217             #
1218             # usage: my $it = $tree_ref->iterator(">") # high-to-low
1219             # my $it = $tree_ref->iterator("<") # low-to-high
1220             #
1221             # returns an iterator over elements in the tree (in order specified).
1222             #
1223             sub iterator
1224             {
1225 2     2 1 3 my ($self, $order) = @_;
1226            
1227 2         3 my $first_dir;
1228             my $second_dir;
1229            
1230 2 100       4 if(!$order){ $order = "<"; }
  1         1  
1231              
1232 2 100       5 if($order eq ">"){ # high to low
1233 1         2 $first_dir = "_right_node";
1234 1         1 $second_dir = "_left_node";
1235             }
1236             else{ # low to high (default)
1237 1         2 $first_dir = "_left_node";
1238 1         1 $second_dir = "_right_node";
1239             }
1240              
1241 2         3 my @stack;
1242 2         2 my $i = 0;
1243 2         3 my $node = $self->{_node};
1244            
1245             return sub{
1246 2     2   846 while(1){
1247 2         6 while($node){
1248 5         4 $stack[$i] = $node;
1249 5         4 $i++;
1250 5         11 $node = $node->{$first_dir};
1251             }
1252 2 50       4 if($i == 0){
1253 0         0 last;
1254             }
1255 2         2 --$i;
1256 2         4 my $obj = $stack[$i]->{_obj};
1257 2         2 $node = $stack[$i];
1258 2         3 $node = $node->{$second_dir};
1259 2         7 return $obj;
1260             }
1261 0         0 return;
1262             }
1263 2         11 }
1264              
1265              
1266             sub get_keys_recursive
1267             {
1268 0     0 0 0 my ($self, $node) = @_;
1269 0         0 my @keys;
1270              
1271 0 0       0 if(!$node){
1272 0         0 $node = $self->{_node};
1273             }
1274            
1275 0 0       0 if($node->{_left_node}){
1276 0         0 push(@keys, Tree::AVL::get_keys_recursive($self, $node->{_left_node}));
1277             }
1278            
1279 0         0 push(@keys, $self->get_key($node));
1280            
1281 0 0       0 if($node->{_right_node}){
1282 0         0 push(@keys, Tree::AVL::get_keys_recursive($self, $node->{_right_node}));
1283             }
1284 0         0 return @keys;
1285             }
1286              
1287              
1288              
1289             sub get_keys
1290             {
1291 0     0 0 0 my ($self) = @_;
1292 0         0 my $node = $self->{_node};
1293 0         0 my @stack;
1294 0         0 my $i = 0;
1295 0         0 my @keys;
1296              
1297 0         0 while(1){
1298 0         0 while($node){
1299 0         0 $stack[$i] = $node;
1300 0         0 $i++;
1301 0         0 $node = $node->{_left_node};
1302             }
1303 0 0       0 if($i == 0){
1304 0         0 last;
1305             }
1306 0         0 --$i;
1307 0         0 push(@keys, $self->get_key($stack[$i]));
1308 0         0 $node = $stack[$i];
1309              
1310 0         0 $node = $node->{_right_node};
1311             }
1312 0         0 return @keys;
1313             }
1314              
1315              
1316             sub get_keys_iterator
1317             {
1318 0     0 0 0 my ($self) = @_;
1319 0         0 my @stack;
1320 0         0 my $i = 0;
1321 0         0 my $node = $self->{_node};
1322              
1323             return sub{
1324            
1325 0     0   0 while(1){
1326 0         0 while($node){
1327 0         0 $stack[$i] = $node;
1328 0         0 $i++;
1329 0         0 $node = $node->{_left_node};
1330             }
1331 0 0       0 if($i == 0){
1332 0         0 last;
1333             }
1334 0         0 --$i;
1335 0         0 my $key = $self->get_key($stack[$i]);
1336 0         0 $node = $stack[$i];
1337 0         0 $node = $node->{_right_node};
1338 0         0 return $key;
1339             }
1340            
1341 0         0 return;
1342             }
1343 0         0 }
1344              
1345              
1346             ################################################################################
1347             #
1348             # Printing functions
1349             #
1350             #
1351             ################################################################################
1352             sub print
1353             {
1354 0     0 1 0 my ($self, $char, $o_char, $node, $depth) = @_;
1355              
1356 0 0 0     0 if(!$node && !defined($depth)){
1357 0         0 $node = $self->{_node};
1358             }
1359 0 0       0 if(!$depth){ $depth = 0; }
  0         0  
1360 0 0       0 if(!$o_char){
1361 0         0 $o_char = $char;
1362             }
1363            
1364 0         0 my $key = $self->get_key($node);
1365 0         0 my $data = $self->get_data($node);
1366              
1367 0 0       0 if(!defined($self->{_node}->{_obj})){
1368 0         0 print "tree is empty.\n";
1369 0         0 return;
1370             }
1371              
1372 0 0       0 if(!defined($key)){
1373 0         0 croak "get_key() function provided to Tree::AVL object returned a null value\n";
1374             }
1375 0 0       0 if(!defined($data)){
1376 0         0 $data = "";
1377             }
1378              
1379 0         0 print $char . $key . ": " . $data;
1380 0         0 print ": height: " . $self->get_height($node) . ": balance: " . $node->{_balance} . "\n";
1381              
1382 0 0       0 if($node->{_left_node}){
1383 0         0 my $leftnode = $node->{_left_node};
1384 0         0 Tree::AVL::print($self, $char . $o_char, $o_char, $leftnode, $depth+1);
1385             }
1386 0 0       0 if($node->{_right_node}){
1387 0         0 my $rightnode = $node->{_right_node};
1388 0         0 Tree::AVL::print($self, $char . $o_char, $o_char, $rightnode, $depth+1);
1389             }
1390             }
1391              
1392              
1393             sub print_node
1394             {
1395 0     0 0 0 my ($self, $node, $char, $o_char) = @_;
1396              
1397 0 0       0 if(!$o_char){
1398 0         0 $o_char = $char;
1399             }
1400              
1401 0         0 my $key = $self->get_key($node);
1402 0         0 my $data = $self->get_data($node);
1403            
1404 0 0       0 if(!defined($key)){
1405 0         0 croak "get_key() function provided to Tree::AVL object returned a null value\n";
1406             }
1407 0 0       0 if(!defined($data)){
1408 0         0 $data = "";
1409             }
1410              
1411              
1412 0         0 print $char . $key . ": " . $data . ": balance: " . $node->{_balance} . "\n";
1413 0 0       0 if($node->{_left_node}){
1414 0         0 my $leftnode = $node->{_left_node};
1415 0         0 Tree::AVL::print_node($self, $leftnode, $char . $o_char, $o_char);
1416             }
1417 0 0       0 if($node->{_right_node}){
1418 0         0 my $rightnode = $node->{_right_node};
1419 0         0 Tree::AVL::print_node($self, $rightnode, $char . $o_char, $o_char);
1420             }
1421             }
1422              
1423             sub print_iterative
1424             {
1425 0     0 0 0 my ($self) = @_;
1426 0         0 my @stack;
1427              
1428 0         0 my $node = $self->{_node};
1429              
1430 0         0 my $i = 0;
1431              
1432 0         0 while(1){
1433 0         0 while($node){
1434 0         0 $stack[$i] = $node;
1435 0         0 $i++;
1436 0         0 $node = $node->{_left_node};
1437             }
1438              
1439 0 0       0 if($i == 0){
1440 0         0 last;
1441             }
1442 0         0 --$i;
1443 0         0 print $self->get_key($stack[$i]) . "\n";
1444 0         0 $node = $stack[$i];
1445              
1446 0         0 $node = $node->{_right_node};
1447             }
1448             }
1449              
1450             #
1451             # default_cmp_func
1452             #
1453             # default comparison function to use in case none is supplied.
1454             # uses lexical comparator.
1455             #
1456             sub default_cmp_func
1457             {
1458 15     15 0 40 my ($obj1, $obj2) = @_;
1459              
1460 15 100       30 if($obj1 lt $obj2){
    100          
1461 9         15 return -1;
1462             }
1463             elsif($obj1 gt $obj2){
1464 4         7 return 1;
1465             }
1466 2         3 return 0;
1467             }
1468              
1469             sub min
1470             {
1471 7     7 0 6 my ($a, $b) = @_;
1472 7 100       17 return $a < $b ? $a : $b;
1473             }
1474              
1475              
1476             sub max
1477             {
1478 7     7 0 7 my ($a, $b) = @_;
1479 7 50       21 return $a < $b ? $b : $a;
1480             }
1481              
1482              
1483             1;
1484             __END__