随笔 - 50  文章 - 8  trackbacks - 0
<2026年6月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿

随笔档案

Blogs

搜索

  •  

最新评论

阅读排行榜

评论排行榜

5.5.1 Step 1: Search

The only difference between this search and an ordinary search in a BST is that we have to keep track of the nodes above the one we're deleting. We do this by pushing them onto the stack defined above. Each iteration through the loop compares item to p's data, pushes the node onto the stack, moves down in the proper direction. The first trip through the loop is something of an exception: we hard-code the comparison result to -1 so that the pseudo-root node is always the topmost node on the stack. When we find a match, we set item to the actual data item found, so that we can return it later.

165. <Step 1: Search AVL tree for item to delete 165> =
k = 0;
p = (struct avl_node *) &tree->avl_root;
for (cmp = -1; cmp != 0; 
     cmp = tree->avl_compare (item, p->avl_data, tree->avl_param))
  { int dir = cmp > 0; pa[k] = p; da[k++] = dir; p = p->avl_link[dir]; if (p == NULL) return NULL; } item = p->avl_data;

This code is included in 164 and 220.

posted @ 2010-10-06 04:41 sohu2000000 阅读(73) | 评论 (0)编辑 收藏

旋转膝落:(空投)↑以外+C或D 
杰克小刀踢:→+B

clip_image002
飞之技巧:(跳跃中)↓+D

clip_image004 clip_image006
雷韧拳:↓↘→+A或C 
clip_image008

空中雷韧拳:(跳跃中)↓↘→+A或C 
clip_image010
真空片手驹:↓↙←+A或C 
clip_image012clip_image014
超级闪电踢:→↓↘+B或D

clip_image016 clip_image018
居合蹴:↓↘→+B或D 
clip_image020 clip_image022

反动三段蹴:→↘↓↙←+B或D 

clip_image024 clip_image026
红丸投:(近身)→↘↓↙←→+A或C 
clip_image028clip_image030

雷光拳:↓↘→↓↘→+A或C 
clip_image032clip_image034
大发电者:(近身)→↘↓↙←→↘↓↙←+A或C 
clip_image036 clip_image038

posted @ 2010-10-06 04:38 sohu2000000 阅读(113) | 评论 (0)编辑 收藏

5.5 Deletion

Deletion in an AVL tree is remarkably similar to insertion. The steps that we go through are analogous:

  1. Search for the item to delete.
  2. Delete the item.
  3. Update balance factors.
  4. Rebalance the tree, if necessary.
  5. Finish up and return.

The main difference is that, after a deletion, we may have to rebalance at more than one level of a tree, starting from the bottom up. This is a bit painful, because it means that we have to keep track of all the nodes that we visit as we search for the node to delete, so that we can then move back up the tree. The actual updating of balance factors and rebalancing steps are similar to those used for insertion.

The following sections cover deletion from an AVL tree in detail. Before we get started, here's an outline of the function.

164. <AVL item deletion function 164> =
void *
avl_delete (struct avl_table *tree, const void *item)
{ /* Stack of nodes. */ struct avl_node *pa[AVL_MAX_HEIGHT]; /* Nodes. */ unsigned char da[AVL_MAX_HEIGHT]; /* avl_link[] indexes. */ int k; /* Stack pointer. */ struct avl_node *p; /* Traverses tree to find node to delete. */ int cmp; /* Result of comparison between item and p. */ assert (tree != NULL && item != NULL); <Step 1: Search AVL tree for item to delete 165> <Step 2: Delete item from AVL tree 166> <Steps 3–4: Update balance factors and rebalance after AVL deletion 171> <Step 5: Finish up and return after AVL deletion 176> }

This code is included in 145.

See also: [Knuth 1998b], pages 473–474; [Pfaff 1998].

posted @ 2010-10-06 04:14 sohu2000000 阅读(42) | 评论 (0)编辑 收藏

低踢:→+B 
clip_image002
滑步:↘+B 
clip_image004
旋风拳:←↙↓↘→+A或C 
clip_image006
爆烈拳:A或C连按
clip_image008 clip_image010
爆烈拳终结:爆烈拳中↓↘→+A或C 
clip_image012 clip_image014
虎破脚:→↓↘+B或D 
clip_image016clip_image018
电光踢:←↙↓↘→+B或D 
clip_image020 clip_image022
黄金之踵落:↓↙←+B或D 
clip_image024
*死亡龙卷风:↓↘→↓↘→+A或C 
clip_image026
*爆烈飓风猛虎踢:↓↘→↘↓↙←+A或C 
clip_image028 clip_image030 clip_image032 clip_image034 clip_image036 clip_image038

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/asiainfolf/archive/2010/10/05/5922701.aspx

posted @ 2010-10-05 18:16 sohu2000000 阅读(87) | 评论 (0)编辑 收藏

低踢:→+B
clip_image002
滑步:↘+B
clip_image004
旋风拳:←↙↓↘→+A或C
clip_image006
爆烈拳:A或C连按
clip_image008 clip_image010
爆烈拳终结:爆烈拳中↓↘→+A或C
clip_image012 clip_image014
虎破脚:→↓↘+B或D
clip_image016clip_image018
电光踢:←↙↓↘→+B或D
clip_image020 clip_image022
黄金之踵落:↓↙←+B或D
clip_image024
*死亡龙卷风:↓↘→↓↘→+A或C
clip_image026
*爆烈飓风猛虎踢:↓↘→↘↓↙←+A或C
clip_image028 clip_image030 clip_image032 clip_image034 clip_image036 clip_image038

posted @ 2010-10-05 18:00 sohu2000000 阅读(70) | 评论 (0)编辑 收藏

5.4.6 Example

We're done with writing the code. Now, for clarification, let's run through an example designed to need lots of rebalancing along the way. Suppose that, starting with an empty AVL tree, we insert 6, 5, and 4, in that order. The first two insertions do not require rebalancing. After inserting 4, rebalancing is needed because the balance factor of node 6 would otherwise become -2, an invalid value. This is case 1, so we perform a right rotation on 6. So far, the AVL tree has evolved this way:

[Click here for plain-text rendering]

If we now insert 1, then 3, a double rotation (case 2.1) becomes necessary, in which we rotate left at 1, then rotate right at 4:

[Click here for plain-text rendering]

Inserting a final item, 2, requires a right rotation (case 1) on 5:

[Click here for plain-text rendering]

posted @ 2010-10-05 16:00 sohu2000000 阅读(45) | 评论 (0)编辑 收藏

5.4.5 Symmetric Case

Finally, we need to write code for the case that we chose not to discuss earlier, where the insertion occurs in the right subtree of y. All we have to do is invert the signs of balance factors and switch avl_link[] indexes between 0 and 1. The results are this:

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

This code is included in 151 and 162.

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

This code is included in 157 and 532.

159. <Rotate right at x then left at y in AVL tree 159> =
assert (x->avl_balance == -1);
w = x->avl_link[0];
x->avl_link[0] = w->avl_link[1];
w->avl_link[1] = x;
y->avl_link[1] = w->avl_link[0];
w->avl_link[0] = 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 157, 174, 310, 428, and 533.

posted @ 2010-10-05 15:59 sohu2000000 阅读(53) | 评论 (0)编辑 收藏

5.4.3 Step 3: Update Balance Factors

When we add a new node n to an AVL tree, the balance factor of n's parent must change, because the new node increases the height of one of the parent's subtrees. The balance factor of n's parent's parent may need to change, too, depending on the parent's balance factor, and in fact the change can propagate all the way up the tree to its root.

At each stage of updating balance factors, we are in a similar situation. First, we are examining a particular node p that is one of n's direct ancestors. The first time around, p is n's parent, the next time, if necessary, p is n's grandparent, and so on. Second, the height of one of p's subtrees has increased, and which one can be determined using da[].

In general, if the height of p's left subtree increases, p's balance factor decreases. On the other hand, if the right subtree's height increases, p's balance factor increases. If we account for the three possible starting balance factors and the two possible sides, there are six possibilities. The three of these corresponding to an increase in one subtree's height are symmetric with the others that go along with an increase in the other subtree's height. We treat these three cases below.

Case 1: p has balance factor 0

If p had balance factor 0, its new balance factor is - or +, depending on the side of the root to which the node was added. After that, the change in height propagates up the tree to p's parent (unless p is the tree's root) because the height of the subtree rooted at p's parent has also increased.

The example below shows a new node n inserted as the left child of a node with balance factor 0. On the far left is the original tree before insertion; in the middle left is the tree after insertion but before any balance factors are adjusted; in the middle right is the tree after the first adjustment, with p as n's parent; on the far right is the tree after the second adjustment, with p as n's grandparent. Only in the trees on the far left and far right are all of the balance factors correct.

[Click here for plain-text rendering]

Case 2: p's shorter subtree has increased in height

If the new node was added to p's shorter subtree, then the subtree has become more balanced and its balance factor becomes 0. If p started out with balance factor +, this means the new node is in p's left subtree. If p had a - balance factor, this means the new node is in the right subtree. Since tree p has the same height as it did before, the change does not propagate up the tree any farther, and we are done. Here's an example that shows pre-insertion and post-balance factor updating views:

[Click here for plain-text rendering]

Case 3: p's taller subtree has increased in height

If the new node was added on the taller side of a subtree with nonzero balance factor, the balance factor becomes +2 or -2. This is a problem, because balance factors in AVL trees must be between -1 and +1. We have to rebalance the tree in this case. We will cover rebalancing later. For now, take it on faith that rebalancing does not increase the height of subtree p as a whole, so there is no need to propagate changes any farther up the tree.

Here's an example of an insertion that leads to rebalancing. On the left is the tree before insertion; in the middle is the tree after insertion and updating balance factors; on the right is the tree after rebalancing to. The -2 balance factor is shown as two minus signs (--). The rebalanced tree is the same height as the original tree before insertion.

[Click here for plain-text rendering]

As another demonstration that the height of a rebalanced subtree does not change after insertion, here's a similar example that has one more layer of nodes. The trees below follow the same pattern as the ones above, but the rebalanced subtree has a parent. Even though the tree's root has the wrong balance factor in the middle diagram, it turns out to be correct after rebalancing.

[Click here for plain-text rendering]

Implementation

Looking at the rules above, we can see that only in case 1, where p's balance factor is 0, do changes to balance factors continue to propagate upward in the tree. So we can start from n's parent and move upward in the tree, handling case 1 each time, until we hit a nonzero balance factor, handle case 2 or case 3 at that node, and we're done (except for possible rebalancing afterward).

Wait a second—there is no efficient way to move upward in a binary search tree!1 Fortunately, there is another approach we can use. Remember the extra code we put into <Step 1: Search AVL tree for insertion point 148>? This code kept track of the last node we'd passed through that had a nonzero balance factor as s. We can use s to move downward, instead of upward, through the nodes whose balance factors are to be updated.

Node s itself is the topmost node to be updated; when we arrive at node n, we know we're done. We also kept track of the directions we moved downward in da[]. Suppose that we've got a node p whose balance factor is to be updated and a direction d that we moved from it. We know that if we moved down to the left (d == 0) then the balance factor must be decreased, and that if we moved down to the right (d == 1) then the balance factor must be increased.

Now we have enough knowledge to write the code to update balance factors. The results are almost embarrassingly short:

150. <Step 3: Update balance factors after AVL insertion 150> =
for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++)
if (da[k] == 0)
p->avl_balance–;
else 
    p->avl_balance++;

This code is included in 146, 301, and 419.

Now p points to the new node as a consequence of the loop's exit condition. Variable p will not be modified again in this function, so it is used in the function's final return statement to take the address of the new node's avl_datamember (see <AVL item insertion function 146> above).

Exercises:

1. Can case 3 be applied to the parent of the newly inserted node?
[answer]
No. Suppose that n is the new node, that p is its parent, and that p has a - balance factor before n's insertion (a similar argument applies if p's balance factor is +). Then, for n's insertion to decrease p's balance factor to -2, n would have to be the left child of p. But if p had a - balance factor before the insertion, it already had a left child, so n cannot be the new left of p. This is a contradiction, so case 3 will never be applied to the parent of a newly inserted node.

2. For each of the AVL trees below, add a new node with a value smaller than any already in the tree and update the balance factors of the existing nodes. For each balance factor that changes, indicate the numbered case above that applies. Which of the trees require rebalancing after the insertion?

[Click here for plain-text rendering]

[answer]

[Click here for plain-text rendering]

In the leftmost tree, case 2 applies to the root's left child and the root's balance factor does not change. In the middle tree, case 1 applies to the root's left child and case 2 applies to the root. In the rightmost tree, case 1 applies to the root's left child and case 3 applies to the root. The tree on the right requires rebalancing, and the others do not.

3. Earlier versions of libavl used chars, not unsigned chars, to cache the results of comparisons, as the elements of da[] are used here. At some warning levels, this caused the GNU C compiler to emit the warning “array subscript has type `char'” when it encountered expressions like q->avl_link[da[k]]. Explain why this can be a useful warning message.
[answer]
Type char may be signed or unsigned, depending on the C compiler and/or how the C compiler is run. Also, a common use for subscripting an array with a character type is to translate an arbitrary character to another character or a set of properties. For example, this is a common way to implement the standard C functions from ctype.h. This means that subscripting such an array with a char value can have different behavior when char changes between signed and unsigned with different compilers (or with the same compiler invoked with different options).

4. If our AVL trees won't ever have a height greater than 32, then we can portably use the bits in a single unsigned long to compactly store what the entire da[] array does. Write a new version of step 3 to use this form, along with any necessary modifications to other steps and avl_probe()'s local variables.
[answer]

Here is one possibility:

650. <Step 3: Update balance factors after AVL insertion, with bitmasks 650> =
for (p = y; p != n; p = p->avl_link[cache & 1], cache >>= 1)
  if ((cache & 1) == 0)
    p->avl_balance–;
  else 
    p->avl_balance++;

Also, replace the declarations of da[] and k by these:

unsigned long cache = 0; /* Cached comparison results. */
int k = 0;              /* Number of cached comparison results. */

and replace the second paragraph of code within the loop in step 1 by this:

if (p->avl_balance != 0)
  z = q, y = p, cache = 0, k = 0;

dir = cmp > 0;
if (dir)
  cache |= 1ul << k;
k++;

It is interesting to note that the speed difference between this version and the standard version was found to be negligible, when compiled with full optimization under GCC (both 2.95.4 and 3.0.3) on x86.

刘峰六的答案:
这个问题主要涉及的是dir方向的回朔存储的向量组的问题,所以只要更改回朔路径向量da[]就可以了,step3和标准答案略有不同,step1我原来的答案有误,忘了考虑要保留置位后的值了,所以必须使用”|”
unsigned long da;
//step1
for(……..)
{
    ……
    if(p->avl_balance != 0)
       z=q,y=p,k=0;
    da |= (0x1<<k); // 用|来置位
}

//step3
for(p=y,k=0;p!=n;p->avl_link[ (da>>(k-1))&0x1 ]) ], k++)
     if((da>>(k-1))& 0x1 )
       ……


Footnotes

[1] We could make a list of the nodes as we move down the tree and reuse it on the way back up. We'll do that for deletion, but there's a simpler way for insertion, so keep reading.


posted @ 2010-10-05 01:43 sohu2000000 阅读(964) | 评论 (0)编辑 收藏

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 @ 2010-10-05 01:43 sohu2000000 阅读(1200) | 评论 (0)编辑 收藏

5.4.2 Step 2: Insert

Following the search loop, q is the last non-null node examined, so it is the parent of the node to be inserted. The code below creates and initializes a new node as a child of q on side dir, and stores a pointer to it into n. Compare this code for insertion to that within <BST item insertion function 32>.

149. <Step 2: Insert AVL node 149> =
n = q->avl_link[dir] = 
  tree->avl_alloc->libavl_malloc (tree->avl_alloc, sizeof *n); if (n == NULL) return NULL; tree->avl_count++; n->avl_data = item; n->avl_link[0] = n->avl_link[1] = NULL; n->avl_balance = 0; if (y == NULL) return &n->avl_data;

This code is included in 146.

Exercises:

1. How can y be NULL? Why is this special-cased?
[answer]

Variable y is only modified within <Step 1: Search AVL tree for insertion point 148>. If y is set during the loop, it is set to p, which is always a non-null pointer within the loop. So y can only be NULL if it is last set before the loop begins. If that is true, it will be NULL only if tree->avl_root == NULL. So, variable y can only be NULL if the AVL tree was empty before the insertion.

A NULL value for y is a special case because later code assumes that y points to a node.

posted @ 2010-10-05 01:42 sohu2000000 阅读(874) | 评论 (0)编辑 收藏
仅列出标题
共5页: 1 2 3 4 5