4.md 25.9 KB
Newer Older
W
wizardforcel 已提交
1
# 四、AVL 树
W
wizardforcel 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

## 平衡树概述

AVL 树是一个自平衡的二叉查找树。它是以它的发明者 G. M .阿德尔森-维尔斯基和 E. M .兰迪斯的名字命名的,他们在 1962 年首次描述了结构和相关的算法。像本系列第一本书中介绍的二叉查找树一样,AVL 树维护这三个简单的规则:

*   树中的每个节点最多有两个子节点(“左”和“右”子节点)。
*   小于当前节点的值将向左移动。
*   大于或等于当前节点的值将位于右侧。

此外,AVL 树添加了一个新规则:

*   左右节点的高度相差不会超过 1。

### 什么是节点高度?

可以测量任何两个相关节点之间的距离。

W
wizardforcel 已提交
19
![](img/image031.jpg)
W
wizardforcel 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

图 30:节点高度

上图显示了一个二叉树,其中的线表示每组节点的级别。根节点 6 没有父节点,因此处于级别 1。节点 4 和 8 是同级节点,都处于级别 2。节点 3 和 5 处于第 3 级。

使用这些级别,我们可以很容易地找到任何两个相关节点之间的距离。例如,6 和 5 之间的距离是 2(因为节点 6 是级别 1,节点 5 是级别 3;3 减 1 等于 2)。也可以测量非根节点之间的距离。节点 4 和 3 之间的距离是 1。

当谈论一个节点的子树的高度时,我们通常指的是整个子树的最大高度。

例如,在图 30 中,根节点的高度为 2,因为根(级别 1)和最深叶节点(级别 3)之间的最大距离为 2。

有了这样的理解,让我们重新审视新的规则:左右节点的高度差永远不会超过 1。

这意味着左边的子节点和右边的子节点的最大高度相差不超过一个,并且该规则适用于树中的每个节点。看到一些无效的平衡树可能有助于使这一点更加清晰。

W
wizardforcel 已提交
35
![](img/image032.jpg)
W
wizardforcel 已提交
36 37 38 39 40 41 42

图 31:根节点的左侧高度为 2,右侧高度为 0。

这个树是不平衡的,因为根节点的右边子节点的高度是 2,左边子节点的高度是 0(因为没有左边子节点)。

请注意,节点 4 本身是平衡的,因为它的左右高度都是一。

W
wizardforcel 已提交
43
![](img/image033.jpg)
W
wizardforcel 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

图 32:根节点是平衡的,但是它的右子节点不是

在本例中,根节点的左侧高度为 2,右侧高度为 3。虽然这看起来是平衡的,但是请记住,规则需要递归地应用于树中的每个节点。节点 7 不平衡。它的右侧高度为 2,左侧高度为 0。

## 平衡算法

了解了平衡的含义后,我们现在需要了解确保树平衡的机制。这有两个步骤。第一个决定操作是否会使树处于不平衡状态。这是通过在更改树结构的操作(例如,添加或删除)期间调查左右子节点高度来完成的。第二种方法通过一个称为节点旋转的过程来平衡树。

AVL 树使用两个基本的节点旋转(左和右)和两个使用基本旋转的复合旋转。

### 右旋转

右旋转是从左向右旋转节点的行为,如下图所示:

W
wizardforcel 已提交
59
![](img/image034.jpg)
W
wizardforcel 已提交
60 61 62

图 33:右旋转将根的左子级移动到根位置

W
wizardforcel 已提交
63
![](img/image035.jpg)
W
wizardforcel 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

图 34:右旋转将根的左子级的右子级移动到旧根的左子级

右旋转的算法有三个步骤。这些步骤都是从原根节点(B)的角度出发的。

1.  将当前根替换为它最左边的子根(这将成为新根)。
    *   节点 B 将其左子节点(A)移动到其位置。
2.  将新根的右子级移动到旧根的左子级。
    *   这将把“乙”改为“丁”
3.  将旧根(D)指定为新根(B)的右子。

### 向左旋转

向左旋转是旋转一个向右运行的节点系列的行为,这样系列的中间部分就变成了新的根。

W
wizardforcel 已提交
79
![](img/image036.jpg)
W
wizardforcel 已提交
80 81 82

图 35:向左旋转将 B 节点从序列的中间移到根

W
wizardforcel 已提交
83
![](img/image037.jpg)
W
wizardforcel 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

图 36:向左旋转,新根的左子级移动到旧根的右子级

向左旋转的算法有三个步骤。这些步骤都是从原根节点(A)的角度出发的。

1.  将当前根替换为其直接的右子根(新根)。
    *   这会将 A 节点的右子节点 C 移动到它的位置。
2.  将新根的(C)左子级(B)移动到旧根的(A)右子级。
    *   这将 C > B 更改为 A > B
3.  将旧根(A)指定为新根(C)的左子。

### 左右旋转

左右旋转对根节点的右子节点应用右旋转,然后在根节点应用左旋转。我们究竟为什么要这样做?考虑以下树结构:

W
wizardforcel 已提交
99
![](img/image038.jpg)
W
wizardforcel 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112

图 37:需要左右旋转的不平衡树

特别要注意的是,这棵树的右边的孩子有左边的孩子,没有右边的孩子。这就是这种情况与前面左右旋转示例中显示的示例树的区别。

树太重了,所以我们的本能反应是向右旋转。使用前面描述的右旋转算法,我们执行以下操作:

1.  将 C 节点移动到根位置。
2.  把 B 从 C 的左孩子移到 A 的右孩子。
3.  将 A 指定为 C 的左子级。

不幸的是,当我们这样做时,结果树仍然是不平衡的。

W
wizardforcel 已提交
113
![](img/image039.jpg)
W
wizardforcel 已提交
114 115 116 117 118 119 120

图 38:应用左旋转后,树仍然不平衡

在这一点上,天真的解决方案是应用右旋转,这将使我们回到我们开始的地方。相反,我们需要通过首先对正确的子对象执行正确的旋转来改变树结构。

因为我们正在执行右子节点(C)的右旋转,所以我们可以暂时忽略根节点(A)。

W
wizardforcel 已提交
121
![](img/image040.jpg)
W
wizardforcel 已提交
122 123 124 125 126 127 128

图 39:右旋转前后的右子树。

右旋转将 B 节点移动到根位置,将 C 移动到其右子节点,并将旧根(C)指定为新根(B)的新右子节点。

在较大的树的上下文中,我们的旋转变换了树,如下所示:

W
wizardforcel 已提交
129
![](img/image041.jpg)
W
wizardforcel 已提交
130 131 132 133 134

图 40:在右边的孩子身上进行的右旋转

结果树(A > B > C)是我们知道如何使用左旋转算法旋转的东西。整个过程如下所示:

W
wizardforcel 已提交
135
![](img/image042.jpg)
W
wizardforcel 已提交
136 137 138 139 140 141 142

图 41:整个左右旋转过程

### 左右旋转

左右旋转只是与左右旋转相反。我们从一个不平衡的树开始,其中根节点有一个左子节点,它有一个右子节点,但没有左子节点。一旦我们确定了这种情况,解决方案是对左子对象应用左旋转,然后对根对象应用右旋转。整个左右旋转过程如下图所示:

W
wizardforcel 已提交
143
![](img/image043.jpg)
W
wizardforcel 已提交
144 145 146 147 148 149 150 151 152 153 154 155 156 157

图 42:整个左右旋转过程

### 重量和平衡系数

现在我们已经知道了四种旋转类型,我们需要知道当一棵树被确定为不平衡时,应该选择哪一种。这是通过两种测量完成的:

*   重量
*   平衡系数

沉重很简单,就是左右子节点高度之差。如果一棵树的左高较大,则称之为“左重”。反之,如果它有较大的右高,则称之为“右重”。

平衡系数是左右高度之差。例如,如果树的右子节点高度为 5,左子节点高度为 3,则平衡因子为 2。同样,如果高度颠倒,平衡系数将为-2。

W
wizardforcel 已提交
158
![](img/image044.jpg)
W
wizardforcel 已提交
159 160 161

图 43:右重节点(A)。它的右边的孩子(C)有一个平衡因子-1,因为平衡因子是 C (0)的右边高度和左边高度(1)的差值。

W
wizardforcel 已提交
162
![](img/image045.jpg)
W
wizardforcel 已提交
163 164 165 166 167 168 169 170 171 172 173

图 44:一个右重节点(A)。它的右孩子(B)的平衡系数为 1,因为它的右孩子身高(1)和左孩子身高(0)之差为 1。

我们将在[平衡](#heading_id_89)[旋转方法](#heading_id_90)部分看到这一切是如何结合在一起的,其中显示了用于确定运行哪个旋转和实际旋转的代码。

## 反斜线 Class

`AVLTreeNode`是 AVL 树中的单个节点。它提供属性来访问节点的左右子节点、节点的值以及节点所在的树的引用。

此外,它还提供了确定左右儿童身高、其[重量和平衡系数](#heading_id_89)所需的所有方法,并提供了执行[四旋转](#heading_id_90)类型的方法。

W
wizardforcel 已提交
174
```cs
W
wizardforcel 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    public class AVLTreeNode<TNode> : IComparable<TNode>
        where TNode : IComparable
    {
        AVLTree<TNode> _tree;
        AVLTreeNode<TNode> _left;
        AVLTreeNode<TNode> _right;

        public AVLTreeNode(TNode value, AVLTreeNode<TNode> parent, AVLTree<TNode> tree)
        {
            Value = value;
            Parent = parent;
            _tree = tree;
        }

        public AVLTreeNode<TNode> Left
        {
            get
            {
                return _left;
            }
            internal set
            {
                _left = value;
                if (_left != null)
                {
                    _left.Parent = this;
                }
            }
        }

        public AVLTreeNode<TNode> Right
        {
            get
            {
                return _right;
            }
            internal set
            {
                _right = value;
                if (_right != null)
                {
                    _right.Parent = this;
                }
            }
        }

        public AVLTreeNode<TNode> Parent { get; internal set; }
        public TNode Value { get; private set; }

        /// <summary>
        /// Compares the current node to the provided value.
        /// </summary>
        /// <param name="other">The node value to compare to.</param>
        /// <returns>1 if the instance value is greater than the provided value, -1 if less, or 0 if equal.</returns>
        public int CompareTo(TNode other)
        {
            return Value.CompareTo(other);
        }

        // The rest of the code for the AVLTreeNode class is
        // in the subsequent sections of this chapter.
    }

```

### 平衡

平衡算法确定是否需要旋转,如果需要,需要执行什么旋转。它首先检查树是否不平衡。

不平衡是通过检查树是右重还是左重来确定的。如果一棵树的右高比左高多 1,它就是右重。相反,如果它的左高度比右高度大 1 以上,则它是左重的。

如果树被确定为不平衡,我们接下来需要确定简单的向右或向左旋转是否足够,或者是否需要向右-向左或向左-向右旋转。

这个决定是通过查看根节点的左或右子节点做出的,这取决于它是右还是左重。接下来,看看平衡因素。这两个比较用于选择旋转算法。

W
wizardforcel 已提交
250
| ![](img/Note.png) | 注意:在这个实现中,树的高度是通过遍历树以找到最长的路径来确定的。这在概念上很简单,但却没有它应有的效率。作为一个练习,考虑可以更有效地确定任何节点高度的其他方法。 |
W
wizardforcel 已提交
251

W
wizardforcel 已提交
252
```cs
W
wizardforcel 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
    internal void Balance()
    {
        if (State == TreeState.RightHeavy)
        {
            if (Right != null && Right.BalanceFactor < 0)
            {
                LeftRightRotation();
            }
            else
            {
                LeftRotation();
            }
        }
        else if (State == TreeState.LeftHeavy)
        {
            if (Left != null && Left.BalanceFactor > 0)
            {
                RightLeftRotation();
            }
            else
            {
                RightRotation();
            }
        }
    }

    private int MaxChildHeight(AVLTreeNode<TNode> node)
    {
        if (node != null)
        {
            return 1 + Math.Max(MaxChildHeight(node.Left), MaxChildHeight(node.Right));
        }

        return 0;
    }

    private int LeftHeight
    {
        get
        {
            return MaxChildHeight(Left);
        }
    }

    private int RightHeight
    {
        get
        {
            return MaxChildHeight(Right);
        }
    }

    private TreeState State
    {
        get
        {
            if (LeftHeight - RightHeight > 1)
            {
                return TreeState.LeftHeavy;
            }

            if (RightHeight - LeftHeight > 1)
            {
                return TreeState.RightHeavy;
            }

            return TreeState.Balanced;
        }
    }

    private int BalanceFactor
    {
        get
        {
            return RightHeight - LeftHeight;
        }
    }

    enum TreeState
    {
        Balanced,
        LeftHeavy,
        RightHeavy,
    }

```

### 旋转方法

旋转是在[平衡算法](#heading_id_34)部分讨论的算法的实现。`ReplaceRoot`方法处理将子节点移动到根节点位置的机制。原始根的父节点需要更新以指向新的根节点。如果没有父节点,则需要让包含树知道新的树根节点。

W
wizardforcel 已提交
344
```cs
W
wizardforcel 已提交
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    private void LeftRotation()
    {
        //     a
        //      \
        //       b
        //        \
        //         c
        //
        // becomes
        //       b
        //      / \
        //     a   c

        AVLTreeNode<TNode> newRoot = Right;

        // Replace the current root with the new root.
        ReplaceRoot(newRoot);

        // Take ownership of right's left child as right (now parent).
        Right = newRoot.Left;

        // The new root takes this as its left.
        newRoot.Left = this;
    }

    private void RightRotation()
    {
        //     c (this)
        //    /
        //   b
        //  /
        // a
        //
        // becomes
        //       b
        //      / \
        //     a   c

        AVLTreeNode<TNode> newRoot = Left;

        // Replace the current root with the new root.
        ReplaceRoot(newRoot);

        // Take ownership of left's right child as left (now parent).
        Left = newRoot.Right;

        // The new root takes this as its right.
        newRoot.Right = this;
    }

    private void LeftRightRotation()
    {
        Right.RightRotation();
        LeftRotation();
    }

    private void RightLeftRotation()
    {
        Left.LeftRotation();
        RightRotation();
    }

    private void ReplaceRoot(AVLTreeNode<TNode> newRoot)
    {
        if (this.Parent != null)
        {
            if (this.Parent.Left == this)
            {
                this.Parent.Left = newRoot;
            }
            else if (this.Parent.Right == this)
            {
                this.Parent.Right = newRoot;
            }
        }
        else
        {
            _tree.Head = newRoot;
        }

        newRoot.Parent = this.Parent;
        this.Parent = newRoot;
    }

```

## AVLTree 类

AVL 树类提供了基本的集合方法来添加、移除、枚举和检查树中是否存在值。此外,它还提供了清除树和获取树中节点数的功能。

W
wizardforcel 已提交
435
```cs
W
wizardforcel 已提交
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
    public class AVLTree<T> : IEnumerable<T>
        where T : IComparable
    {
        public AVLTreeNode<T> Head { get;  internal set; }

        public void Add(T value);

        public bool Contains(T value);

        public bool Remove(T value);

        public IEnumerator<T> GetEnumerator();

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator();

        public void Clear();

        public int Count { get;  private set; }
    }

```

### 添加

| 行为 | 将特定值添加到树中,确保方法完成时树处于平衡状态。 |
| 表演 | *O* (日志 *n* ) |

AVL 树`Add`操作与二叉查找树加法操作几乎相同。它使用本章前面描述的[规则](#Rules)在树上行走。一旦找到合适的节点位置,该节点就被插入到树中。由于树现在可能是不平衡的,节点执行平衡操作以确保当`Add`方法返回时树是平衡的。

W
wizardforcel 已提交
465
```cs
W
wizardforcel 已提交
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
    public void Add(T value)
    {
        // Case 1: The tree is empty--allocate the head.
        if (Head == null)
        {
            Head = new AVLTreeNode<T>(value, null, this);
        }
        // Case 2: The tree is not empty--find the right location to insert.
        else
        {
            AddTo(Head, value);
        }

        Count++;
    }

    // Recursive add algorithm.
    private void AddTo(AVLTreeNode<T> node, T value)
    {
        // Case 1: Value is less than the current node value.
        if (value.CompareTo(node.Value) < 0)
        {
            // If there is no left child, make this the new left.
            if (node.Left == null)
            {
                node.Left = new AVLTreeNode<T>(value, node, this);
            }
            else
            {
                // Else, add it to the left node.
                AddTo(node.Left, value);
            }
        }
        // Case 2: Value is equal to or greater than the current value.
        else
        {
            // If there is no right, add it to the right.
            if (node.Right == null)
            {
                node.Right = new AVLTreeNode<T>(value, node, this);
            }
            else
            {
                // Else, add it to the right node.
                AddTo(node.Right, value);
            }
        }

        node.Balance();
    }

```

### 包含

| 行为 | 如果树中包含特定值,则返回`true`。否则返回`false`。 |
| 表演 | *O* (日志 *n* ) |

一个传统的二叉查找树有 *O* ( *n* )最坏情况的复杂性,因为一个病态的不平衡树基本上是一个链表。因为一个 AVL 树是平衡的,所以我们有保证的复杂性 *O* (日志 *n* )。

W
wizardforcel 已提交
526
```cs
W
wizardforcel 已提交
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
    public bool Contains(T value)
    {
        return Find(value) != null;
    }

    /// <summary>
    /// Finds and returns the first node containing the specified value. If the value
    /// is not found, it returns null. It also returns the parent of the found node (or null)
    /// which is used in Remove.
    /// </summary>
    /// <param name="value">The value to search for.</param>
    /// <param name="parent">The parent of the found node (or null).</param>
    /// <returns>The found node (or null).</returns>
    private AVLTreeNode<T> Find(T value)
    {
        // Now, try to find data in the tree.
        AVLTreeNode<T> current = Head;

        // While we don't have a match...
        while (current != null)
        {
            int result = current.CompareTo(value);

            if (result > 0)
            {
                // If the value is less than current, go left.
                current = current.Left;
            }
            else if (result < 0)
            {
                // If the value is greater than current, go right.
                current = current.Right;
            }
            else
            {
                // We have a match!
                break;
            }
        }

        return current;
    }

```

### 移除

| 行为 | 如果树内存在指定的值,则该值被移除,并且该方法返回`true`。否则返回`false`。 |
| 表演 | *O* (日志 *n* ) |

`Add`一样,`Remove`方法的工作原理和传统的二叉查找树`Remove`算法一样;但是,它会添加在已删除节点的父节点上执行的平衡操作。

二叉查找树删除算法有些复杂,在 [*数据结构第 1 部分*](http://www.syncfusion.com/resources/techportal/ebooks/datastructurespart1) 中有详细描述。

W
wizardforcel 已提交
581
```cs
W
wizardforcel 已提交
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
    /// <summary>
    /// Removes the first occurrence of the specified value from the tree.
    /// </summary>
    /// <param name="value">The value to remove.</param>
    /// <returns>True if the value was removed; false otherwise.</returns>
    public bool Remove(T value)
    {
        AVLTreeNode<T> current;
        current = Find(value);

        if (current == null)
        {
            return false;
        }

        AVLTreeNode<T> treeToBalance = current.Parent;

        Count--;

        // Case 1: If current has no right child, then current's left replaces current.
        if (current.Right == null)
        {
            if (current.Parent == null)
            {
                Head = current.Left;
                if (Head != null)
                {
                    Head.Parent = null;
                }
            }
            else
            {
                int result = current.Parent.CompareTo(current.Value);
                if (result > 0)
                {
                    // If the parent value is greater than the current value,
                    // make the current left child a left child of parent.
                    current.Parent.Left = current.Left;
                }
                else if (result < 0)
                {
                    // If the parent value is less than the current value,
                    // make the current left child a right child of parent.
                    current.Parent.Right = current.Left;
                }
            }
        }
        // Case 2: If current's right child has no left child, then current's right child
        //         replaces current.
        else if (current.Right.Left == null)
        {
            current.Right.Left = current.Left;

            if (current.Parent == null)
            {
                Head = current.Right;
                if (Head != null)
                {
                    Head.Parent = null;
                }
            }
            else
            {
                int result = current.Parent.CompareTo(current.Value);
                if (result > 0)
                {
                    // If the parent value is greater than the current value,
                    // make the current right child a left child of parent.
                    current.Parent.Left = current.Right;
                }
                else if (result < 0)
                {
                    // If the parent value is less than the current value,
                    // make the current right child a right child of parent.
                    current.Parent.Right = current.Right;
                }
            }
        }
        // Case 3: If current's right child has a left child, replace current with current's
        //         right child's leftmost child.
        else
        {
            // Find the right's leftmost child.
            AVLTreeNode<T> leftmost = current.Right.Left;

            while (leftmost.Left != null)
            {
                leftmost = leftmost.Left;
            }

            // The parent's left subtree becomes the leftmost's right subtree.
            leftmost.Parent.Left = leftmost.Right;

            // Assign leftmost's left and right to current's left and right children.
            leftmost.Left = current.Left;

            leftmost.Right = current.Right;

            if (current.Parent == null)
            {
                Head = leftmost;
                if (Head != null)
                {
                    Head.Parent = null;
                }
            }
            else
            {
                int result = current.Parent.CompareTo(current.Value);
                if (result > 0)
                {
                    // If the parent value is greater than the current value,
                    // make leftmost the parent's left child.
                    current.Parent.Left = leftmost;
                }
                else if (result < 0)
                {
                    // If the parent value is less than the current value,
                    // make leftmost the parent's right child.
                    current.Parent.Right = leftmost;
                }
            }
        }

        if (treeToBalance != null)
        {
            treeToBalance.Balance();
        }
        else
        {
            if (Head != null)
            {
                Head.Balance();
            }
        }

        return true;
    }

```

### 获取分子

| 行为 | 返回一个枚举数,该枚举数执行 AVL 树的有序遍历。 |
| 表演 | *O* (1)返回枚举器。 *O* ( *n* )为调用者枚举每个节点。 |

W
wizardforcel 已提交
728
本系列的第一本书更详细地描述了 inorder(最小到最大值)遍历算法。下面的实现使用一个栈来演示在没有递归的情况下执行遍历。
W
wizardforcel 已提交
729

W
wizardforcel 已提交
730
```cs
W
wizardforcel 已提交
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
    /// <summary>
    /// Enumerates the values contained in the binary tree in inorder traversal order.
    /// </summary>
    /// <returns>The enumerator.</returns>
    public IEnumerator<T> InOrderTraversal()
    {
        // This is a non-recursive algorithm using a stack to demonstrate removing
        // recursion to make using the yield syntax easier.
        if (Head != null)
        {
            // Store the nodes we've skipped in this stack (avoids recursion).
            Stack<AVLTreeNode<T>> stack = new Stack<AVLTreeNode<T>>();

            AVLTreeNode<T> current = Head;

            // When removing recursion, we need to keep track of whether
            // we should be going to the left nodes or the right nodes next.
            bool goLeftNext = true;

            // Start by pushing Head onto the stack.
            stack.Push(current);

            while (stack.Count > 0)
            {
                // If we're heading left...
                if (goLeftNext)
                {
                    // Push everything but the leftmost node to the stack.
                    // We'll yield the leftmost after this block.
                    while (current.Left != null)
                    {
                        stack.Push(current);
                        current = current.Left;
                    }
                }

                // Inorder is left -> yield -> right
                yield return current.Value;

                // If we can go right, then do so.
                if (current.Right != null)
                {
                    current = current.Right;

                    // Once we've gone right once, we need to start
                    // going left again.
                    goLeftNext = true;
                }
                else
                {
                    // If we can't go right we need to pop off the parent node
                    // so we can process it and then go to its right node.
                    current = stack.Pop();
                    goLeftNext = false;
                }
            }
        }
    }

    /// <summary>
    /// Returns an enumerator that performs an inorder traversal of the binary tree.
    /// </summary>
    /// <returns>The inorder enumerator.</returns>
    public IEnumerator<T> GetEnumerator()
    {
        return InOrderTraversal();
    }

    /// <summary>
    /// Returns an enumerator that performs an inorder traversal of the binary tree.
    /// </summary>
    /// <returns>The inorder enumerator.</returns>
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

```

### 晴

| 行为 | 从树中移除所有节点。 |
| 表演 | *O* (1) |

W
wizardforcel 已提交
815
```cs
W
wizardforcel 已提交
816 817 818 819 820 821 822 823 824 825 826 827 828
    public void Clear()
    {
        Head = null;
        Count = 0;
    }

```

### 计数

| 行为 | 返回树中当前包含的节点数。 |
| 表演 | *O* (1) |

W
wizardforcel 已提交
829
```cs
W
wizardforcel 已提交
830 831 832 833 834 835 836
    public int Count
    {
        get;
        private set;
    }

```