Step 4: Rebalance

5.4.4 Step 4: Rebalance

We've covered steps 1 through 3 so far. Step 4, rebalancing, is somewhat complicated, but it's the key to the entire insertion procedure. It is also similar to, but simpler than, other rebalancing procedures we'll see later. As a result, we're going to discuss it in detail. Follow along carefully and it should all make sense.

Before proceeding, let's briefly review the circumstances under which we need to rebalance. Looking back a few sections, we see that there is only one case where this is required: case 3, when the new node is added in the taller subtree of a node with nonzero balance factor.

Case 3 is the case where y has a -2 or +2 balance factor after insertion. For now, we'll just consider the -2 case, because we can write code for the +2 case later in a mechanical way by applying the principle of symmetry. In accordance with this idea, step 4 branches into three cases immediately, one for each rebalancing case and a third that just returns from the function if no rebalancing is necessary:

 

151. <Step 4: Rebalance after AVL insertion 151> =if (y->avl_balance == -2){ 
    <Rebalance AVL tree after insertion in left subtree 152>
  }else if (y->avl_balance == +2){
    <Rebalance AVL tree after insertion in right subtree 157>
  }else
  return &n->avl_data;

See also 153 and 154.

This code is included in 146.

We will call y's left child x. The new node is somewhere in the subtrees of x. There are now only two cases of interest, distinguished on whether x has a + or - balance factor. These cases are almost entirely separate:

152. <Rebalance AVL tree after insertion in left subtree 152> =struct avl_node *x = y->avl_link[0];if (x->avl_balance == -1){ 
    <Rotate right at y in AVL tree 155>
  }else
  {
    <Rotate left at x then right at y in AVL tree 156>
  }

This code is included in 151 and 162.

In either case, w receives the root of the rebalanced subtree, which is used to update the parent's pointer to the subtree root (recall that z is the parent of y):

153. <Step 4: Rebalance after AVL insertion 151> +=z->avl_link[y != z->avl_link[0]] = w;

Finally, we increment the generation number, because the tree's structure has changed. Then we're done and we return to the caller:

154. <Step 4: Rebalance after AVL insertion 151> +=tree->avl_generation++;return &n->avl_data;
Case 1: x has - balance factor

For a - balance factor, we just rotate right at y. Then the entire process, including insertion and rebalancing, looks like this:

[Click here for plain-text rendering]

This figure also introduces a new graphical convention. The change in subtree a between the first and second diagrams is indicated by an asterisk (*).1 In this case, it indicates that the new node was inserted in subtree a.

The code here is similar to rotate_right() in the solution to Exercise 4.3-2:

155. <Rotate right at y in AVL tree 155> =w = x;y->avl_link[0] = x->avl_link[1];x->avl_link[1] = y;x->avl_balance = y->avl_balance = 0;

This code is included in 152 and 529.

Case 2: x has + balance factor

This case is just a little more intricate. First, let x's right child be w. Either w is the new node, or the new node is in one of w's subtrees. To restore balance, we rotate left at x, then rotate right at y (this is a kind of “double rotation”). The process, starting just after the insertion and showing the results of each rotation, looks like this:

[Click here for plain-text rendering]

At the beginning, the figure does not show the balance factor of w. This is because there are three possibilities:

Case 2.1: w has balance factor 0.
This means that w is the new node. a, b, c, and d have height 0. After the rotations, x and y have balance factor 0.
Case 2.2: w has balance factor -.
a, b, and d have height h > 0, and c has height h - 1.
Case 2.3: w has balance factor +.
a, c, and d have height h > 0, and b has height h - 1.

156. <Rotate left at x then right at y in AVL tree 156> =assert (x->avl_balance == +1);w = x->avl_link[1];x->avl_link[1] = w->avl_link[0];w->avl_link[0] = x;y->avl_link[0] = w->avl_link[1];w->avl_link[1] = y;if (w->avl_balance == -1) 
  x->avl_balance = 0, y->avl_balance = +1;else if (w->avl_balance == 0)
  x->avl_balance = y->avl_balance = 0;else /* w->avl_balance == +1 */
  x->avl_balance = -1, y->avl_balance = 0;w->avl_balance = 0;

This code is included in 152, 177, 307, 427, and 530.

Exercises:

1. Why can't the new node be x rather than a node in x's subtrees?
[answer]
Because then y's right subtree would have height 1, so there's no way that y could have a +2 balance factor.

2. Why can't x have a 0 balance factor?
[answer]
The value of y is set during the search for item to point to the closest node above the insertion point that has a nonzero balance factor, so any node below y along this search path, including x, must have had a 0 balance factor originally. All such nodes are updated to have a nonzero balance factor later, during step 3. So x must have either a - or + balance factor at the time of rebalancing.

3. For each subcase of case 2, draw a figure like that given for generic case 2 that shows the specific balance factors at each step.
[answer]

3.1.

[Click here for plain-text rendering]

3.2.

[Click here for plain-text rendering]

3.3.

[Click here for plain-text rendering]

4. Explain the expression z->avl_link[y != z->avl_link[0]] = w in the second part of <Step 4: Rebalance after AVL insertion 151> above. Why would it be a bad idea to substitute the apparent equivalent z->avl_link[y == z->avl_link[1]] = w?
[answer]
w should replace y as the left or right child of z. y != z->avl_link[0] has the value 1 if y is the right child of z, or 0 if y is the left child. So the overall expression replaces y with w as a child of z.
The suggested substitution is a poor choice because if z == (struct avl_node *) &tree->root, z->avl_link[1] is undefined.【注意,这就是为什么不使用==的原因】

5. Suppose that we wish to make a copy of an AVL tree, preserving the original tree's shape, by inserting nodes from the original tree into a new tree, using avl_probe(). Will inserting the original tree's nodes in level order (see the answer to Exercise 4.7-4) have the desired effect?
[answer]
Yes.

3. The first item to be inserted have the value of the original tree's root. After that, at each step, we can insert either an item with the value of either child x of any node in the original tree corresponding to a node y already in the copy tree, as long as x's value is not already in the copy tree.

4. The function below traverses tree in “level order”. That is, it visits the root, then the root's children, then the children of the root's children, and so on, so that all the nodes at a particular level in the tree are visited in sequence.

See also: [Sedgewick 1998], Program 5.16.

621. <Level-order traversal 621> =/* Calls visit for each of the nodes in tree in level order.   Returns nonzero if successful, zero if out of memory. */static int 
bst_traverse_level_order (struct bst_table *tree, bst_item_func *visit)
{ struct bst_node **queue; size_t head, tail; if (tree->bst_count == 0) return 1; queue = tree->bst_alloc->libavl_malloc (tree->bst_alloc,
                                          sizeof *queue * tree->bst_count); if (queue == NULL) return 0; head = tail = 0; queue[head++] = tree->bst_root; while (head != tail)
    { struct bst_node *cur = queue[tail++]; visit (cur->bst_data, tree->bst_param); if (cur->bst_link[0] != NULL) queue[head++] = cur->bst_link[0]; if (cur->bst_link[1] != NULL) queue[head++] = cur->bst_link[1]; } tree->bst_alloc->libavl_free (tree->bst_alloc, queue); return 1;}
刘峰六:level_order的遍历实现使用“队列控序”技术

Footnotes

[1] A “prime” (') is traditional, but primes are easy to overlook.


posted on 2010-10-05 01:27  sohu2000000  阅读(216)  评论(0编辑  收藏  举报

导航