(* Insertion and deletion in an AVL-balanced tree. In the previous program (tree), the binary tree may grow in all sorts of shapes -- if the inserted keys are ordered upon arrival, the "tree" even degenerates into a linear list. In the following program, a balance is maintained, such that at each node the heights of its two subtree differ by at most 1. *) MODULE BalTree; FROM InOut IMPORT WriteString,WriteInt,WriteLn,ReadInt; FROM Storage IMPORT ALLOCATE, DEALLOCATE; TYPE ref = POINTER TO word; word = RECORD key : INTEGER; count : INTEGER; left ,right : ref; bal : [-1..1] END; VAR root : ref; h : BOOLEAN; k : INTEGER; (******************************************************) PROCEDURE printree(w: ref; l: INTEGER); VAR i : INTEGER; BEGIN IF w <> NIL THEN WITH w^ DO printree(left, l+1); FOR i := 1 TO l DO WriteString(" ") END; WriteInt(key,5); WriteInt(bal,5);WriteLn; printree(right,l+1); END;(*with*); END; (*if*) END printree; (******************************************************) PROCEDURE search(x: INTEGER; VAR p:ref; VAR h: BOOLEAN); VAR p1,p2: ref; (* h = FALSE*) BEGIN IF p = NIL THEN (* word is not in tree; insert it *) NEW(p); h:= TRUE; WITH p^ DO key := x; count := 1; left := NIL; right := NIL; bal := 0 END;(*with*) ELSIF x < p^.key THEN search(x,p^.left,h); IF h THEN (* left branch has grown higher *) CASE p^.bal OF 1 : p^.bal:= 0; h:= FALSE; | 0 : p^.bal:= -1; | -1 : p1 := p^.left; (* rebalance *) IF p1^.bal = -1 THEN (* single LL rotation *) p^.left:= p1^.right; p1^.right:= p; p^.bal:= 0; p := p1; ELSE (* double LR rotation *) p2 := p1^.right; p1^.right:= p2^.left; p2^.left:= p1; p^.left:= p2^.right; p2^.right:= p; IF p2^.bal = -1 THEN p^.bal := +1 ELSE p^.bal:= 0 END; IF p2^.bal = +1 THEN p1^.bal := -1 ELSE p1^.bal:= 0 END; p := p2; END; (*if*) p^.bal:= 0; h:= FALSE; END; (*case*) END; (*if*) ELSIF x > p^.key THEN search(x,p^.right,h); IF h THEN (*right branch has grown higher *) CASE p^.bal OF -1 : p^.bal:= 0; h:= FALSE; | 0 : p^.bal:= +1; | 1 : p1 := p^.right; (* rebalance *) IF p1^.bal = +1 THEN (* single RR rotation *) p^.right:= p1^.left; p1^.left:= p; p^.bal:= 0; p := p1; ELSE (* double RL rotation *) p2 := p1^.left; p1^.left:= p2^.right; p2^.right:= p1; p^.right:= p2^.left; p2^.left:= p; IF p2^.bal = +1 THEN p^.bal := -1 ELSE p^.bal:= 0 END; IF p2^.bal = -1 THEN p1^.bal := +1 ELSE p1^.bal:= 0 END; p := p2 END; (*if*) p^.bal:= 0; h:= FALSE END; (*case*) END; (*if*) ELSE INC(p^.count); h:= FALSE END; (*if*) END search; (******************************************************) PROCEDURE delete(x: INTEGER; VAR p:ref; VAR h: BOOLEAN); VAR q: ref; (* h = FALSE*) PROCEDURE balance1(VAR p:ref; VAR h: BOOLEAN); VAR p1,p2: ref; b1,b2: [-1..+1]; BEGIN (*h = true, left branch has become less high *) CASE p^.bal OF -1 : p^.bal:= 0; | 0 : p^.bal:= +1; h:= FALSE; (* rebalance *) | 1 : p1:= p^.right; b1:= p1^.bal; IF b1 >= 0 THEN (* single RR rotation *) p^.right := p1^.left; p1^.left:=p; IF b1 = 0 THEN p^.bal:= +1; p1^.bal := -1; h:= FALSE ELSE p^.bal:= 0; p1^.bal:= 0 END; (*if*) p := p1; ELSE (* double RL rotation *) p2 := p1^.left; b2 := p2^.bal; p1^.left:= p2^.right; p2^.right:= p1; p^.right:= p2^.left; p2^.left:= p; IF b2 = +1 THEN p^.bal := -1 ELSE p^.bal:= 0 END; IF b2 = -1 THEN p1^.bal := +1 ELSE p1^.bal:= 0 END; p := p2; p2^.bal := 0; END; (*if*) END; (*case*) END balance1; PROCEDURE balance2(VAR p:ref; VAR h: BOOLEAN); VAR p1,p2: ref; b1,b2: [-1..+1]; BEGIN (*h = true, right braanch has become less high *) CASE p^.bal OF 1 : p^.bal:= 0; | 0 : p^.bal:= -1; h:= FALSE; | -1 : p1:= p^.left; (* rebalance *) b1:= p1^.bal; IF b1 <= 0 THEN (* single LL rotation *) p^.left:= p1^.right; p1^.right:=p; IF b1 = 0 THEN p^.bal:= -1; p1^.bal := +1; h:= FALSE ELSE p^.bal:= 0; p1^.bal:= 0 END; (*if*) p := p1; ELSE (* double LR rotation *) p2 := p1^.right; b2 := p2^.bal; p1^.right:= p2^.left; p2^.left:= p1; p^.left:= p2^.right; p2^.right:= p; IF b2 = -1 THEN p^.bal := +1 ELSE p^.bal:= 0 END; IF b2 = +1 THEN p1^.bal := -1 ELSE p1^.bal:= 0 END; p := p2; p2^.bal := 0; END; (*if*) END; (*case*) END balance2; PROCEDURE del(VAR r:ref; VAR h: BOOLEAN); BEGIN (* h = false *) IF r^.right <> NIL THEN del(r^.right,h); IF h THEN balance2(r,h) END; ELSE q^.key := r^.key; q^.count := r^.count; r := r^.left; h := TRUE; END; (*if*) END del; BEGIN (*delete*) IF p = NIL THEN WriteString("key is not in tree");WriteLn; h := FALSE; ELSIF x < p^.key THEN delete(x,p^.left,h); IF h THEN balance1(p,h) END; (*if*) ELSIF x > p^.key THEN delete(x,p^.right,h); IF h THEN balance2(p,h) END; (*if*) ELSE (* delete p^*) q := p; IF q^.right = NIL THEN p := q^.left; h := TRUE; ELSIF q^.left = NIL THEN p := q^.right; h := TRUE ELSE del(q^.left,h); IF h THEN balance1(p,h) END; (*if*) END; (*if*) (*dispose q*) END; (*if*) END delete; (******************************************************) BEGIN WriteString("enter a 0 to quit and a negative number for deletion"); WriteLn; WriteString("enter a node -> "); ReadInt(k); WriteLn; root := NIL; WHILE k <> 0 DO IF k >= 0 THEN WriteString("insert"); WriteInt(k,4); WriteLn; search(k,root,h); ELSE WriteString("delete");WriteInt(-k,4); WriteLn; delete(-k,root,h) END; (*if*) printree(root,0); WriteString("enter a node -> "); ReadInt(k); WriteLn; END; (*while*) END BalTree.