【问题标题】:What's wrong with this tree implementation in C#?C# 中的这个树实现有什么问题?
【发布时间】:2016-01-30 13:54:56
【问题描述】:

我目前正在尝试在 C# 中实现一个非常简单的树/节点类,其中节点有一个对象作为数据,并且它们可以有零到多个子节点。我目前有两个问题:

  1. 出于某种原因,打印出对象最终会打印 TYPE 的对象而不是它的 toString() 每个节点不是 根。
  2. 我似乎无法正确打印出我的树的多个分支, 并且找不到问题,是否是我的打印问题 方法或我将子节点添加到节点的方式。

我的节点类在下面。

namespace Tree
{
    class Node
    {
        public object data;
        private LinkedList<Node> children;

        public Node(object data)
        {
            this.data = data;
            children = new LinkedList<Node>();
        }

        public void Add(params object[] objects)
        {
            foreach (object obj in objects)
            {
                children.AddLast(new Node(obj));
            }
        }

        public int Count()
        {
            int count = 1;

            foreach (Node n in children)
            {
                count += n.Count();
            }

            return count;
        }

        public void Print(int depth)
        {
            string s = new string('\t',depth);
            s += data;
            Console.WriteLine(s);
            depth++;

            foreach (Node n in children)
            {
                n.Print(depth);
            }
        }
    }
}

为了测试,我正在创建一棵树,它的根有三个孩子,然后这三个孩子中的每一个都有另外三个孩子,如下所示。

Node core = new Node("root");

Node o1 = new Node("1");
Node o2 = new Node("2");
Node o3 = new Node("3");

o1.Add(new Node("11"), new Node("12"), new Node("13"));
o2.Add(new Node("21"), new Node("22"), new Node("23"));
o3.Add(new Node("31"), new Node("32"), new Node("33"));
core.Add(o1, o2, o3);

Console.WriteLine(core.Count());
core.Print(0);

预期的输出当然是:

13
root
 1
  11
  12
  13
 2
  21
  22
  23
 3
  31
  32
  33

不幸的是,我得到了:

4
root
    Tree.Node
    Tree.Node
    Tree.Node

这是我第一次在 C# 中进行递归,所以也许我缺少一些简单的东西。如果是这种情况,我宁愿解释问题,也不愿在代码中给出解决方案。谢谢。

【问题讨论】:

  • 您还需要在Count() 上进行递归
  • Count() 已经有递归,因为它调用了子节点的 Count()
  • 您应该考虑将树类简化为:public class Node&lt;T&gt; : List&lt;Node&lt;T&gt;&gt; { public T Data { get; set; } }
  • public class Node&lt;T&gt; : LinkedList&lt;Node&lt;T&gt;&gt; { public T Data { get; set; } }
  • public class Node&lt;T&gt; : LinkedList&lt;Node&lt;T&gt;&gt; 我想我以前没有遇到过这个符号,我的意思是逗号。

标签: c# tree nodes


【解决方案1】:

快速修复:

public void Add(params Node[] objects)
{
    foreach (Node obj in objects)
    {
        children.AddLast(obj);
    }
}

如果您的 Add 方法应该添加子节点,那么首先您应该为 objects 参数使用相应的类型。其次,您应该删除对Node 的额外对象转换,因为您已经传递了Node 类型参数。

【讨论】:

  • 感觉很傻,但至少我明白我做错了什么,谢谢。
【解决方案2】:

问题在于您的 Add() 方法。目前它被实现为接收对象并使用这些对象添加节点。但是您正在使用它来添加子节点。您需要两种不同的方法:

public void AddObjects(params object[] objects)
{
    foreach (object obj in objects)
    {
        children.AddLast(new Node(obj));
    }
}

public void AddChildNodes(params Node[] nodes)
{
    foreach (Node node in nodes)
    {
        children.AddLast(node);
    }
}

然后在设置树结构的地方,使用 AddChildNodes() 而不是 Add()

这就是设置代码的外观:

Node core = new Node("root");

Node o1 = new Node("1");
Node o2 = new Node("2");
Node o3 = new Node("3");

o1.AddObjects("11", "12", "13");
o2.AddObjects("21", "22", "23");
o3.AddObjects("31", "32", "33");
core.AddChildNodes(o1, o2, o3);

Console.WriteLine(core.Count());
core.Print(0);

【讨论】:

    【解决方案3】:
    public Node(object data)
    {
        this.data = data;
        children = new LinkedList<Node>();
    }
    

    在这里,当您执行Add(new Node("11")) 之类的操作时,您正在将此节点的data 初始化为Node 类型的对象。本质上,您构建的节点现在包含另一个节点作为数据,而不是您最初想要的“11”。

    不要将object 用于任何事情,没有理由将其作为您学习 C# 的一部分,而且它只会像您在此处发现的那样对您不利。对类型使用泛型或标记联合来获取可以包含不同类型数据的节点。

    了解泛型,然后重新审视你的树实现,这是我的建议。

    【讨论】:

      【解决方案4】:

      除了现有答案之外,不要将字符串与+= 连接:

      s += data;
      

      使用

      s = String.Concat(s, data.ToString()); 
      

      相反。

      data 是否真的需要是 object 类型的?在不了解您的整个系统的情况下,这只是一个猜测,但拥有一个通用的 Nodeclass 可能是可行的,例如:

      class Node<T> 
      {
        public T data;
        ...
        public void AddChild(Node<T> childNode) ...
        public void AddChilds(IEnumerable<Node<T>> childNode) ...
      }
      
      
      Node<String> root = new Node<String>("root");
      root.AddChild(new Node<String>("FirstBelowRoot");
      

      【讨论】:

      • 感谢您的建议。我希望它能够接受给它的任何对象,但我以前没有使用过泛型类,我相信现在我知道它们会非常有用。
      • 这绝对值得,因为它提供了严格的类型检查。我刚刚和你有同样的要求,我使用了一个很好的通用树类。
      【解决方案5】:

      这不是对您问题的直接回答,只是对如何构建您的课程的更多建议。

      试试这个:

      public class Node<T> : LinkedList<Node<T>>
      {
          public T Data { get; set; }
      
          public Node(T data)
          {
              this.Data = data;
          }
      }
      

      就是这样。好吧,至少你的核心代码就是这样。您需要这组扩展方法来使用它:

      public static class NodeEx
      {
          public static void Add<T>(this Node<T> tree, Node<T> child)
          {
              tree.AddLast(child);
          }
      
          public static int Count<T>(this Node<T> tree)
          {
              int count = 1;
              foreach (Node<T> n in tree)
              {
                  count += n.Count();
              }
              return count;
          }
      
          public static void Print<T>(this Node<T> tree, int depth)
          {
              Console.WriteLine(new string('\t', depth) + tree.Data);
              foreach (Node<T> n in tree)
              {
                  n.Print(depth + 1);
              }
          }
      }
      

      现在使用void Add&lt;T&gt;(this Node&lt;T&gt; tree, Node&lt;T&gt; child) 扩展方法,您可以编写以下代码:

      Node<string> core = new Node<string>("root")
      {
          new Node<string>("1")
          {
              new Node<string>("11"),
              new Node<string>("12"),
              new Node<string>("13")
          },
          new Node<string>("2")
          {
              new Node<string>("21"),
              new Node<string>("22"),
              new Node<string>("23")
          },
          new Node<string>("3")
          {
              new Node<string>("31"),
              new Node<string>("32"),
              new Node<string>("33")
          },
      };
      

      int Count&lt;T&gt;(this Node&lt;T&gt; tree)void Print&lt;T&gt;(this Node&lt;T&gt; tree, int depth) 按预期工作。这段代码:

      Console.WriteLine(core.Count());
      core.Print(0);
      

      ...产生:

      13
      root
        1
          11
          12
          13
        2
          21
          22
          23
        3
          31
          32
      33
      

      现在,最大的优势是所有可用于LinkedList&lt;T&gt; 对象的常规方法都适用于Node&lt;T&gt;

      【讨论】:

        猜你喜欢
        • 2021-10-17
        • 2011-09-28
        • 2011-12-14
        • 2017-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多