【问题标题】:Traversing a Tree Structure遍历树结构
【发布时间】:2014-11-20 22:58:41
【问题描述】:

我正在尝试了解如何遍历树数据结构,但在执行此操作时遇到了问题,尤其是在我尝试使用 IEnumerable 时。我希望树作为字典,这样我就可以通过它们的字符串名称来引用节点。树将包含不同的类对象,但由于这些对象都实现了接口IMyInterface,因此树的类型将是IMyInterface 接口。

我有树:

internal class Tree<T>
{
    private TreeNode<T> root;

    internal Tree(T value)
    {
        if (value == null)
        {
            throw new ArgumentNullException(
                "Cannot use a null value to construct a tree.");
        }

        this.root = new TreeNode<T>(value);
    }

    internal Tree(T value, params Tree<T>[] children) : this(value)
    {
        foreach (Tree<T> child in children)
        {
            this.root.AddChild(child.root);
        }
    }

    internal TreeNode<T> Root
    {
        get { return this.root; }
    }

    private void PrintDFS(TreeNode<T> root, int spaces)
    {
        if (spaces < 0)
        {
            throw new ArgumentOutOfRangeException(
                "The number of spaces used to represent the parent-child relation in a tree must be greater than or equal to zero.");
        }

        if (this.root == null)
        {
            return;
        }

        StringBuilder sb = new StringBuilder();
        sb.Append(' ', spaces);
        sb.Append(root.Value);

        TreeNode<T> child = null;

        foreach (Tree<T> child in this.root)     // <--- this generates an error
        {
            PrintDFS(child, spaces);
        }
    }

    // Traverses and prints the tree in
    // Depth-First Search (DFS) manner
    internal void TraverseDFS()
    {
        this.PrintDFS(this.root, 0);
    }
}

我的节点类是:

internal class TreeNode<T> : IEnumerable<TreeNode<T>>
{
    private T value;
    private bool hasParent;

    private readonly Dictionary<string, TreeNode<T>> children = new Dictionary<string, TreeNode<T>>();

    internal TreeNode(T value)
    {
        if (value == null)
        {
            throw new ArgumentNullException(
                "Cannot insert null values for a tree node!");
        }

        this.value = value;
        this.children = new Dictionary<string, TreeNode<T>>();      // dictionary that holds the children of each node
    }

    internal T Value
    {
        get { return this.value; }
        set { this.value = value; }
    }

    internal int ChildrenCount
    {
        get
        {
            return this.children.Count;
        }
    }

    internal void AddChild(TreeNode<T> child)
    {
        if (child == null)
        {
            throw new ArgumentNullException(
                "Cannot insert null value as child node.");
        }

        if (child.hasParent)
        {
            throw new ArgumentException(
                "The child node already has a parent.");
        }

        child.hasParent = true;
        this.children.Add(child.ToString(), child);
    }

    internal TreeNode<T> GetChild(string nodeName)
    {
        return this.children[nodeName];
    }

    internal IEnumerator<TreeNode<T>> GetEnumerator()
    {
        return this.children.Values.GetEnumerator();
    }

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

    IEnumerator<TreeNode<T>> IEnumerable<TreeNode<T>>.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

问题似乎是代码:

foreach (Tree<T> child in this.root)     // <--- this generates an error
{
    PrintDFS(child, spaces);
}

(来自 Tree 类的代码 sn-p)任何建议将不胜感激。

编辑

我收到错误消息:

错误 669 无法在此范围内声明名为“child”的局部变量,因为它会给“child”赋予不同的含义,而“child”已在“父或当前”范围中用于表示其他内容。

错误 672 无法将类型 TreeNode&lt;T&gt; 转换为 Tree&lt;T&gt;

还有警告信息:

警告 668 TreeNode&lt;T&gt; 未实现“收集”模式。 TreeNode&lt;T&gt;.GetEnumerator() 要么是静态的,要么是不公开的。

【问题讨论】:

  • 嗯嗯。什么不起作用?你有例外吗?
  • @Keith Payne:我收到一个错误,即无法在此范围内声明子变量,因为它会将含义更改为已声明的子变量。我收到一个警告,说 TreeNode 类没有实现“集合”模式。 GetEnnumerator 是静态的或不公开的。
  • 只需在你的 foreach() 中使用不同的变量名 - foreach (var node in this.root) { PrintDFS(node, spaces); }。这将修复您的编译器错误。此外,将 GetEnumerator() 方法声明为 public - public IEnumerator IEnumerable.GetEnumerator()...
  • @Keith Payne:如您所见,我试图将所有内容都保留在内部。真的有必要将 GetEnumerator() 声明为 public 吗?

标签: c# tree


【解决方案1】:

问题 #1

错误 669 不能在此声明名为 'child' 的局部变量 范围,因为它会给“孩子”赋予不同的含义,即 已在“父级或当前”范围内用于表示其他内容。

TreeNode<T> child = null;

foreach (Tree<T> child in this.root)     // <--- this generates an error
{
     PrintDFS(child, spaces);
}

您已经有一个child 变量。您需要以不同的方式命名它们。该错误是不言自明的。

对于这种情况,只需从foreach 上方删除child,因为它在那里没用。

foreach (var child in this.root) 
{
     PrintDFS(child, spaces);
}

我认为您想要 TreeNode&lt;T&gt;,但不确定应该从 root 实际返回什么。

问题 #2

错误 672 无法将 TreeNode 类型转换为 Tree

如果您应该循环遍历TreeNode&lt;T&gt;,而不是错误状态的Tree&lt;T&gt;。只需使用var,除非您实际上是在尝试迭代树而不是节点。

问题 #3

警告 668 TreeNode 未实现“集合”模式。 TreeNode.GetEnumerator() 要么是静态的,要么不是公共的。

它需要公开。 Internal 不削减它,因为它需要遵守 IEnumerable 合同。看起来你已经通过显式实现解决了这个问题。

【讨论】:

  • 我正在尝试打印给定树的所有子节点。你是对的,它应该是 TreeNode.
  • 我需要做出改变:public IEnumerator> GetEnumerator() 让它工作。
【解决方案2】:

首先,将您的 3 个 GetEnumertor 函数替换为以下 2 个函数:

public IEnumerator<TreeNode<T>> GetEnumerator()
{
  return this.children.Values.GetEnumerator();
}

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

然后将 PrintDFS 循环的最后一部分更改如下:

//TreeNode<T> child = null;

foreach (var child in this.root)
{
  PrintDFS(child, spaces);
}

编译。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多