File Coverage

blib/lib/Tree/RedBlack.pm
Criterion Covered Total %
statement 141 186 75.8
branch 65 94 69.1
condition 17 36 47.2
subroutine 16 16 100.0
pod 9 12 75.0
total 248 344 72.0


line stmt bran cond sub pod time code
1             package Tree::RedBlack;
2              
3 1     1   27676 use strict;
  1         2  
  1         55  
4 1     1   558 use Tree::RedBlack::Node;
  1         3  
  1         32  
5 1     1   7 use vars qw($VERSION);
  1         2  
  1         3364  
6             $VERSION = '0.5';
7              
8             =head1 NAME
9              
10             Tree::RedBlack - Perl implementation of Red/Black tree, a type of balanced tree.
11              
12             =head1 SYNOPSIS
13              
14             use Tree::RedBlack;
15            
16             my $t = new Tree::RedBlack;
17             $t->insert(3, 'cat');
18             $t->insert(4, 'dog');
19             my $v = $t->find(4);
20             my $min = $t->min;
21             my $max = $t->max;
22             $t->delete(3);
23             $t->print;
24              
25             =head1 DESCRIPTION
26              
27             This is a perl implementation of the Red/Black tree algorithm found in the book
28             "Algorithms", by Cormen, Leiserson & Rivest (more commonly known as "CLR" or
29             "The White Book"). A Red/Black tree is a binary tree which remains "balanced"-
30             that is, the longest length from root to a node is at most one more than the
31             shortest such length. It is fairly efficient; no operation takes more than
32             O(lg(n)) time.
33              
34             A Tree::RedBlack object supports the following methods:
35              
36             =over 4
37              
38             =item new ()
39              
40             Creates a new RedBlack tree object.
41              
42             =item root ()
43              
44             Returns the root node of the tree. Note that this will either be undef if no
45             nodes have been added to the tree, or a Tree::RedBlack::Node object. See the
46             L manual page for details on the Node object.
47              
48             =item cmp (&)
49              
50             Use this method to set a comparator subroutine. The tree defaults to lexical
51             comparisons. This subroutine should be just like a comparator subroutine to
52             sort, except that it doesn't do the $a, $b trick; the two elements to compare
53             will just be the first two items on the stack.
54              
55             =item insert ($;$)
56              
57             Adds a new node to the tree. The first argument is the key of the node, the
58             second is its value. If a node with that key already exists, its value is
59             replaced with the given value and the old value is returned. Otherwise, undef
60             is returned.
61              
62             =item delete ($)
63              
64             The argument should be either a node object to delete or the key of a node
65             object to delete. WARNING!!! THIS STILL HAS BUGS!!!
66              
67             =item find ($)
68              
69             Searches the tree to find the node with the given key. Returns the value of
70             that node, or undef if a node with that key isn't found. Note, in particular,
71             that you can't tell the difference between finding a node with value undef and
72             not finding a node at all. If you want to determine if a node with a given key
73             exists, use the node method, below.
74              
75             =item node ($)
76              
77             Searches the tree to find the node with the given key. Returns that node
78             object if it is found, undef otherwise. The node object is a
79             Tree::RedBlack::Node object.
80              
81             =item min ()
82              
83             Returns the node with the minimal key.
84              
85             =item max ()
86              
87             Returns the node with the maximal key.
88              
89             =back
90              
91             =head1 AUTHOR
92              
93             Benjamin Holzman
94              
95             =head1 SEE ALSO
96              
97             Tree::RedBlack::Node
98              
99             =cut
100              
101             sub new {
102 2     2 1 15 my $type = shift;
103 2         14 return bless {'null' => Tree::RedBlack::Node::->new,
104             'root' => undef}, $type;
105             }
106              
107 2 50   2   110 sub DESTROY { if ($_[0]->{'root'}) { $_[0]->{'root'}->DESTROY } }
  2         7  
108              
109             sub root {
110 1     1 1 465 my $this = shift;
111 1         12 return $this->{'root'};
112             }
113              
114             sub cmp {
115 1     1 1 7 my($this, $cr) = @_;
116 1         3 $this->{'cmp'} = $cr;
117             }
118              
119             sub insert {
120 8     8 1 17 my($this, $key, $value) = @_;
121 8         12 my $cmp = $this->{'cmp'};
122 8         9 my $node = $this->{'root'};
123 8         9 my $parent;
124 8         15 while ($node) {
125 10         8 $parent = $node;
126 10 100       33 if ($cmp ? $cmp->($key, $node->key) < 0 : $key lt $node->key) {
    100          
127 3         8 $node = $node->left;
128             } else {
129 7         569 $node = $node->right;
130             }
131             }
132 8 100       13 if ($parent) {
133             # Handle case of inserting node with duplicate key.
134 6 100       17 if ($cmp ? $cmp->($parent->key, $key) == 0 : $parent->key eq $key) {
    100          
135 1         3 my $val = $parent->val;
136 1         28 $parent->val($value);
137 1         3 return $val;
138             }
139 5         15 $node = $parent->new($key, $value);
140 5 100       18 if ($this->{'cmp'} ? $this->{'cmp'}->($key, $parent->key) < 0
    100          
141             : $key lt $parent->key) {
142 2         10 $parent->left($node);
143             } else {
144 3         7 $parent->right($node);
145             }
146             } else {
147 2         8 $this->{'root'} = $node = Tree::RedBlack::Node::->new($key, $value);
148             }
149 7         21 $node->color(1);
150 7   100     37 while ($node != $this->{'root'} && $node->parent->color) {
151 3 100 100     10 if (defined $node->parent->parent->left && $node->parent == $node->parent->parent->left) {
152 1         3 my $uncle = $node->parent->parent->right;
153 1 50 33     4 if ($uncle && $uncle->color) {
154 0         0 $node->parent->color(0);
155 0         0 $uncle->color(0);
156 0         0 $node->parent->parent->color(1);
157 0         0 $node = $node->parent->parent;
158             } else {
159 1 50       3 if ($node == $node->parent->right) {
160 1         2 $node = $node->parent;
161 1         2 $this->left_rotate($node);
162             }
163 1         4 $node->parent->color(0);
164 1         3 $node->parent->parent->color(1);
165 1         3 $this->right_rotate($node->parent->parent);
166             }
167             } else {
168 2         5 my $uncle = $node->parent->parent->left;
169 2 100 66     8 if ($uncle && $uncle->color) {
170 1         4 $node->parent->color(0);
171 1         3 $uncle->color(0);
172 1         3 $node->parent->parent->color(1);
173 1         3 $node = $node->parent->parent;
174             } else {
175 1 50 33     3 if (defined $node->parent->left && $node == $node->parent->left) {
176 0         0 $node = $node->parent;
177 0         0 $this->right_rotate($node);
178             }
179 1         2 $node->parent->color(0);
180 1         3 $node->parent->parent->color(1);
181 1         4 $this->left_rotate($node->parent->parent);
182             }
183             }
184             }
185 7         21 $this->{'root'}->color(0);
186 7         12 return;
187             }
188              
189             sub left_rotate {
190 3     3 0 4 my($this, $node) = @_;
191 3         8 my $child = $node->right;
192 3         7 $node->right($child->left);
193 3 100       8 if ($child->left) {
194 1         4 $child->left->parent($node);
195             }
196 3         8 $child->parent($node->parent);
197 3 100       8 if ($node->parent) {
198 1 50       3 if ($node == $node->parent->left) {
199 1         2 $node->parent->left($child);
200             } else {
201 0         0 $node->parent->right($child);
202             }
203             } else {
204 2         4 $this->{'root'} = $child;
205             }
206 3         8 $child->left($node);
207 3         7 $node->parent($child);
208             }
209              
210             sub right_rotate {
211 1     1 0 2 my($this, $node) = @_;
212 1         2 my $child = $node->left;
213 1         3 $node->left($child->right);
214 1 50       2 if ($child->right) {
215 0         0 $child->right->parent($node);
216             }
217 1         4 $child->parent($node->parent);
218 1 50       3 if ($node->parent) {
219 1 50       3 if ($node == $node->parent->right) {
220 1         3 $node->parent->right($child);
221             } else {
222 0         0 $node->parent->left($child);
223             }
224             } else {
225 0         0 $this->{'root'} = $child;
226             }
227 1         3 $child->right($node);
228 1         4 $node->parent($child);
229             }
230              
231             sub delete {
232 1     1 1 2 my($this, $node_or_key) = @_;
233 1         3 my $node;
234 1 50 33     5 if (ref $node_or_key && $node_or_key->isa('Tree::RedBlack::Node')) {
235 0         0 $node = $node_or_key;
236             } else {
237 1 50       4 $node = $this->node($node_or_key) or return;
238             }
239 1         1 my($successor, $successor_child);
240 1 50 33     4 if (!($node->left && $node->right)) {
241 1         1 $successor = $node;
242             } else {
243 0         0 $successor = $node->successor;
244             }
245 1 50       13 if ($successor->left) {
246 0         0 $successor_child = $successor->left;
247             } else {
248 1   33     4 $successor_child = $successor->right || $this->{'null'};
249             }
250 1         4 $successor_child->parent($successor->parent);
251 1 50 33     7 if (!$successor_child || !$successor_child->parent) {
    50          
252 0         0 $this->{'root'} = $successor_child;
253             } elsif ($successor == $successor->parent->left) {
254 1         4 $successor->parent->left($successor_child);
255             } else {
256 0         0 $successor->parent->right($successor_child);
257             }
258 1 50       4 if ($successor != $node) {
259 0         0 $node->key($successor->key);
260 0         0 $node->val($successor->val);
261             }
262 1 50       5 if (!$successor->color) {
263 1         6 $this->delete_fixup($successor_child);
264             }
265 1 50       4 if (!$successor_child->parent) {
266 0         0 $this->{'root'} = undef;
267             }
268 1         6 $successor;
269             }
270              
271             sub delete_fixup {
272 1     1 0 3 my($this, $x) = @_;
273 1   66     7 while ($x != $this->{'root'} && !$x->color) {
274 1 50       4 if ($x == $x->parent->left) {
275 1         4 my $w = $x->parent->right;
276 1 50       4 if ($w->color) {
277 0         0 $w->color(0);
278 0         0 $x->parent->color(1);
279 0         0 $this->left_rotate($x->parent);
280             }
281 1 50 33     5 if (!$w->left->color && !$w->right->color) {
282 0         0 $w->color(1);
283 0         0 $x = $x->parent;
284             } else {
285 1 50       4 if (!$w->right->color) {
286 0         0 $w->left->color(0);
287 0         0 $w->color(1);
288 0         0 $this->right_rotate($w);
289 0         0 $w = $x->parent->right;
290             }
291 1         5 $w->color($x->parent->color);
292 1         5 $x->parent->color(0);
293 1         4 $w->right->color(0);
294 1         4 $this->left_rotate($x->parent);
295 1         7 $x = $this->{'root'};
296             }
297             } else {
298 0         0 my $w = $x->parent->left;
299 0 0       0 if ($w->color) {
300 0         0 $w->color(0);
301 0         0 $x->parent->color(1);
302 0         0 $this->right_rotate($x->parent);
303             }
304 0 0 0     0 if (!$w->left->color && !$w->right->color) {
305 0         0 $w->color(1);
306 0         0 $x = $x->parent;
307             } else {
308 0 0       0 if (!$w->left->color) {
309 0         0 $w->right->color(0);
310 0         0 $w->color(1);
311 0         0 $this->left_rotate($w);
312 0         0 $w = $x->parent->left;
313             }
314 0         0 $w->color($x->parent->color);
315 0         0 $x->parent->color(0);
316 0         0 $w->left->color(0);
317 0         0 $this->right_rotate($x->parent);
318 0         0 $x = $this->{'root'};
319             }
320             }
321             }
322 1         44 $x->color(0);
323             }
324              
325             sub min {
326 6     6 1 10 my $this = shift;
327 6 100       15 if ($this->{'root'}) {
328 5 100       19 if ($this->{'root'}->left) {
329 4         17 return $this->{'root'}->left->min;
330             } else {
331 1         5 return $this->{'root'};
332             }
333             }
334 1         5 return;
335             }
336              
337             sub max {
338 6     6 1 13 my $this = shift;
339 6 100       16 if ($this->{'root'}) {
340 5 100       16 if ($this->{'root'}->right) {
341 2         6 return $this->{'root'}->right->max;
342             } else {
343 3         12 return $this->{'root'};
344             }
345             }
346 1         4 return;
347             }
348              
349             sub find {
350 6     6 1 12 my($this, $key) = @_;
351 6         10 my $cmp = $this->{'cmp'};
352 6         10 my $node = $this->{'root'};
353 6         16 while ($node) {
354 8 50       29 if ($cmp ? $cmp->($key, $node->key) == 0 : $key eq $node->key) {
    50          
    100          
    100          
355 4         12 return $node->val;
356             } elsif ($cmp ? $cmp->($key, $node->key) < 0 : $key lt $node->key) {
357 1         4 $node = $node->left;
358             } else {
359 3         8 $node = $node->right;
360             }
361             }
362             # Got to the end without finding the node.
363 2         11 return;
364             }
365              
366             sub node {
367 5     5 1 7 my($this, $key) = @_;
368 5         7 my $cmp = $this->{'cmp'};
369 5         7 my $node = $this->{'root'};
370 5         13 while ($node) {
371 10 100       33 if ($cmp ? $cmp->($key, $node->key) == 0 : $key eq $node->key) {
    100          
    100          
    100          
372 4         19 return $node;
373             } elsif ($cmp ? $cmp->($key, $node->key) < 0 : $key lt $node->key) {
374 4         12 $node = $node->left;
375             } else {
376 2         5 $node = $node->right;
377             }
378             }
379             # Got to the end without finding the node.
380 1         3 return;
381             }
382              
383              
384             1;