C# 中的这个树实现有什么问题?
What's wrong with this tree implementation in C#?
我目前正在尝试在 C# 中实现一个非常简单的 tree/node class,其中节点有一个 object 作为数据,它们可以有零到多个 children.我目前有两个问题:
- 出于某种原因,打印出 object 最终打印出 TYPE
object 而不是每个节点的 toString()
根。
- 我似乎无法正确打印出树的多个分支,
找不到问题,是否是我打印的问题
方法或我将 children 添加到节点的方式。
我的节点class在下面。
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);
}
}
}
}
为了测试,我正在创建一个树,其根有三个 children,这三个 children 中的每一个 children 然后还有另外三个 children,如下所示。
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# 中进行递归,所以我可能缺少一些简单的东西。如果是这种情况,我宁愿让问题得到解释,而不是在代码中给我解决方案。谢谢。
快速修复:
public void Add(params Node[] objects)
{
foreach (Node obj in objects)
{
children.AddLast(obj);
}
}
如果您的 Add
方法应该添加子节点,那么首先您应该为 objects
参数使用相应的类型。其次,您应该删除对 Node
的额外对象转换,因为您已经传递了 Node
类型参数。
问题出在您的 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);
除了现有答案之外,不要使用 +=
:
连接字符串
s += data;
使用
s = String.Concat(s, data.ToString());
相反。
此外 data
真的需要是对象类型吗?在不了解您的整个系统的情况下,这只是一个猜测,但是拥有一个通用的 Node
class 是可行的,例如:
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");
public Node(object data)
{
this.data = data;
children = new LinkedList<Node>();
}
此处您正在将此节点的 data
初始化为类型 Node
的对象,当您执行类似 Add(new Node("11"))
的操作时。本质上,您构建的节点现在包含另一个节点作为数据,而不是您最初预期的“11”。
不要将 object
用于任何事情,没有理由将其作为 C# 学习的一部分,它只会咬你一口,正如你在这里发现的那样。对类型使用泛型或标记联合以获得可以包含不同类型数据的节点。
学习泛型然后重新审视你的树实现,这是我的建议。
这不是您问题的直接答案,只是关于如何构建您的 class.
的更多建议
试试这个:
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<T>(this Node<T> tree, Node<T> 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<T>(this Node<T> tree)
和 void Print<T>(this Node<T> 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<T>
对象可用的所有常规方法都适用于 Node<T>
。
我目前正在尝试在 C# 中实现一个非常简单的 tree/node class,其中节点有一个 object 作为数据,它们可以有零到多个 children.我目前有两个问题:
- 出于某种原因,打印出 object 最终打印出 TYPE object 而不是每个节点的 toString() 根。
- 我似乎无法正确打印出树的多个分支, 找不到问题,是否是我打印的问题 方法或我将 children 添加到节点的方式。
我的节点class在下面。
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);
}
}
}
}
为了测试,我正在创建一个树,其根有三个 children,这三个 children 中的每一个 children 然后还有另外三个 children,如下所示。
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# 中进行递归,所以我可能缺少一些简单的东西。如果是这种情况,我宁愿让问题得到解释,而不是在代码中给我解决方案。谢谢。
快速修复:
public void Add(params Node[] objects)
{
foreach (Node obj in objects)
{
children.AddLast(obj);
}
}
如果您的 Add
方法应该添加子节点,那么首先您应该为 objects
参数使用相应的类型。其次,您应该删除对 Node
的额外对象转换,因为您已经传递了 Node
类型参数。
问题出在您的 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);
除了现有答案之外,不要使用 +=
:
s += data;
使用
s = String.Concat(s, data.ToString());
相反。
此外 data
真的需要是对象类型吗?在不了解您的整个系统的情况下,这只是一个猜测,但是拥有一个通用的 Node
class 是可行的,例如:
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");
public Node(object data)
{
this.data = data;
children = new LinkedList<Node>();
}
此处您正在将此节点的 data
初始化为类型 Node
的对象,当您执行类似 Add(new Node("11"))
的操作时。本质上,您构建的节点现在包含另一个节点作为数据,而不是您最初预期的“11”。
不要将 object
用于任何事情,没有理由将其作为 C# 学习的一部分,它只会咬你一口,正如你在这里发现的那样。对类型使用泛型或标记联合以获得可以包含不同类型数据的节点。
学习泛型然后重新审视你的树实现,这是我的建议。
这不是您问题的直接答案,只是关于如何构建您的 class.
的更多建议试试这个:
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<T>(this Node<T> tree, Node<T> 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<T>(this Node<T> tree)
和 void Print<T>(this Node<T> 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<T>
对象可用的所有常规方法都适用于 Node<T>
。