Step 4: Rebalance

本文详细介绍了AVL树的插入操作及后续的平衡调整过程,重点解析了第三种情况,即当新节点被添加到不平衡节点较高子树时的处理方法。文章通过具体的案例分析,展示了不同情况下如何进行旋转操作以保持AVL树的平衡。

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.


转载于:https://www.cnblogs.com/sohu2000000/archive/2010/10/05/1842913.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值