9.4.6 Maintaining Balance

Consider the AVL tree of Figure 9.10(a). To insert a new record into this tree, let us first do a "simple" insertion. The dashed branches indicate the sixteen possible insertion positions for a new node. You should confirm that, of these, only an insertion of a new node as a successor of 45, 53, 81, or 100 will cause the new tree to fail to remain an AVL tree. An insertion at node 45 causes the subtrees with roots 55, 40, and 70 to become unbalanced. For example, the subtree with root 40 would have a left subtree of depth 3, while its right subtree would have depth 5. These differ by more than 1. Notice that 55, 40, and 70 lie along the search path from the root to the new node inserted as a successor of 45. If 43 were inserted, then the result would be the tree of Figure 9.10(b). Thus the "simple" insertion discussed earlier will not retain the balance of a search tree.

This imbalance is remedied by rearranging the subtree with root 55 as shown in Figure 9.10(c). Notice that the subtrees with roots 40 and 70 are no longer out of balance. In fact, the tree of Figure 9.10(c) is now an AVL tree. Notice also that the subtree in Figure 9.10(c) replacing the subtree of 55 in Figure 9.10(b) has exactly the same depth as the original subtree of 55 in Figure 9.10(a). If any of the other insertions that would have produced an imbalance in Figure 9.10(a) had occurred, they could have been remedied by similar rearrangements. You should confirm this by trying them all and finding the suitable rearrangements. Remember, the rearrangements must not only rebalance but also preserve the binary search tree property.

It is possible to analyze the general situation to determine exactly when an insertion will cause an imbalance and what its remedy will be. An insertion into an AVL tree with zero or one node cannot cause an imbalance. When the tree has more than one node, the types of imbalance, and their corresponding remedies, reduce to the four indicated in Figures 9.11 and 9.12.

Each rectangle in Figures 9.11 and 9.12 represents an AVL tree. Any of these may be null. An X represents the position of the inserted new record that has caused imbalance. Three X's appear in imbalances LR and RL, actually representing three possibilities, although only one will have taken place. The LR and RL imbalances are meant to include the special case where B is actually the inserted node. This is why the X appears next to node B. Subtrees 1, 2, 3, and 4 must then be null. This fact is noted here, since it is not clear from the figures. In cases LL and RR, the subtree with root B represents the node closest to the inserted node, along the path from the inserted node to the root, where an imbalance occurs. In cases LR and RL the subtree with root C represents this node. In all remedies, the subtrees represented by the rectangles retain their relative positions. Note that all remedies preserve the binary search tree property. It is very important to see that, in each case, the depth of the remedy is exactly the same as the depth of the original subtree before the new insertion was made. Consequently, no nodes on the path from the subtree root, B in cases LL and RR, and C in cases LR and RL, imbalanced by the new insertion, will be imbalanced after the remedy is applied. It is also important to see that these remedies are applied locally; they do not involve the creation of a new AVL tree in its entirety.

Figure 9.10 An AVL Tree before and after Insertion

In Figure 9.11, when 43 was inserted, case LL occurred. The roles of B and A were taken by 55 and 50, respectively. The three rectangles, 1, 2, 3 of Figure 9.11 (a and b), represented, respectively, the subtrees shown in Figure 9.13. The X represented 43.

(a) Imbalance LL

(b) Remedy for LL

(c) Imbalance LR

(d) Remedy for LR

Figure 9.11 Two Imbalances and Their Remedies

Consider imbalance LL. The remedy looks as though A has moved up to the root and B has moved to the right. This is called a rotation to the right of the subtree with B at its root. The remedy for imbalance RR would be described as a rotation to the left of the subtree with B at its root. To rectify imbalance LR, perform a rotation to the left of the subtree with root A to obtain Figure 9.14(a). A further rotation to the right of the subtree rooted at C produces (b) of Figure 9.14. Assume that rotations are accomplished by the procedures rotateleft and

(a) Imbalance RR

(b) Remedy for RR

(c) Imbalance RL

(d) Remedy for RL

Figure 9.12 Two More Imbalances and Their Remedies

Figure 9.13 Rectangles 1-3 of Figure 9.12(a) and 9.12(b)

Figure 9.14 The Double Rotation Producing Remedy LR

rotateright, each with a parameter that points to the root of the subtree to be rotated. Then the remedies for each imbalance may be obtained by

LL:  rotateright(plast)             where last points to B

RR:  rotateleft(plast)              where last points to B

LR:  rotateleft(p(last.leftptr))    where last points to C

     rotateright(plast)

RL:  rotateright(p(last.rightptr))  where last points to C

     rotateleft(plast)

The procedure for rotateright, for example, might be as follows:

rotateright(plocalroot)

/* Performs a rotation to the right

of the subtree whose root is

pointed to by localroot.

*/

binarytreepointer *plocalroot;

{

binarytreepointer q;

q = (*plocalroot).leftptr;

(*plocalroot).leftptr = q?FONT FACE="Symbol" SIZE=2>>rightptr;

q->rightptr = *plocalroot;

*plocalroot = q;

}

Note that the procedure also changes the pointer in localroot, so it points to the root of the new subtree. It is assumed that nodes of the AVL tree are represented as follows:

typedef struct treenode

{

whatever key;

struct treenode *leftptr;

struct treenode *rightptr;

int balance;

}binarytreenode,*binarytreepointer;

The balance field stores the node's right subtree depth minus the node's left subtree depth. In an AVL tree it will have values - 1, 0, or + 1.

Consider the binary tree shown in Figure 9.15(a), which was balanced until the new node, marked by X, was inserted. The initial balance values are also shown. Because of the insertion, the root node and node C have become unbalanced. Since node C is closest to the inserted node, we remedy this imbalance by applying the remedy for RL to obtain Figure 9.15(b). The tree itself is now balanced, but the balances of nodes A, B, and C are now incorrect, as are the balances of the three nodes between X and A. The last node, however, does have the correct balance value.

In general, after rebalancing, one of the nodes involved in the remedy will be the new root of the subtree that was rebalanced (B in the example). If the remedy was LL or RR, all nodes along the path from this node to the inserted node, starting with its successor, need their balances reset. If the remedy was LR or RL, then all nodes along this path, starting with its successor's successor (the successor of A in the example), need their balances reset. The node not on this path involved in the remedy (C in the example) must have its balance reset, and, finally, the new root of the rebalanced subtree must have its balance set to 0. The special case when the inserted node is at B requires only that C's balance be set to 0. All nodes above this new root need not be reset; they retain their original values. Any time the root node is involved, special processing is required, since the head of the tree must be modified rather than a left or right subtree pointer of a node.

The algorithm for inserting a node into an AVL tree can now be written.

1. Search for the keyvalue.

2. If it is not found, then

a. Create a new node for it and set its field values.

b. Insert the new node into the tree.

c. If the tree has more than one node, then

If it is not necessary to rebalance, then reset last's balance and the balances of nodes on he search path between last and the new node.

If it is necessary to rebalance, then

i. apply the appropriate remedy to rebalance, reset the balance of the node involved in the remedy but not on the new search path, reset the proper new search path balances, set the balance of the root of the subtree at which rebalancing occurred to 0.

ii. If this root was the root of the tree itself, then

set the head of the tree to this root; otherwise

set its predecessor to this root;

else

reset the balance of the predecessor of the inserted node.

It has already been noted that, when rebalancing is necessary, once the subtree whose root is nearest the newly inserted node has been rebalanced with the correct remedy, all other subtrees along the path to the root of the entire tree will also be back in balance. Again, this is because the smallest subtree causing the imbalance has been modified by the remedy to have its original depth. All the subtrees were originally balanced when it had that depth, so they must be balanced now. During the search in step 1 for the insertion key, suppose you keep a pointer last. Last is updated to point to the node that may become imbalanced on the search path nearest the insertion node. Only nodes with balance values of + 1 or -1 may become imbalanced due to the insertion. It is necessary to rebalance only if last.balance was -1 and the search path went left at last, or last.balance was + 1 and the search path went right at last, and p is not t, and pred.balance = 0. Only nodes on the search path, between the inserted node and last, will need to have their balance fields updated.

(a) Unbalanced

(b) Balanced

Figure 9.15 Rebalancing a Binary Tree

During the search, an updated pointer to the predecessor node of last should be kept in predlast. P will point to the node whose key field is currently being compared with the search key, and predp points to its predecessor. An implementation of the AVL tree insertion is given in the insertavl function. For clarity, the procedures createnode and search are then given separately, followed by procedures insertnode and resetpathbalances.

Nonrecursive AVL Insertion

#define TRUE 1

#define FALSE 0

#define NULL 0

insertavl(keyvalue,pt)

/* Inserts a new record with key equal to

keyvalue in the AVL tree t, if a record

with this keyvalue is not present in t.

After insertion t will still be an AVL tree.

*/

whatever keyvalue;

binarytreepointer *pt;

{

binarytreepointer p,predp,last,predlast,q;

int found;

>Comment

search(keyvalue,*pt,&p,&predp,

&last,&predlast,&found);

>Comment

if(!found)

{

>Comment

createnode(keyvalue,&p);

>Comment

insertnode(keyvalue,p,predp,pt);

>Comment

if(p != *pt)

if(predp->balance == 0)

if(keyvalue < last->key)

if(last->balance == 0)

>Comment

{

last->balance = -1;

resetpathbalances(keyvalue,

last->leftptr,p);

}

else if(last->balance == +1)

>Comment

{

last->balance = 0;

resetpathbalances(keyvalue,

last?gt;leftptr,p);

}

else

>Comment

{

>Comment

q = last->leftptr;

if(keyvalue < q->key)

ll(keyvalue,&last,p);

else

lr(keyvalue,&last,&q,p);

last->balance = 0;

>Comment

if(predlast == NULL)

*pt = last;

else if(keyvalue < predlast->key)

predlast->leftptr = last;

else

predlast->rightptr = last;

}

else

if(last->balance == 0)

>Comment

{

last->balance = +1;

resetpathbalances(keyvalue,

last->rightptr,p);

}

else if(last->balance == -1)

>Comment

{

last->balance = 0;

resetpathbalances(keyvalue,

last->rightptr,p);

}

else

>Comment

{

>Comment

q = last->rightptr;

if(keyvalue < q->key)

rr(keyvalue,&last,p);

else

rl(keyvalue,&last,&q,p);

last->balance = 0;

>Comment

if(predlast == NULL)

*pt = last;

else if(keyvalue < predlast->key)

predlast->leftptr = last;

else

predlast->rightptr = last;

}

else

>Comment

predp->balance = 0;

}

}

ll(keyvalue,plast,p)

/* Applies remedy ll, resets the balance of the node

involved in the remedy but not on the new search

path, and resets the proper new search path balances.

*/

whatever keyvalue;

binarytreepointer *plast,p;

{

rotateright(plast);

((*plast)->rightptr)->balance = 0;

resetpathbalances(keyvalue,(*plast)->leftptr,p);

}

lr(keyvalue,plast,pq,p)

/* Applies remedy lr, resets the balance of the node

involved in the remedy but not on the new search

path, and resets the proper new search path balances.

*/

whatever keyvalue;

binarytreepointer *plast,*pq,p;

{

rotateleft(&((*plast)->leftptr));

rotateright(plast);

if(keyvalue < (*plast)->key)

{

(*pq).balance = 0;

((*plast)->rightptr)->balance = +1;

resetpathbalances(keyvalue,

((*plast)->leftptr)->rightptr,p);

}

else if(keyvalue > (*plast)->key)

{

(*pq)->balance = -1;

((*plast)->rightptr)->balance = 0;

resetpathbalances(keyvalue,

((*plast)->rightptr)->leftptr,p);

}

else

((*plast)->rightptr)->balance = 0;

}

rr(keyvalue,plast,p)

/* Applies remedy rr, resets the balance

of the node involved in the remedy but

not on the new search path, and resets

the proper new search path balances.

*/

whatever keyvalue;

binarytreepointer *plast,p;

{

rotateleft(plast);

((*plast)->leftptr)->balance = 0;

resetpathbalances(keyvalue,(*plast)->rightptr,p);

}

rl(keyvalue,plast,pq,p)

/* Applies remedy rl, resets the balance

of the node involved in the remedy but

not on the new search path, and resets

the proper new search path balances.

*/

whatever keyvalue;

binarytreepointer *plast,*pq,p;

{

rotateright(&((*plast)->rightptr));

rotateleft(plast);

if(keyvalue > (*plast)->key)

{

(*pq)->balance = 0;

((*plast)->leftptr)->balance = -1;

resetpathbalances(keyvalue,((*plast)->rightptr)->leftptr,p);

}

else if(keyvalue < (*plast)->key)

{

(*pq)->balance = +1;

((*plast)->leftptr)->balance = 0;

resetpathbalances(keyvalue,((*plast)->leftptr)->rightptr,p);

}

else

((*plast)->leftptr)->balance = 0;

}

rotateright(plocalroot)

/* Performs a rotation to the right

of the subtree whose root is

pointed to by localroot.

*/

binarytreepointer *plocalroot;

{

binarytreepointer q;

q = (*plocalroot)->leftptr;

(*plocalroot)->leftptr = q->rightptr;

q->rightptr = *plocalroot;

*plocalroot = q;

}

rotateleft(plocalroot)

/* Performs a rotation to the left

of the subtree whose root is

pointed to by localroot.

*/

binarytreepointer *plocalroot;

{

binarytreepointer q;

q = (*plocalroot)->rightptr;

(*plocalroot)->rightptr = q->leftptr;

q->leftptr = *plocalroot;

*plocalroot = q;

}

resetpathbalances(keyvalue,start,p)

/* Resets the balances of all nodes on

search path between the nodes

pointed to by start and p.

*/

whatever keyvalue;

binarytreepointer start,p;

{

binarytreepointer q;

q = start;

while(q !=p)

if(keyvalue < q->key)

{

q-balance = -1;

q = q->leftptr;

}

else

{

q->balance = +1;

q = q->rightptr;

}

}

createnode(keyvalue,pp)

/* Returns with p pointing to a new node

record with key equal to keyvalue,

left and right pointer fields null, and

balance field zero.

*/

whatever keyvalue;

binarytreepointer *pp;

{

*pp = malloc(sizeof(binarytreenode));

(*pp)->key = keyvalue;

(*pp)->leftptr = NULL;

(*pp)->rightptr = NULL;

(*pp)->balance = 0;

}

search(keyvalue,t,pp,ppredp,plast,ppredlast,pfound)

/* Returns with found true only if the binary search

tree t has a node with its key equal to keyvalue.

If found is true

p and predp point respectively, to

the found node and its predecessor,

else

predp, and last, will point, respectively,

to the node that will be the new node's

predecessor, and the node along the search

path closest to the new node's insertion point

that may become unbalanced.

*/

whatever keyvalue;

binarytreepointer t,*pp,*ppredp,*plast,*ppredlast;

int *pfound;

{

*pfound = FALSE;

*pp = t;

*plast = t;

*ppredp = NULL;

*ppredlast = NULL;

while((*pp != NULL) && (!*pfound))

if(keyvalue < (*pp)->key)

{

if((*pp)->balance != 0)

{

*ppredlast = *ppredp;

*plast = *pp;

}

*ppredp = *pp;

*pp = (*pp)->leftptr;

}

else if(keyvalue > (*pp)->key)

{

if((*pp)->balance != 0)

{

*ppredlast = *ppredp;

*plast = *pp;

}

*ppredp = *pp;

*pp = (*pp)->rightptr;

}

else

*pfound = TRUE;

}

insertnode(keyvalue,p,predp,pt)

/* Inserts the new node, pointed to

by p, as the proper successor

of predp.

*/

whatever keyvalue;

binarytreepointer p,predp,*pt;

{

if(predp == NULL)

*pt = p;

else

if(predp == *pt)

if(keyvalue < (*pt)->key)

(*pt)->leftptr = p;

else

(*pt)->rightptr = p;

else

if(keyvalue < predp->key)

predp->leftptr = p;

else

predp->rightptr = p;

}

As another illustration of recursion, a recursive version is presented next. It is based on the following formulation.

To insert a node in t:

If t is null then

create the new node, set its field values and set t to it

else

if keyvalue < t.key, then

insert the node in t.leftptr

if t is balanced, then

reset its balance

else

rebalance t and reset the balances of the nodes involved in the

remedy

else if keyvalue > t.key then

insert the node in t.rightptr

if t is balanced, then

reset its balance

else

rebalance t and reset the balances of the nodes involved in the

remedy.

The recursive insertavl procedure is as follows:

Recursive AVL Insertion

#define TRUE 1

#define FALSE 0

#define NULL 0

insertavl(keyvalue,pt,pincrease)

/* Inserts a new record with key value to

keyvalue in the AVL tree t, if a record

with this keyvalue is not present in t.

After insertion t will still be an AVL tree.

Increase must be zero when the function is invoked.

*/

whatever keyvalue;

binarytreepointer *pt;

int *pincrease;

{

binarytreepointer q;

>Comment

if(*pt == NULL)

{

>Comment

createnode(keyvalue,pt);

>Comment

*pincrease = 1;

}

>Comment

else if(keyvalue < (*pt)->key)

{

>Comment

insertavl(keyvalue,

&((*pt)->leftptr),pincrease);

>Comment

if(*pincrease) == 1)

if((*pt)->balance == 0)

>Comment

(*pt)->balance = -1;

else if((*pt)->balance == +1)

{

>Comment

(*pt)->balance = 0;

>Comment

*pincrease = 0;

}

else

{

>Comment

q = (*pt)->leftptr;

if(keyvalue < q->key)

ll(pt);

else

lr (pt,q);

>Comment

(*pt)->balance = 0;

*pincrease = 0;

1 t's depth did not increase

}

}

>Comment

else if(keyvalue > (*pt)->key)

{

>Comment

insertavl(keyvalue,

&((*pt)->rightptr),pincrease);

>Comment

if(*pincrease == 1)

if((*pt)->balance == 0)

>Comment

(*pt)->balance = +1;

else if((*pt)->balance == -1)

{

>Comment

(*pt)->balance = 0;

>Comment

*pincrease = 0;

}

else

{

>Comment

q = (*pt)->rightptr;

if(keyvalue > q->key)

rr (pt)

else

rl(pt,q);

>Comment

(*pt)->balance = 0;

>Comment

*pincrease = 0;

}

}

}

ll(pt)

/* Apply remedy ll and reset the

balance of the node involved in

the remedy.

*/

binarytreepointer *pt;

{

rotateright(pt);

reset1balance((*pt)->rightptr);

}

lr(pt,q)

/* Apply remedy lr and reset the balances

of the two nodes involved in

the remedy.

*/

binarytreepointer *pt,q;

{

rotateleft(&((*pt)->leftptr));

rotateright(pt);

reset2balances(q,*pt,(*pt)->rightptr);

}

rr(pt)

/* Apply remedy rr and reset the

balance of the node involved in

the remedy.

*/

binarytreepointer *pt;

{

rotateleft(pt);

reset1balance((*pt)->leftptr);

}

rl(pt,q)

/* Apply remedy rl and reset the balances

of the two nodes involved in the remedy.

*/

binarytreepointer *pt,q;

{

rotateright(&((*pt)->rightptr));

rotateleft(pt);

reset2balances(q,*pt,(*pt)->leftptr);

}

rotateright(plocalroot)

/* Performs a rotation to the right

of the subtree whose root is

pointed to by localroot.

*/

binarytreepointer *plocalroot;

{

binarytreepointer q;

q = (*plocalroot)->leftptr;

(*plocalroot)->leftptr = q->rightptr;

q->rightptr = *plocalroot;

*plocalroot = q;

}

rotateleft(plocalroot)

/* Performs a rotation to the left

of the subtree whose root is

pointed to by localroot.

*/

binarytreepointer *plocalroot;

{

binarytreepointer q;

q = (*plocalroot)->rightptr;

(*plocalroot)->rightptr = q->leftptr;

q->leftptr = *plocalroot;

*plocalroot = q;

}

createnode(keyvalue,pp)

/* Returns with p pointing to a new node

record with key equal to keyvalue,

left and right pointer fields null, and balance field zero.

*/

whatever keyvalue;

binarytreepointer *pp;

{

*pp = malloc(sizeof(binarytreenode));

(*pp)->key = keyvalue;

(*pp)->leftptr = NULL;

(*pp)->rightptr = NULL;

(*pp)->balance = 0;

}

In order to determine the balance of t upon return from the recursive calls (these are underlined in the algorithm), we will need to know if the subtree in which the node was inserted has increased in depth. A flag increase, with values 1 or 0, indicates an increase. Increase may be nonlocal to, or a parameter of, the function, but it must be initialized to 0 before the function is invoked. We make it a parameter.

The functions for resetting balances are

reset1balance(q)

/* Sets the balance of node

q to zero.

*/

binarytreepointer q;

{

q->balance = 0;

}

reset2balances(q,t,p)

/* Resets the balance of nodes

p and q determined by the

balance of t.

*/

binarytreepointer q,t,p;

{

if(t->balance == -1)

p->balance = +1;

else

p->balance = 0;

if(t->balance == +1)

q->balance = -1;

else

q->balance = 0;

}

Suppose you wanted to create an AVL tree by reading a sequence of key values and to test the AVL insertion. The code below, when insertavl and the functions it invokes are included, creates the AVL tree using the recursive version of insertavl. It prints the key field values of the resultant tree nodes in preorder access order as the tree is created, and prints both the key and balance values in preorder access order for the final tree. The preorder traversal routine calls a process routine, as in Chapter 7, that prints out the key and balance field values of a node it is called to work on.

#include <stdio.h>

#define NULL 0

typedef int whatever;

typedef struct treenode

{

whatever key;

struct treenode *leftptr;

struct treenode *rightptr;

int balance;

}binarytreenode,*binarytreepointer;

binarytreepointer left(p)

/* Returns a copy of the left pointer

of the node pointed to by p.

*/

binarytreepointer p;

{

return(p->leftptr);

}

binarytreepointer right(p)

/* Returns a copy of the right pointer

of the node pointed to by p.

*/

binarytreepointer p;

{

return(p->rightptr);

}

info(l)

/* Returns the key field value of

the node pointed to by l.

*/

binarytreepointer l;

{

return(l->key);

}

bal(l)

/* Returns the balance field value of

the node pointed to by l.

*/

binarytreepointer l;

{

return(l->balance);

}

binarytreepointer setnull()

/* Returns a null pointer. */

{

return(NULL);

}

The function insertavl and all its invoked functions should go here, since they depend on the tree's implementation.

#define SENTINEL -1

main()

/* Reads record information, creates an AVL tree

storing the records, prints the key field values

of the tree records in preorder access order

after each record is inserted in the tree, and

prints the key & balance field values of the

final tree in preorder access order.

*/

{

binarytreepointer t,setnull();

int keyvalue,increase;

>Comment

t = setnull();

printf(\" enter keyvalue or sentinel \n");

>Comment

scanf("%d",&keyvalue);

while(keyvalue != SENTINEL)

}

increase = 0;

>Comment

insertavl(keyvalue,&t,&increase);

printf("\n enter keyvalue or sentinel \n");

>Comment

scanf("%d",&keyvalue);

>Comment

printtree(t);

}

>Comment

preorder (t);

}

printtree(t)

/* Prints the keys of t in

preorder access order.

*/

binarytreepointer t,left(),right();

{

printf("\n %d \n",info(l));

printtree(left(t));

printtree(right(t));

}

preorder(t)

/* Prints the keys & balances of

t in preorder access order.

*/

binarytreepointer t,left(),right(),setnull();

{

if(t == setnull())

printf("\n The tree is null \n");

else

{

process(t);

preorder(left(t));

preorder(right(t));

}

}

process(t)

/* Prints the key and balance fields of

the node pointed to by t.

*/

binarytreepointer t;

{

printf("\n key:%d balance:%d \n",info(t),bal(t));

}

Notice that all components of the program except insertavl and its associated functions are written treating the binary tree as a data abstraction.

Deletion procedures for AVL trees are not developed in detail here, but the basic ideas will be illustrated. Consider the AVL tree of Figure 9.16(a). The tree is a Fibonacci tree of depth 5. Suppose the rightmost node of its right subtree, 12, is deleted, as in a simple binary search tree. The result is Figure 9.16(b).

Figure 9.16 A Fibonacci Tree of Depth 5 and Deletion

Figure 9.16

Node 11, the predecessor of 12, is now unbalanced. Rebalancing can be achieved by applying remedy LR at node 11 to obtain Figure 9.16(c). Node 8, the predecessor of 11 before rebalancing, is now unbalanced. Rebalancing can be done by applying remedy LL at node 8 to obtain Figure 9.16(d).

This tree is finally an AVL tree. Deleting the rightmost node from any such Fibonacci tree will always lead to a rebalancing at every node along the search path.

The basic algorithm for deletion of a node from an AVL tree is first to delete, as in a simple binary tree. Then the search path must be retraced, from the deleted node back to the root. If an imbalance occurs, it must be remedied. Deletion can require O(1g n) rebalances, while insertion can require at most one rebalance.

A recursive deletion algorithm is sketched below.

To delete a node from t:

If t is not null, then

if keyvalue<t.key, then

delete the node from t.leftptr

if t is balanced, then

reset its balance

else

rebalance t and reset balances

else if keyvalue>t.key, then

delete the node from t.rightptr

if t is balanced, then

reset its balance

else

rebalance t and reset balances

else

delete the node with the largest key value in t.leftptr

replace the root of t by that deleted node

if t is balanced, then

reset its balance

else

rebalance t and reset balances.