注意:这是我只需要指导的家庭作业。我当时没有要求任何其他帮助。
我已经查找了如何在C#中添加二进制搜索树中的节点,但是我得到了非常不同类型的答案。所以,假设我有节点类:
class Node
{
public int value;
public Node left;
public Node right;
public Node(int v)
{
value = v;
left = right = null;
}
}
我的添加功能所在的类:
class BST
{
public void addNode(int x, Node root)
{
Node newNode = new Node(x);
if (root == null)
{
root = newNode;
}
if (root.value == newNode.value)
{
return;
}
else if (newNode.value < root.value)
{
if (root.left == null)
{
root.left = newNode;
}
else
{
addNode(x, root.left);
}
}
else if (newNode.value > root.value)
{
if (root.right == null)
{
root.right = newNode;
}
else
{
addNode(x, root.right);
}
}
}
}
所以现在我的问题是:我很确定我已经掌握了大部分逻辑,但是我怀疑的是,当我递归调用函数时,我不确定树是否实际上转到了左子树递归调用。或者我做得对吗?
答案 0 :(得分:3)
如果您认为自己做得对,那么您应该对其进行测试。首先,您应该增强类,以便在调试器中更容易可视化。显而易见的事情是向Node添加“ToString()”方法:
public class Node
{
public int value;
public Node left;
public Node right;
public Node(int v)
{
value = v;
left = right = null;
}
public override string ToString()
{
return value.ToString();
}
}
现在,只需将鼠标悬停在类实例上,即可在Visual Studio中检查树的值。根据Sayse的建议,在开始构建树之后在代码中设置断点:
将鼠标悬停在根节点上。一个面板将出现“ToString()”值 - 节点值,实际上,由于你的“ToString()”覆盖。您还会看到一个“+”按钮。如果单击该按钮,Visual Studio将显示类实例的字段和属性,您可以递归地展开它们:
检查了调试器中的值后,您现在可能需要更复杂的方法从类中提取数据,例如返回列表中Node
内或下的所有值:
public class Node
{
public IList<int> ToList()
{
var list = new List<int>();
AddToList(list);
return list;
}
public void AddToList(List<int> list)
{
if (left != null)
left.AddToList(list);
list.Add(value);
if (right != null)
right.AddToList(list);
}
}
这应该可以让你看到你拥有的东西,并验证它是你所期望的。您可以使用Enumerable.SequentialEqual
在代码中添加asserts,以确保返回的列表正确无误,如下所示:
private static void TestBST()
{
var bst = new BST();
Node root = new Node(23);
bst.addNode(13, root);
bst.addNode(-12, root);
bst.addNode(1, root);
Debug.Assert(Enumerable.SequenceEqual(root.ToList(), new int[] { -12, 1, 13, 23 }));
}
您还可以从Immediate Window调用“ToList()”方法,这样您可以在程序在断点处停止时键入c#表达式并对其进行解释并以交互方式显示结果:
因此,总而言之,无论何时设计新类,总是要添加逻辑,以便于可视化,调试和断言代码的正确性。 TobiMcNamobi提到的单元测试方法是正式的方法,但您可以在家庭作业代码中非正式地进行。
(顺便说一下,你注意到我在上面明确地分配了一个根节点,而不是使用BST类实例来创建根节点?你剩下的作业就是找出原因。)
答案 1 :(得分:0)
你的左/右逻辑在大多数情况下是可以的,但开始是令人困惑的:
if (root == null)
{
root = newNode;
}
if (root.Value == newNode.Value)
{
return;
}
实际上很多。如果root
为null
,则表示出现问题。
你可以忽略这一点:
if (root == null)
return;
if (newNode.Value < root.Value)
或者让用户现在,那是错误的
if (root == null)
throw new ArgumentNullException("root");
但是,如果您希望在root
来电之外更改AddNode
,则必须将root
作为reference传递:
public void AddNode(int x, ref Node root)
{
Node newNode = new Node(x);
this->AddNode(newNode, ref root);
}
private void AddNode(Node newNode, ref Node root)
{
if (root == null)
{
root = newNode;
return;
}
if (root.Value == newNode.Value)
{
return;
}
要改进的一些事项:
最好在UpperCamelCase中用C#编写方法名称,这样就可以调用方法AddNode
。这不会改变逻辑,但每种编程语言都有自己的风格指南,应该尝试遵循。
对于公共属性/字段也是如此,因此请将其称为Left
,Right
和Value
。
每次拨打AddNode
时,您都会创建一个新的Node对象来保存x
。
这是可以避免的开销。只需创建另一个funcktion AddNode
,它接收一个Node作为第一个参数,并从你的函数中调用该函数:
public void AddNode(int x, Node root)
{
Node newNode = new Node(x);
this->AddNode(newNode, root);
}
private void AddNode(Node newNode, Node root)
{
if (root == null)
{
root = newNode;
}
if (root.Value == newNode.Value)
{
return;
}
else if (newNode.Value < root.Value)
{
if (root.Left == null)
{
root.Left = newNode;
}
else
{
AddNode(newNode, root.Left);
}
}
else if (newNode.Value > root.Value)
{
if (root.Right == null)
{
root.Right = newNode;
}
else
{
AddNode(newNode, root.Right);
}
}
}