{######################################################################### # 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; c^.bf := 0; b^.bf := -1; a^.bf := 0; end; { rl_c } end; end; { rebalance } 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 } . .