{#########################################################################
# HEIGHT BALANCED BINARY SEARCH TREE (AVL TREE)
#
# This program will insert 'avlelements' into a height balanced tree
# mantaining a search time of LOG(n) time...the fastest you can get.
#
# NOTES: 1) Be sure to copy AVLMOD.TYP, an include file that contains
# the definition of the avlelement and it's corresponding
# COMPARE and PRINT funct/proc. If you change the element type,
# be sure to change both COMPARE and PRINT to match.
# 2) Also be sure to copy the file AVLMOD.EXT that defines all of
# the external funct/procs of this module
# 3) There is a short AVLTST.PAS test driver you can use to test
# your change of avlelement if you should do so.
# 4) Your final program should consist of your DRIVER and then link
# this module to your driver.
#
# by DAVE HEYLIGER - AMUS STAFF
#
# last update: 09-16-85
###########################################################################}
MODULE AVLMOD;
{+-- This module implements the following set operations using an height
| balanced binary search tree:
|
| avl_makenull - initializes set to empty set
|
| avl_insert - inserts element into set
|
| avl_search - searches and if found retrieves element
|
| avl_dump - dumps the tree using an inorder traversal
+-------------------------------------------------------------------------}
{$I avlmod.typ}
procedure avlmakenull(var t: avl);
{+-- on entry - true
| on exit - t represents an empty set
+-----------------------------------------}
begin { avlmakenull }
t := nil;
end; { avlmakenull }
procedure avlinsert(x: avlelement; var t: avl);
{+-- on entry - t has been initialized previously,
| if for any tree node n compare(x,n)=0 then n := x;
| compare determines '=','<','>' for avl_elements.
| on exit - x is inserted into t, tree is height balanced
+---------------------------------------------------------------------}
type
rotkind = (none,ll,rr,lra,lrb,lrc,rla,rlb,rlc); { types of rotation }
var
inserted,focal: avllink;
rot: rotkind;
function computefocal(t: avllink): avllink;
{+-- on entry - t points to the just inserted node;
| on exit - walks up the tree recomputing the balancing factors until
| either the root is reached
| or the recomputed BF is -2,0,2
+-------------------------------------------------------------------------}
var
temp: avllink;
begin { computefocal }
temp := t;
if t^.parent <> nil then
begin { not root }
t := t^.parent;
repeat { until we reach the root or recomp. BF to -2,0,2 }
if t^.left = temp then { came from left subtree }
t^.bf := t^.bf + 1
else { came from right subtree } t^.bf := t^.bf - 1;
temp := t; { keeps track where we came from }
t := t^.parent;
until (t = nil) or (temp^.bf=-2) or (temp^.bf=0) or (temp^.bf=2);
{ until we reach the root or recomp. BF to -2,0,2 }
end; { not root }
computefocal := temp;
end; { compute_focal }
function computerotkind(t: avllink): rotkind;
{+-- on entry - t is the focal node after an compute_focal;
| on exit - returns the kind of rotation required
+-----------------------------------------------------------}
begin { computerotkind }
if t^.bf = 2 then { L rotation }
if t^.left^.bf = 1 then computerotkind := ll
else { -1 } if t^.left^.right^.bf = 0 then computerotkind := lra
else if t^.left^.right^.bf = 1 then computerotkind := lrb
else if t^.left^.right^.bf = -1 then computerotkind := lrc
else writeln('module: avlmod, procedure insert, bad tree')
else if t^.bf = -2 then
if t^.right^.bf = -1 then computerotkind := rr
else { 1 } if t^.right^.left^.bf = 0 then computerotkind := rla
else if t^.right^.left^.bf = -1 then computerotkind := rlb
else if t^.right^.left^.bf = 1 then computerotkind := rlc
else writeln('module: avlmod, procedure insert, bad tree')
else computerotkind := none;
end; { computerotkind }
procedure rebalance(a: avllink; rot: rotkind);
{+-- on entry - a is focal node on which rot is to be performed
| on exit - rot is performed; root of tree (t) is reassigned if nec
+------------------------------------------------------------------------}
var
b,c,
al,ar,bl,br,cl,cr: avllink;
procedure hookup(bc: avllink);
{+-- on entry - bc is the root of the balanced subtree
| on exit - bc is hooked up to its parent
+--------------------------------------------------------}
begin { hook_up }
if bc^.parent = nil then t := bc
else if bc^.parent^.left = a then bc^.parent^.left := bc
else bc^.parent^.right := bc;
end; { hook_up }
begin { rebalance }
case rot of
none: begin end;
ll : begin { ll }
b := a^.left;
br := b^.right;
a^.left := br;
if br <> nil then br^.parent := a;
b^.parent := a^.parent;
hookup(b);
b^.right := a;
a^.parent := b;
a^.bf := 0;
b^.bf := 0;
end; { ll }
lra: begin { lr_a }
b := a^.left;
c := b^.right;
c^.parent := a^.parent;
hookup(c);
c^.left := b;
b^.parent := c;
c^.right := a;
a^.parent := c;
b^.right := nil;
a^.left := nil;
b^.bf := 0;
a^.bf := 0;
end; { lr_a }
lrb: begin { lr_b }
b := a^.left;
c := b^.right;
cl := c^.left;
cr := c^.right;
c^.parent := a^.parent;
hookup(c);
c^.left := b;
b^.parent := c;
b^.right := cl;
if cl <> nil then cl^.parent := b;
c^.right := a;
a^.parent := c;
a^.left := cr;
if cr <> nil then cr^.parent := a;
c^.bf := 0;
b^.bf := 0;
a^.bf := -1;
end; { lr_b }
lrc: begin { lr_c }
b := a^.left;
c := b^.right;
cl := c^.left;
cr := c^.right;
c^.parent := a^.parent;
hookup(c);
c^.left := b;
b^.parent := c;
c^.right := a;
a^.parent := c;
b^.right := cl;
if cl <> nil then cl^.parent := b;
a^.left := cr;
if cr <> nil then cr^.parent := a;
c^.bf := 0;
b^.bf := 1;
a^.bf := 0;
end; { lr_c }
{ the R rotations }
rr : begin { rr }
b := a^.right;
bl := b^.left;
a^.right := bl;
if bl <> nil then bl^.parent := a;
b^.parent := a^.parent;
hookup(b);
b^.left := a;
a^.parent := b;
a^.bf := 0;
b^.bf := 0;
end; { ll }
rla: begin { rl_a }
b := a^.right;
c := b^.left;
c^.parent := a^.parent;
hookup(c);
c^.right := b;
b^.parent := c;
c^.left := a;
a^.parent := c;
b^.left := nil;
a^.right := nil;
b^.bf := 0;
a^.bf := 0;
end; { rl_a }
rlb: begin { rl_b }
b := a^.right;
c := b^.left;
cl := c^.right;
cr := c^.left;
c^.parent := a^.parent;
hookup(c);
c^.right := b;
b^.parent := c;
b^.left := cl;
if cl <> nil then cl^.parent := b;
c^.left := a;
a^.parent := c;
a^.right := cr;
if cr <> nil then cr^.parent := a;
c^.bf := 0;
b^.bf := 0;
a^.bf := 1;
end; { rl_b }
rlc: begin { rl_c }
b := a^.right;
c := b^.left;
cl := c^.right;
cr := c^.left;
c^.parent := a^.parent;
hookup(c);
c^.right := b;
b^.parent := c;
b^.left := cl;
if cl <> nil then cl^.parent := b;
c^.left := a;
a^.parent := c;
a^.right := cr;
if cr <> nil then cr^.parent := a;
procedure ubsinsert(x: avlelement; var t: avl);
{+-- on entry - t is init.
| on exit - x is inserted
+--------------------------------------------------------}
begin { ubs_insert }
if t = nil
then begin { base case }
new(t);
inserted := t; { keep track of inserted node }
t^.element := x;
t^.bf := 0; { set balancing factor to zero }
t^.parent := nil;
t^.left := nil; { no left subtree }
t^.right := nil { no right subtree }
end { base case }
else if compare(x,t^.element) = -1
then begin { left subtree }
ubsinsert(x,t^.left);
t^.left^.parent := t; { reconstruct parent pointers }
end { left subtree }
else if compare(x,t^.element) = 0
then t^.element := x
else begin { right subtree }
ubsinsert(x,t^.right);
t^.right^.parent := t; { reconstruct paretn pointers }
end; { right subtree }
end; { ubs_insert }
begin { avl_insert }
inserted := nil; { if nil after ubs_insert => reassignment }
ubsinsert(x,t);
if inserted <> nil
then begin { rebalancing process }
focal := computefocal(inserted);
rot := computerotkind(focal);
rebalance(focal,rot);
end; { rebalancing process }
end; { avl_insert }
function avlsearch(x: avlelement; var r: avlelement; t: avl): boolean;
{+-- on entry - t has been initialized previously;
| on exit - returns true iff there is an element r in the tree such that
| compare(x,r) = 0; in that case r is returned in r.
| returns false otherwise.
+-------------------------------------------------------------------------}
begin { avl_search }
if t = nil
then avlsearch := false { not found }
else if compare(x,t^.element) = -1
then avlsearch := avlsearch(x,r,t^.left)
else if compare(x,t^.element) = 0
then begin { found }
r := t^.element; { returns element }
avlsearch := true; { x found }
end { found }
else avlsearch := avlsearch(x,r,t^.right);
end; { avl_search }
procedure avldump(t: avl; var out: text);
{+-- on entry - t has been initialized previously
| on exit - tree contents is dumped on file out in a reasonable form;
| inorder traversal is used
+------------------------------------------------------------------------}
begin { avl_dump }
if t <> nil
then begin { non empty tree }
avldump(t^.left, out);
print(out,t^.element);
writeln(out,' ',t^.bf:1);
avldump(t^.right,out);
end; { non empty tree }
end; { avl_dump }