递归函数的示例

时间:2008-09-24 12:16:32

标签: recursion

有人可以建议用于说明递归函数的编程示例吗?有通常的老马,如 Fibonacci系列 河内的塔 ,但除了它们之外的任何东西都会很有趣

23 个答案:

答案 0 :(得分:118)

This illustration是英文版,而不是实际的编程语言,但对于以非技术方式解释过程非常有用:

A child couldn't sleep, so her mother told a story about a little frog,
  who couldn't sleep, so the frog's mother told a story about a little bear,
     who couldn't sleep, so bear's mother told a story about a little weasel
       ...who fell asleep.
     ...and the little bear fell asleep;
  ...and the little frog fell asleep;
...and the child fell asleep.

答案 1 :(得分:32)

understand recursion,必须首先了解recursion

答案 2 :(得分:18)

递归的经验法则是,"使用递归,当且仅当每次迭代时,您的任务分为两个或更多个类似的任务"。

所以Fibonacci不是递归应用的好例子,而河内是一个很好的例子。

因此,大多数递归的好例子都是在不同的问题中进行树遍历。

例如: 图遍历 - 访问节点永远不会再次访问的要求使图形成为树(树是没有简单循环的连通图)

划分和征服算法(快速排序,合并排序) - 在"划分"之后的部分构成儿童节点,"征服"构造从父节点到子节点的边缘。

答案 3 :(得分:15)

如何测试字符串作为回文?

bool isPalindrome(char* s, int len)
{
  if(len < 2)
    return TRUE;
  else
    return s[0] == s[len-1] && isPalindrome(&s[1], len-2);
}

当然,你可以更有效地使用循环。

答案 4 :(得分:13)

答案 5 :(得分:10)

另外两个“常见嫌疑人”是Quicksort和MergeSort

答案 6 :(得分:8)

从数学世界来看,有the Ackermann function

Ackermann(m, n)
{
  if(m==0)
    return n+1;
  else if(m>0 && n==0)
    return Ackermann(m-1, 1);
  else if(m>0 && n>0)
    return Ackermann(m-1, Ackermann(m, n-1));
  else
    throw exception; //not defined for negative m or n
}

它总是终止,但即使对于非常小的输入也会产生非常大的结果。例如,Ackermann(4,2)返回2 65536 - 3。

答案 7 :(得分:6)

interpreter design pattern是一个非常好的例子,因为许多人没有发现递归。维基百科文章中列出的示例代码很好地说明了如何应用它。但是,仍然实现解释器模式的更基本的方法是嵌套列表的ToString函数:

class List {
    public List(params object[] items) {
        foreach (object o in items)
            this.Add(o);
    }

    // Most of the implementation omitted …
    public override string ToString() {
        var ret = new StringBuilder();
        ret.Append("( ");
        foreach (object o in this) {
            ret.Append(o);
            ret.Append(" ");
        }
        ret.Append(")");
        return ret.ToString();
    }
}

var lst = new List(1, 2, new List(3, 4), new List(new List(5), 6), 7);
Console.WriteLine(lst);
// yields:
// ( 1 2 ( 3 4 ) ( ( 5 ) 6 ) 7 )

(是的,我知道如果您期望一个名为Eval的函数,在上面的代码中发现解释器模式并不容易......但实际上,解释器模式并没有告诉我们函数被调用甚至是什么它的作用和GoF书明确列出了上述模式的例子。)

答案 8 :(得分:6)

这是一个来自文件系统世界的实用例子。此实用程序递归计算指定目录下的文件。 (我不记得为什么,但很久以前我真的需要这样的东西......)

public static int countFiles(File f) {
    if (f.isFile()){
        return 1;
    }

    // Count children & recurse into subdirs:
    int count = 0;
    File[] files = f.listFiles();
    for (File fileOrDir : files) {
        count += countFiles(fileOrDir);
    }
    return count;
}

(注意,在Java中,File实例可以表示普通文件或目录。该实用程序从计数中排除目录。)

一个常见的现实世界的例子是来自Commons IO图书馆的FileUtils.deleteDirectory();见API doc&amp; source

答案 9 :(得分:6)

在我看来,递归很有必要,但大多数可以使用递归的解决方案也可以使用迭代完成,迭代效率更高。

这里说的是一种在嵌套树(例如ASP.NET或Winforms)中查找控件的递归方法:

public Control FindControl(Control startControl, string id)
{
      if (startControl.Id == id)
           return startControl

      if (startControl.Children.Count > 0)
      {
           foreach (Control c in startControl.Children)
           {
                return FindControl(c, id);
           }
      }
      return null;
}

答案 10 :(得分:4)

现实世界的例子是“物料清单成本核算”问题。

假设我们有一家制造最终产品的制造公司。每个产品都可以通过其零件清单和组装这些零件所需的时间来描述。例如,我们使用机箱,电机,卡盘,开关和电线制造手持式电钻,耗时5分钟。

鉴于每分钟标准人工成本,制造每种产品需要多少成本?

哦,顺便说一下,购买了一些零件(例如电线),所以我们直接了解它们的成本。

但我们实际上自己制造了一些零件。我们用外壳,定子,转子,轴和轴承制造电机,需要15分钟。

我们用冲压件和电线制造定子和转子,......

因此,确定成品的成本实际上相当于遍历表示我们流程中所有零件清单关系的树。用递归算法很好地表达了这一点。它当然也可以迭代完成,但核心理念与自己动手的簿记混合在一起,因此不清楚发生了什么。

答案 11 :(得分:2)

我认识的最毛茸茸的例子是Knuth的Man or Boy Test。 除了递归之外,它还使用嵌套函数定义(闭包),函数引用和常量/函数二元函数(按名称调用)的Algol特性。

答案 12 :(得分:2)

正如其他人已经说过的那样,许多规范的递归示例都是学术性的。

我过去遇到的一些实际用途是:

1 - 导航树结构,例如文件系统或注册表

2 - 操作可能包含其他容器控件的容器控件(如Panels或GroupBoxes)

答案 13 :(得分:1)

我个人最喜欢的是Binary Search

编辑:此外,树遍历。例如,沿文件夹文件结构向下走。

答案 14 :(得分:1)

我最喜欢的排序,Merge Sort

(收藏,因为我记得算法在性能方面不是太糟糕)

答案 15 :(得分:1)

Guido van Rossum的

Implementing Graphs在Python中有一些递归函数来查找图中两个节点之间的路径。

答案 16 :(得分:0)

processing lists怎么样,如:

  • map(和andmap,ormap)
  • fold(foldl,foldr)
  • 过滤
  • 等...

答案 17 :(得分:0)

以下是我在此网站上发布的一个示例,用于递归生成菜单树:Recursive Example

答案 18 :(得分:0)

如何反转字符串?

void rev(string s) {
  if (!s.empty()) {
    rev(s[1..s.length]);
  }
  print(s[0]);
}

理解这有助于理解递归。

答案 19 :(得分:0)

很久以前,不久前,小学生们使用Logo和Turtle Graphics学习了递归。 http://en.wikipedia.org/wiki/Turtle_graphics

递归对于通过详尽的试验解决谜题也很有用。有一种叫做“填充”(Google it)的谜题,你可以在其中得到像填字游戏一样的网格和单词,但没有线索,没有编号的正方形。我曾经为拼图出版商编写了一个使用递归的程序来解决难题,以确保已知的解决方案是独一无二的。

答案 20 :(得分:0)

递归函数非常适合使用recursively defined datatypes

  • 自然数为零或另一自然数的继承者
  • 列表是空列表或其他带有前面元素的列表
  • 树是具有一些数据和零个或多个其他子树的节点

答案 21 :(得分:0)

  • 阶乘
  • 深入遍历树(在文件系统,游戏空间或任何其他情况下)

答案 22 :(得分:-1)

将电子表格列索引翻译为列名称。

它比听起来更棘手,因为电子表格列不能正确处理'0'数字。例如,如果从Z增加到AA时将A-Z作为数字,则可能是从9到11或9到00而不是10(取决于A是1还是0)。即使Microsoft Support example对于高于AAA的任何事情也是错误的!

递归解决方案有效,因为您可以直接递归到这些新数字边界。此实现在VB.Net中,并将第一列('A')视为索引1。

Function ColumnName(ByVal index As Integer) As String
    Static chars() As Char = {"A"c, "B"c, "C"c, "D"c, "E"c, "F"c, "G"c, "H"c, "I"c, "J"c, "K"c, "L"c, "M"c, "N"c, "O"c, "P"c, "Q"c, "R"c, "S"c, "T"c, "U"c, "V"c, "W"c, "X"c, "Y"c, "Z"c}

    index -= 1 'adjust index so it matches 0-indexed array rather than 1-indexed column'

    Dim quotient As Integer = index \ 26 'normal / operator rounds. \ does integer division'
    If quotient > 0 Then
        Return ColumnName(quotient) & chars(index Mod 26)
    Else
        Return chars(index Mod 26)
    End If
End Function