繁体   English   中英

没有父属性的Binary搜索树迭代器C#

[英]BInary Search Tree iterator c# without parent property

我最近开始用C#制作二进制搜索树以进行练习。 完成此任务后,我决定实现ICollection<T>以便我的树可以与foreach一起使用,并且通常可以用于我的练习。

我以这样的方式构造了类,即我有一个Node<T>类和一个BinarySearchTree<T>类,其中包含一个Node<T>一个Count整数和一个IsReadOnly布尔值。 这是我的Node类:

    internal class Node<T>: INode<T> where T: IComparable<T>
  {
    public Node<T> RightChildNode { get; set; }
    public Node<T> LeftChildNode { get; set; }
    public T Key { get; set; }

    //some methods go here
  }

这是我的BST课程:

    public class BinarySearchTree<T>: ICollection<T> where T: IComparable<T>
{
    internal Node<T> Root { get; set; }
    public int Count { get; private set; }
    public bool IsReadOnly => false;

    //some methods go here
}

现在,为了实现ICollection<T>我显然需要一个枚举器,该枚举器已经(部分)实现为:

    internal class BinarySearchTreeEnumerator<T> : IEnumerator<T> where T: IComparable<T>
{
    private BinarySearchTree<T> _parentTree;
    private BinarySearchTree<T> _currentTree;
    private Node<T> _currentNode => _currentTree.Root;
    private T _currentKey;

    public T Current => _currentNode.Key;

    /// <summary>
    /// generic constructor
    /// </summary>
    /// <param name="tree"></param>
    public BinarySearchTreeEnumerator(BinarySearchTree<T> tree)
    {
        this._parentTree = tree;
    }

    object IEnumerator.Current => Current;

    void IDisposable.Dispose(){}

    //pls
    public bool MoveNext()
    {
        if (_currentTree is null)
        {
            _currentTree = _parentTree;
        }
        var leftSubtree = this._currentTree.GetLeftSubtree();
        var rightSubtree = this._currentTree.GetRightSubtree();

        if (!(leftSubtree is null))
        {
            this._currentTree = leftSubtree;

        }
        else if (!(rightSubtree is null))
        {
            this._currentTree = rightSubtree;

        }
        else
        {

        }

    }

    public void Reset()
    {
        _currentTree = _parentTree;
    }
}

现在我的问题很明显是MoveNext()方法。 它现在不起作用,因为它所做的只是将它沿着树的最左侧路径拖到了该路径的末端,然后卡在该路径的末端。 我知道我可以通过将Parent属性添加到Node<T>类中来解决此问题,然后每当到达树中路径的末尾时,我都可以向上上一个Node并检查是否存在其他路径...但是这将意味着完全改变我原来的课程,而我宁愿不这样做。

这是不可避免的吗? 有什么方法可以解决此问题而无需以这种方式更改Node<T>类?

编辑:我做了一件事情,但不起作用:/

      public bool MoveNext()
    {
        if (_currentNode is null)
        {
            this._currentNode = _parentTree.Root;
            this._nodeStack.Push(_currentNode);
            return true;
        }
        var leftNode = this._currentNode.LeftChildNode;
        var rightNode = this._currentNode.RightChildNode;

        if (!(leftNode is null))
        {
            this._currentNode = leftNode;
            this._nodeStack.Push(_currentNode);
            return true;
        }
        else if (!(rightNode is null))
        {
            this._currentNode = rightNode;
            this._nodeStack.Push(_currentNode);
            return true;
        }
        else
        {
            //current node does not have children
            var parent = this._nodeStack.Pop();
            do
            {

                if (parent is null)
                {
                    return false;
                }

            } while (!(parent.RightChildNode is null));

            this._currentNode = parent.RightChildNode;
            this._nodeStack.Push(_currentNode);
            return true;
        }

    }

如果添加到枚举器

private List<Node<T>> _currentParentNodes = new List<Node<T>>();

并像堆栈一样使用它,每次下降到某个级别时,将当前节点推送到currentParentNodes ,每次上升时,都从currentParentNodes弹出父节点,那么所有问题都将消失:-)

使用递归来实现这一点可能会更容易。 例如:

递归版本(仅适用于平衡树)

public IEnumerator<T> GetEnumerator()
{
    return enumerate(Root).GetEnumerator();
}

IEnumerable<T> enumerate(Node<T> root)
{
    if (root == null)
        yield break;

    yield return root.Key;

    foreach (var value in enumerate(root.LeftChildNode))
        yield return value;

    foreach (var value in enumerate(root.RightChildNode))
        yield return value;
}

这些是BinarySearchTree<T>成员。

给定以上实现,然后执行以下代码:

BinarySearchTree<double> tree = new BinarySearchTree<double>();

tree.Root = new Node<double> {Key = 1.1};

tree.Root.LeftChildNode  = new Node<double> {Key = 2.1};
tree.Root.RightChildNode = new Node<double> {Key = 2.2};

tree.Root.LeftChildNode.LeftChildNode   = new Node<double> { Key = 3.1 };
tree.Root.LeftChildNode.RightChildNode  = new Node<double> { Key = 3.2 };
tree.Root.RightChildNode.LeftChildNode  = new Node<double> { Key = 3.3 };
tree.Root.RightChildNode.RightChildNode = new Node<double> { Key = 3.4 };

foreach (var value in tree)
{
    Console.WriteLine(value);
}

产生以下输出:

1.1
2.1
3.1
3.2
2.2
3.3
3.4

警告:对于32位进程,堆栈空间限制为1MB,对于64位进程,堆栈空间限制为4MB,因此,如果树退化(严重失衡),则使用递归可能会耗尽堆栈空间。

非递归版本

您可以相当简单地实现非递归版本,如下所示:

IEnumerable<T> enumerate(Node<T> root)
{
    var stack = new Stack<Node<T>>();
    stack.Push(root);

    while (stack.Count > 0)
    {
        var node = stack.Pop();

        if (node == null)
            continue;

        yield return node.Key;
        stack.Push(node.RightChildNode);
        stack.Push(node.LeftChildNode);
    }
}

这将以与递归版本相同的顺序返回元素。

由于即使对于退化的树,此版本也不会耗尽堆栈空间,因此它优于递归版本。

您是否需要深度优先搜索(DFS)方法? 它具有递归性质,很难保存为状态(它使用调用堆栈)。

也许考虑采用广度优先搜索(BFS)的方法,这种方法是迭代的。 您只需要一个currentNode和一个Queue。

规则是

current = Queue.poll();
For child in current
     Queue.offer(child);

在初始化时,你会做

Queue.offer(rootNode);

非常抱歉,我的移动用户格式和语法不佳。 安德烈斯

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM