遍历树结构

时间:2014-11-20 22:58:42

标签: c# tree

我试图了解如何遍历树数据结构,我遇到了问题,特别是当我尝试使用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类的代码片段)非常感谢任何建议。

修改

我收到错误消息:

错误669名为'child'的局部变量不能在此范围内声明,因为它会给'child'赋予不同的含义,'child'已在'parent或current'范围内用于表示其他内容。

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

警告信息:

警告668 TreeNode<T>未实施“收集”模式。 TreeNode<T>.GetEnumerator()要么是静态的,要么不公开。

2 个答案:

答案 0 :(得分:2)

问题#1

  

错误669名为&#39; child&#39;的局部变量不能在此声明   范围,因为它会给孩子带来不同的意义,即   已经在父母或当前使用过的&#39;范围表示别的东西。

TreeNode<T> child = null;

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

您已有child个变量。您需要以不同的方式命名它们。这个错误非常自我解释。

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

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

我认为你想要TreeNode<T>,但不确定实际上应该从root返回什么。

问题#2

  

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

如果您应该循环TreeNode<T>,而不是Tree<T>作为错误状态。只需使用var,除非您实际尝试迭代树而不是节点。

问题#3

  

警告668 TreeNode未实现&#39;集合&#39;图案。 TreeNode.GetEnumerator()是静态的,也可以不是公共的。

它需要公开。内部不会削减它,因为它需要遵守IEnumerable合同。看起来你已经通过明确的实现解决了这个问题。

答案 1 :(得分:1)

首先,用这两个代替你的3个GetEnumertor函数:

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);
}

编译。