二叉搜索树IEnumerator.MoveNext()非递归中序遍历实现。如何?

Binary search tree IEnumerator.MoveNext() non recursive in order traversal implementation. How to?

在构建了由 BSTNode<Tkey,TValue> 个节点组成的二叉搜索树 BST<Tkey,TValue> 之后,我正在尝试为其实现 IEnumerable 接口。

这就是我构建 BSTNodeEnumrator<Tkey,TValue>:

的方式
public class BSTNodeEnumerator<TKey, TValue> : IEnumerator<BSTNode<TKey, TValue>> where TKey : IComparable<TKey>
{
    private Stack<BSTNode<TKey, TValue>> _stack;

    public BSTNodeEnumerator(BSTNode<TKey, TValue> root)
    {
        _stack = new Stack<BSTNode<TKey, TValue>>();
        _current = null;
        _root = root;
    }

    // ... rest of the implementation
}

我传入root节点,_current是枚举的结果。我也在尝试为此使用堆栈,因为我不像在 AVL BST 中那样跟踪父节点。

现在我希望枚举器以非递归的方式按顺序+遍历树。由于 bst 的属性,这也应该导致排序的枚举,这很好,因为这正是我想要实现的。

伪代码顺序遍历的非递归算法wikipedia article

    iterativeInorder(node)
  s ← empty stack
  while (not s.isEmpty() or node ≠ null)
    if (node ≠ null)
      s.push(node)
      node ← node.left
    else
      node ← s.pop()
      visit(node)
      node ← node.right

我们可以将算法转换成这个c#代码:

public BSTNode<Tkey,TValue> Next() 
{
   while (_stack.Count > 0 || _current != null) 
    {
         if (_current != null)
         {
          _stack.Push(_current);
          _current = _current.left;
         }
         else
         {
          _current = _stack.Pop();
          BSTNode<Tkey,TValue> result = _current;
          _current = _current.Right;
         }
    }
    return result;
}

但这不是必需的 bool MoveNext() 实现,因为我必须 return 一个布尔值。如果我确实将 _current 设置为适当的节点,则为 True;如果我在末尾,则为 false。

我应该如何实施 public bool MoveNext()?我无法解决的主要问题是,如果我想将 BSTNode<Tkey,TValue> Next() 转换为 bool MoveNext(),我必须 return true 而不是简单地访问节点 BSTNode<Tkey,TValue> result = _current; 和只有在那组 _current = _current.Right; 之后,我显然做不到。

老实说,对于像这样的复杂枚举器,最好只使用 .NET 中内置的工具。它可以自动将您写入的代码转换为枚举器,只需返回 IEnumerator<BSTNode<Tkey,TValue>> 并使用 yield return 关键字进行非常小的调整。

class BSTNode<TKey, TValue> : IEnumerable<BSTNode<TKey, TValue>>
     where TKey : IComparable<TKey>
{
    public IEnumerator<BSTNode<TKey, TValue>> GetEnumerator()
    {
        var stack = new Stack<BSTNode<TKey, TValue>>();
        var current = this;
        while (stack.Count > 0 || current != null)
        {
            if (current != null)
            {
                stack.Push(current);
                current = current.Left;
            }
            else
            {
                current = stack.Pop();
                yield return current;
                current = current.Right;
            }
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public BSTNode<TKey, TValue> Left { get; set; }

    public BSTNode<TKey, TValue> Right { get; set; }

    public TKey Key { get; set; }

    public TValue Value { get; set; }
}

如果您好奇,这里是编译器为 IEnumerator class 在幕后制作的代码生成的代码

[CompilerGenerated]
  private sealed class <GetEnumerator>d__0 : IEnumerator<BSTNode<TKey, TValue>>, IDisposable, IEnumerator
  {
    private int <>1__state;
    private BSTNode<TKey, TValue> <>2__current;
    public BSTNode<TKey, TValue> <>4__this;
    private Stack<BSTNode<TKey, TValue>> <stack>5__1;
    private BSTNode<TKey, TValue> <current>5__2;

    BSTNode<TKey, TValue> IEnumerator<BSTNode<TKey, TValue>>.Current
    {
      [DebuggerHidden] get
      {
        return this.<>2__current;
      }
    }

    object IEnumerator.Current
    {
      [DebuggerHidden] get
      {
        return (object) this.<>2__current;
      }
    }

    [DebuggerHidden]
    public <GetEnumerator>d__0(int <>1__state)
    {
      base.\u002Ector();
      this.<>1__state = param0;
    }

    [DebuggerHidden]
    void IDisposable.Dispose()
    {
    }

    bool IEnumerator.MoveNext()
    {
      switch (this.<>1__state)
      {
        case 0:
          this.<>1__state = -1;
          this.<stack>5__1 = new Stack<BSTNode<TKey, TValue>>();
          this.<current>5__2 = (BSTNode<TKey, TValue>) null;
          goto label_8;
        case 1:
          this.<>1__state = -1;
          this.<current>5__2 = this.<current>5__2.Right;
          break;
        default:
          return false;
      }
label_7:
label_8:
      if (this.<stack>5__1.Count <= 0 && this.<current>5__2 == null)
        return false;
      if (this.<current>5__2 != null)
      {
        this.<stack>5__1.Push(this.<current>5__2);
        this.<current>5__2 = this.<current>5__2.Left;
        goto label_7;
      }
      else
      {
        this.<current>5__2 = this.<stack>5__1.Pop();
        this.<>2__current = this.<current>5__2;
        this.<>1__state = 1;
        return true;
      }
    }

    [DebuggerHidden]
    void IEnumerator.Reset()
    {
      throw new NotSupportedException();
    }
  }

调用方正在遍历枚举(可能在 foreach 循环中)。因此,您可以在每次想要 return 结果时中止循环。问题来了,因为_current = _current.Right;必须在结果确定后才执行。因此我引入了一个新变量 _result.

private BSTNode<TKey, TValue> _result;

bool IEnumerator.MoveNext()
{
    while (_stack.Count > 0 || _current != null)
    {
        if (_current != null)
        {
            _stack.Push(_current);
            _current = _current.left;
        }
        else
        {
            _current = _stack.Pop();
            _result = _current;
            _current = _current.Right;
            return true;
        }
    }
    return false;
}

BSTNode<TKey, TValue> IEnumerator<BSTNode<TKey, TValue>>.Current
{
    get { return _result; }
}

请注意,遍历枚举包括首先调用 MoveNext() 和测试布尔结果。如果 true 被 return 编辑,则使用 Current 编辑的值 return。