1 package Heap071
::Fibonacci
;
4 use vars
qw($VERSION @ISA @EXPORT @EXPORT_OK);
9 @ISA = qw(Exporter AutoLoader);
12 # No names available for export.
18 # Preloaded methods go here.
22 # el - linkable element, contains user-provided value
23 # v - user-provided value
25 ################################################# debugging control
30 # enable/disable debugging output
32 @_ ?
($debug = shift) : $debug;
35 # enable/disable validation checks on values
37 @_ ?
($validate = shift) : $validate;
47 $width = 2 if $width < 2;
50 $bar = $corner = ' ' x
$width;
51 substr($bar,-2,1) = '|';
52 substr($corner,-2,2) = '+-';
70 hdump
$ch1 = $el->{child
},
71 $l1 . sprintf( $vfmt, $el->{val
}->val),
75 for( $ch = $ch1->{right
}; $ch != $ch1; $ch = $ch->{right
} ) {
76 hdump
$ch, $b . $corner, $b . $bar;
85 my $top = $$h or last;
89 hdump
$el, sprintf( "%02d: ", $el->{degree
}), ' ';
107 $cur = $cur->{right
};
108 die "bad back link" unless $cur->{left
} == $prev;
109 die "bad parent link"
110 unless (defined $p && defined $cur->{p
} && $cur->{p
} == $p)
111 || (!defined $p && !defined $cur->{p
});
112 die "bad degree( $cur->{degree} > $p->{degree} )"
113 if $p && $p->{degree
} <= $cur->{degree
};
114 die "not heap ordered"
115 if $p && $p->{val
}->cmp($cur->{val
}) > 0;
116 $ch = $cur->{child
} and bhcheck
$ch, $cur;
124 while( $h = shift ) {
125 heapdump
$h if $validate >= 2;
126 $el = $$h and bhcheck
$el, undef;
131 ################################################# forward declarations
138 ################################################# heap methods
140 # Cormen et al. use two values for the heap, a pointer to an element in the
141 # list at the top, and a count of the number of elements. The count is only
142 # used to determine the size of array required to hold log(count) pointers,
143 # but perl can set array sizes as needed and doesn't need to know their size
144 # when they are created, so we're not maintaining that field.
147 my $class = ref($self) || $self;
162 die "Method 'heap' required for element on heap"
163 unless $v->can('heap');
164 die "Method 'cmp' required for element on heap"
165 unless $v->can('cmp');
169 if( !($top = $$h) ) {
172 link_to_left_of
$top->{left
}, $el ;
173 link_to_left_of
$el,$top;
174 $$h = $el if $v->cmp($top->{val
}) < 0;
187 my $el = $$h or return undef;
188 my $ltop = $el->{left
};
192 # $el is the heap with the lowest value on it
193 # move all of $el's children (if any) to the top list (between
195 if( $cur = $el->{child
} ) {
196 # remember the beginning of the list of children
199 # the children are moving to the top, clear the p
200 # pointer for all of them
202 } until ($cur = $cur->{right
}) == $first;
204 # remember the end of the list
206 link_to_left_of
$ltop, $first;
207 link_to_left_of
$cur, $el;
210 if( $el->{right
} == $el ) {
211 # $el had no siblings or children, the top only contains $el
212 # and $el is being removed
215 link_to_left_of
$el->{left
}, $$h = $el->{right
};
216 # now all those loose ends have to be merged together as we
218 # new smallest element
222 # extract the actual value and return that, $el is no longer used
223 # but break all of its links so that it won't be pointed to...
224 my $top = $el->{val
};
226 $el->{left
} = $el->{right
} = $el->{p
} = $el->{child
} = $el->{val
} =
231 *extract_minimum
= \
&extract_top
;
244 my $el2 = $$h2 or return $h;
246 # add $el2 and its siblings to the head list for $h
247 # at start, $ell -> $el -> ... -> $ell is on $h (where $ell is
249 # $el2l -> $el2 -> ... -> $el2l are on $h2
250 # at end, $ell -> $el2l -> ... -> $el2 -> $el -> ... -> $ell are
252 my $el2l = $el2->{left
};
253 link_to_left_of
$el->{left
}, $el2;
254 link_to_left_of
$el2l, $el;
256 # change the top link if needed
257 $$h = $el2 if $el->{val
}->cmp( $el2->{val
} ) > 0;
266 # a key has been decreased, it may have to percolate up in its heap
271 my $el = $v->heap or return undef;
274 # first, link $h to $el if it is now the smallest (we will
275 # soon link $el to $top to properly put it up to the top list,
276 # if it isn't already there)
277 $$h = $el if $top->{val
}->cmp( $v ) > 0;
279 if( $p = $el->{p
} and $v->cmp($p->{val
}) < 0 ) {
280 # remove $el from its parent's list - it is now smaller
282 ascending_cut
$top, $p, $el;
289 # to delete an item, we bubble it to the top of its heap (as if its key
290 # had been decreased to -infinity), and then remove it (as in extract_top)
294 my $el = $v->heap or return undef;
296 # if there is a parent, cut $el to the top (as if it had just had its
297 # key decreased to a smaller value than $p's value
299 $p = $el->{p
} and ascending_cut
$$h, $p, $el;
301 # $el is in the top list now, make it look like the smallest and
308 ################################################# internal utility functions
322 $el->{left
} = $el->{right
} = $el;
331 $el->{left
}->{right
} = undef;
334 $ch = $el->{child
} and elem_DESTROY
$ch;
335 $next = $el->{right
};
337 defined $el->{val
} and $el->{val
}->heap(undef);
338 $el->{child
} = $el->{right
} = $el->{left
} = $el->{p
} = $el->{val
}
344 sub link_to_left_of
{
352 sub link_as_parent_of
{
358 if( $pc = $p->{child
} ) {
359 link_to_left_of
$pc->{left
}, $c;
360 link_to_left_of
$c, $pc;
362 link_to_left_of
$c, $c;
377 my $last = $next->{left
};
380 # examine next item on top list
381 $this = $cur = $next;
382 $next = $cur->{right
};
383 my $d = $cur->{degree
};
385 while( $alt = $a[$d] ) {
386 # we already saw another item of the same degree,
387 # put the larger valued one under the smaller valued
388 # one - switch $cur and $alt if necessary so that $cur
390 ($cur,$alt) = ($alt,$cur)
391 if $cur->{val
}->cmp( $alt->{val
} ) > 0;
392 # remove $alt from the top list
393 link_to_left_of
$alt->{left
}, $alt->{right
};
394 # and put it under $cur
395 link_as_parent_of
$cur, $alt;
396 # make sure that $h still points to a node at the top
398 # we've removed the old $d degree entry
400 # and we now have a $d+1 degree entry to try to insert
404 # found a previously unused degree
406 } until $this == $last;
408 for $cur (grep defined, @a) {
409 $$h = $cur if $$h->{val
}->cmp( $cur->{val
} ) > 0;
419 if( --$p->{degree
} ) {
420 # there are still other children below $p
423 link_to_left_of
$l, $el->{right
};
425 # $el was the only child of $p
428 link_to_left_of
$top->{left
}, $el;
429 link_to_left_of
$el, $top;
433 # propagate up the list
437 last unless $p = $el->{p
};
439 # quit if we can mark $el
440 $el->{mark
} = 1, last unless $el->{mark
};
451 Heap::Fibonacci - a Perl extension for keeping data partially sorted
457 $heap = Heap::Fibonacci->new;
458 # see Heap(3) for usage
462 Keeps elements in heap order using a linked list of Fibonacci trees.
463 The I<heap> method of an element is used to store a reference to
464 the node in the list that refers to the element.
466 See L<Heap> for details on using this module.
470 John Macdonald, jmm@perlwolf.com
474 Copyright 1998-2003, O'Reilly & Associates.
476 This code is distributed under the same copyright terms as perl itself.
480 Heap(3), Heap::Elem(3).