[英]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.