C# 二进制搜索树IEnumerator.MoveNext()非递归顺序遍历实现。如何?

C# 二进制搜索树IEnumerator.MoveNext()非递归顺序遍历实现。如何?,c#,algorithm,binary-search-tree,ienumerable,enumerator,C#,Algorithm,Binary Search Tree,Ienumerable,Enumerator,在构建了由BSTNode节点组成的二叉搜索树之后,我正在尝试为它实现IEnumerable接口 这就是我如何构造bstnodeenumerator: 我们可以将算法转换为以下c代码: public节点下一步() { 而(_stack.Count>0 | | | u current!=null) { 如果(_current!=null) { _堆栈推送(_当前); _当前=_current.left; } 其他的 { _当前=_stack.Pop(); BSTNode结果=_当前; _当前=_cu

在构建了由
BSTNode
节点组成的二叉搜索树之后,我正在尝试为它实现IEnumerable接口

这就是我如何构造
bstnodeenumerator

我们可以将算法转换为以下c代码:

public节点下一步()
{
而(_stack.Count>0 | | | u current!=null)
{
如果(_current!=null)
{
_堆栈推送(_当前);
_当前=_current.left;
}
其他的
{
_当前=_stack.Pop();
BSTNode结果=_当前;
_当前=_current.Right;
}
}
返回结果;
}
但这不是必需的
boolmovenext()
实现,因为我必须返回bool。如果我确实将
\u current
设置为适当的节点,则为True;如果我在末尾,则为false


我应该如何实现公共bool MoveNext()?我无法理解的主要问题是,如果我想将
BSTNode Next()
转换为
bool MoveNext()
我必须
返回true
而不是简单地访问节点
BSTNode result=\u current并且仅在该设置之后
\u current=\u current.Right这显然是我做不到的。

老实说,对于这样一个非繁琐的枚举器,最好只使用.NET内置的工具。只需返回
IEnumerator
并使用
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; }
}
class节点:IEnumerable
其中TKey:i可比较
{
公共IEnumerator GetEnumerator()
{
var stack=新堆栈();
无功电流=此;
while(stack.Count>0 | | current!=null)
{
如果(当前!=null)
{
堆栈推送(当前);
电流=电流。左;
}
其他的
{
当前=stack.Pop();
产生回流电流;
电流=电流。右;
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
返回GetEnumerator();
}
公共节点左{get;set;}
公共节点权限{get;set;}
公钥{get;set;}
公共TValue值{get;set;}
}

如果您感到好奇,下面是编译器为它在幕后创建的IEnumerator类生成的代码

[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();
    }
  }
[编译生成]
私有密封类d__0:IEnumerator、IDisposable、IEnumerator
{
私立国际1____州;
专用节点2___当前;
公共BST节点4____;
专用堆栈5__1;
专用节点5_uu2;
BSTNode IEnumerator.Current
{
[调试隐藏]获取
{
返回此0.2__当前值;
}
}
对象IEnumerator.Current
{
[调试隐藏]获取
{
返回(对象)此0.2__当前值;
}
}
[调试程序隐藏]
公共数据0(内部1状态)
{
基本参数。\u002Ector();
此0.1__状态=参数0;
}
[调试程序隐藏]
void IDisposable.Dispose()无效
{
}
bool IEnumerator.MoveNext()
{
开关(此.1___状态)
{
案例0:
该状态为-1;
此0.5__1=新堆栈();
此0.5_uu2=(BSTNode)null;
转到标签_8;
案例1:
该状态为-1;
this.5_uuuu2=this.5_uuuu2.Right;
打破
违约:
返回false;
}
标签7:
标签8:

如果(this.5_uu1.Count调用方正在枚举上循环(可能在foreach循环中)。因此,您可以在每次希望返回结果时中止循环。出现了一个问题,因为
\u current=\u current.Right;
必须在确定结果后执行。因此,我将引入一个新变量
\u 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; }
}
private BSTNode\u结果;
bool IEnumerator.MoveNext()
{
而(_stack.Count>0 | | | u current!=null)
{
如果(_current!=null)
{
_堆栈推送(_当前);
_当前=_current.left;
}
其他的
{
_当前=_stack.Pop();
_结果=_电流;
_当前=_current.Right;
返回true;
}
}
返回false;
}
BSTNode IEnumerator.Current
{
获取{return\u result;}
}

请注意,枚举上的循环包括第一次调用
MoveNext()
并测试布尔结果。如果返回了
true
,则使用
Current
返回的值。

为什么不直接使用HashSet或Dictionary?您需要自己制作IEnumerator吗?为什么不让GetEnumerator函数直接返回您的实现,而是在
上执行
返回当前值;
BSTNode result=_current;
line。我将发布一个实现作为答案。@MatthewWhited-当然我可以简单地使用.Net中已经存在的东西,我只是在尝试学习。我现在也在与AVL二叉树和Skiplits作斗争,只是为了它。我确信这是可以理解的。我的意思是.Net不仅仅来自天空。真的吗d@ScottChamberlain注释。迭代器方法和
yield
语句是专门为非平凡的枚举数设计的。我知道,
yield-return
基本上保留了枚举的当前状态,使我的生活更轻松。@pijemcolu,如果你想了解更多的话。你还可以用
yield
做其他很酷的技巧,如果您的代码中有一个
try/finally
,如果调用方处理了
IEnumerator
finally
块中的代码将被执行,如果它在
try
块中等待
yield return
。啊,这就是执行的方法。我试图找出一条漫长的路,我没有想到addi
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; }
}
[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();
    }
  }
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; }
}