递归方法:单个元素参数还是元素集合?

时间:2010-02-13 22:31:53

标签: recursion

我对实现递归方法的两种不同方法的优点有疑问。我一直遵循版本1的方法,即接受单个Node参数,但我最近遇到了版本2中使用的样式,它接受一组节点。

考虑以下Node类以及Visit方法的2个版本:

class Node
{
    public List<Node> children = new List<Node>();
    // other data members 
}

版本1接受单个Node参数:

Visit(Node n)
{
  DoSomethingUsefulWith(n);

  foreach (Node child in n.children)
    Visit(child);
}

版本2接受节点集合:

Visit(List<Node> nodes)
{
  foreach (Node n in nodes)
  {
    DoSomethingUsefulWith(n);
    Visit(n.children);
  }
}

使用一种形式而不是另一种形式,是否有任何好处,甚至是风格上的?选择是否应该完全取决于您是从单个节点开始还是从节点集合开始,即使在任何一种情况下都使用任一方法版本都是微不足道的?

7 个答案:

答案 0 :(得分:2)

我不会实现版本2,始终版本为1 版本2基本上是for-each循环中的版本1

如果您稍后决定要使用一个参数调用该方法,则可以始终重新使用版本1 如果您只有版本2和一个节点,则必须创建一个虚拟列表以使用版本1.

我不认为这里的表现是一个真正的问题。两种方法都有一个(通过引用)参数,它们应该使用大约相同数量的内存。

然而,.NET可能能够更好地优化两者之一。简要介绍两种方法。

答案 1 :(得分:2)

我更喜欢采用单个节点的签名,因为如果我只想在该节点上执行操作并且它是子节点,那么我就不必构造一个人工列表。它在节点列表上同样易于使用,您只需遍历列表并将其应用于每个节点。

答案 2 :(得分:1)

您应该在递归函数中传递尽可能少的参数(即使是专用于您使用的类型的内存大小)。这是我遵循的一般规则。如果你传递一些大的结构,你可能会耗尽内存。如果你使用的是参考类型,那不是问题。

答案 3 :(得分:1)

我能看到的唯一显着差异在于易于打电话。

这个版本3怎么样:

void Visit(Node n)
{
    DoSomethingUsefulWith(n);
    Visit(n.children);
}

void Visit(List<Node> nodes)
{
    foreach (Node n in nodes)
        Visit(n);
}

答案 4 :(得分:1)

我认为版本2没有任何实际好处 - 只是它限制了你的功能的灵活性,因为你不能再用单个节点开始你的递归了。

Imo,版本1更容易阅读,所以我会选择第一。

答案 5 :(得分:1)

我个人认为版本1更好地反映了树的递归定义(通常基于单个节点)。因此,我赞成第1版。

答案 6 :(得分:0)

在风格上,它取决于您正在递归的对象类型的根。

实际上它们是一样的。除非我的大脑在公园.......