【问题标题】:What is a good example of recursion other than generating a Fibonacci sequence?除了生成斐波那契数列之外,还有什么递归的好例子?
【发布时间】:2011-06-24 03:02:41
【问题描述】:

可能的重复:
Real-world examples of recursion
Examples of Recursive functions

我看到大多数编程语言教程都通过使用一个简单的例子来教授递归,即如何生成斐波那契数列,我的问题是,除了生成斐波那契数列之外,还有其他很好的例子来解释递归是如何工作的吗?

【问题讨论】:

  • 斐波那契真的不是“递归的好例子”。
  • 也是stackoverflow.com/questions/126756/… 的副本。 (嗯,与这个不同,这个问题没有被标记为 C++。但我怀疑这与理解递归有关。)
  • @Nabb:为什么不呢?我认为这是一个现象示例,因为它可以进行许多智能优化,它不仅可以解释普通递归,还可以解释记忆化和动态编程。
  • 奇怪的是这些"Hey, give me an example of ____." 的问题如何获得这么多票。

标签: c++ algorithm recursion fibonacci


【解决方案1】:

经典的是二叉树搜索:

def findval (node,val):
    if node == null:
        return null
    if node.val = val:
        return node
    if node.val > val:
        return findval (node.left,val)
    return findval (node.right,val)

findval (root,thing_to_find)

这可能比一个简单的公式复杂一点,但它是递归的“面包和黄油”使用,它说明了使用它的最佳位置,即最小化递归级别。

我的意思是:您可以将两个非负数相加:

def add (a,b):
    if b == 0:
        return a
    return add (a+1,b-1)

但是你会发现自己很快就会用完大量的堆栈空间(当然,除非编译器优化了尾端递归,但你可能应该忽略你关心的教学水平)。

【讨论】:

  • 是关于退出堆栈空间的旁注是重振python尾端递归巨魔的诱饵吗?这绝对是一个 python 问题...
  • 不,虽然 看起来 像 Python,但它确实是伪代码(我发现 Python 是一个非常好的伪代码模板)。我只是说,如果编译器不执行尾部优化,你需要非常严格地控制堆栈级别,以免耗尽。
  • 我最喜欢的是 :: 如果你找到了一个很好的例子,那么你就完成了,否则搜索例如 here
【解决方案2】:

其他答案提到了各种算法,这完全可以,但是如果您想要更“具体”的示例,则可以列出某个目录及其子目录中的所有文件。分层文件系统是递归(树)结构的一个著名示例,您可以使用这个具体示例展示深度优先和广度优先搜索。

【讨论】:

  • +1。直到我也提供了相同的答案之后,才错过了这个答案。我添加了示例代码
【解决方案3】:

我最喜欢的递归示例是Towers of Hanoi:要将一堆棋子从极点 A 移动到极点 B,您将最下面的棋子上方的所有东西移动到不是 A 或 B 的极点,然后将最低的棋子移动到B,然后你移动放在最低一块顶部的“辅助杆”上的堆栈。对于第一步和第三步,您将递归地遵循此指令。查看链接以获得更长的解释:)

【讨论】:

  • +1。此外,可以稍微调整 ToH 以迫使您更多地考虑工作中的递归;例如,一个环一次只能移动一个极点(没有直接的 A->C)等。递归的好习惯!
  • 我最近在阅读 JavaScript 的优秀部分时遇到了这个问题。我花了大约一个小时在白板上思考和绘图,然后才意识到它是一个多么简洁的算法。困难的部分是弄清楚当参数在递归调用中切换时算法正在解决什么子问题。
【解决方案4】:

检查palyndrome

bool recursivePalindrome(std::string str, unsigned int index = 0) {
    if (index > str.length()/2) return true;
    else if (str[index] == str[str.length()-index-1])
        return recursivePalindrome(str, ++index);
    else return false;
}

或者说不那么严肃:)

void StackOverflow()
{
   StackOverflow();
}

【讨论】:

  • 虽然一个好的尾端优化器会简单地将其转换为while(1); :-)
  • 以非递归方式执行回文似乎很多更容易,不过:unsigned last = str.size() - 1; while (index < last) if (str[index++] != str[last--]) return false; return true;.
  • 这接近于回文并且更严重::(){ :|: & };:
【解决方案5】:

如何找到阶乘。

int GetFactorial(int n )
{
  if ( n==0) return 1;
  return n*GetFactorial(n-1);
}

这个想法是,阶乘被递归地定义为 n 和 (n-1) 的阶乘的乘积。从递归定义中,你得到你的递归。

【讨论】:

  • 好吧,阶乘与斐波那契并没有什么不同,是吗?
  • 没错,但已经足够不同了:)
  • @sbi 与斐波那契不同,递归计算阶乘与以合理的迭代方式进行计算是相同的 big-O 运行时,因此它绝对是一种改进。另一方面,它没有演示多次递归调用。
  • @Nick:你说得有道理,尽管我仍然认为两者非常相似。 (哦,如果你使用模板元编程来做斐波那契,那将避免重复计算相同的结果。:)
【解决方案6】:

作为文件系统的一部分遍历目录树的文件夹层次结构是一个很好的真实示例。查看此 SO 帖子以获取 C++ 示例:

Why am I having problems recursively deleting directories?

【讨论】:

【解决方案7】:
  • 这里给出的大多数其他示例都是数学示例,它们实际上只是重新说明了递归的相同应用。
  • 搜索和排序变体是很好的算法示例,但对于初学者来说通常有点过于复杂。
  • 河内塔很经典,但真的很做作。

=================

我用来演示递归的简单功能的示例是目录树中的递归文件处理。

这是一个 C# 示例

void ProcessFiles( string sFolder )
{
    foreach( string f in Directory.GetFiles( sFolder ) )
    {
        DoSomethingTo( f );
    }
    foreach( string d in Directory.GetDirectories( sFolder ))
    {
        ProcessFiles( d );
    }
}

【讨论】:

    【解决方案8】:

    归并排序是一个很好的算法示例,该算法在递归实现时更易于阅读和理解。

    这是合并排序的高级伪代码版本:

    def merge_sort(List sortlist)
        if sortlist.length <= 1 return sortlist
        split sortlist into leftlist and rightlist
        return merge(merge_sort(leftlist), merge_sort(rightlist))   
    
    def merge(List leftlist, List rightlist)
        while(leftlist and rightlist not empty)
            compare leftlist.first to rightlist.first
            pop lowest value off its list and append to resultlist
        append any remains of leftlist onto resultlist
        append any remains of rightlist onto resultlist
        return resultlist
    

    这样的迭代版本编写和可视化要复杂得多。

    【讨论】:

    • @back2dos:是的 +1,快速排序是另一个很好的例子。我认为在教程的情况下合并排序可能更容易理解,但它们基本上非常相似。
    【解决方案9】:

    有几个示例:

    Catalan numbers

    T(n) = Sum(T(i)*T(n-i)) for all 1 <= i < n
    

    Ackermann function:

    A(x,y) = y+1 (if x = 0)
    A(x,y) = A(x-1,1) (if y=0)
    A(x,y) = A(x-1, A(x,y-1)) otherwise.
    

    Simple Maze problem

    Finding Hamiltonian Path problem

    阶乘。

    您可以查看wiki 页面以获取其他参考资料。

    【讨论】:

    • 加泰罗尼亚数字具有更高效的迭代/尾递归形式。
    • @Donal Fellows,斐波那契数迭代算法比递归 O(n) 比 (O((1+sqrt(5))^n) 更强大,如果你说你可以使用 memoization ,您也可以对加泰罗尼亚语数字使用记忆递归。
    • 记忆化确实适用于所有这些情况,但在有线性算法的情况下它就不那么重要了。真正受益的是真正的非线性算法。 (此外,递归的最佳示例涉及递归结构,例如文件系统或其他类型的树。)
    【解决方案10】:

    递归的好例子通常与底层数据结构或问题本身是递归的情况有关:树、图、使用分治法的算法(如许多种类)、递归语法的解析器(如常见的算术表达式)、为类似国际象棋的两人游戏(例如考虑 Nim)、组合问题等找到策略。

    【讨论】:

      【解决方案11】:

      尝试递归二分查找: http://www.fredosaurus.com/notes-cpp/algorithms/searching/rbinarysearch.html

      或递归快速排序: http://www.dreamincode.net/forums/topic/72311-recursive-quicksort-algorithm/

      这些只是庞大的递归函数世界中的两个小例子。

      【讨论】:

        【解决方案12】:

        算术表达式的计算可以递归地或使用堆栈迭代地完成。比较这两种方法可能很有启发性。

        【讨论】:

          【解决方案13】:

          我的岳母参加了 C 的入门课程。她有一个家庭作业问题,例如:

          你有一根金属条(长度为 len)和一个数字 将金属切割成的订单数 (n) 各种长度。你想最大化 使用的金属量,但 不能超过总长度。

          讲师建议以二进制形式从 1 迭代到 2**n,如果相应位为 0,则排除订单,如果其位为 1,则包括订单,同时跟踪最大和。他的提议将在多项式时间内运行。

          存在使用递归knapsack algorithm 的另一种解决方案。您可以从 len 向下迭代到 1 并进行深度优先搜索以递归地找到长度的总和。

          我使用递归的另一个领域是Huffman coding(用于压缩字符串),但这没有背包问题的直观感觉。

          递归是一个完全不同的绝妙概念。祝您学习或教授它。

          【讨论】:

            【解决方案14】:

            阿克曼函数:

            /* undefined if m and n are negative */
            uint32 ackermann( uint32 m, uint32 n )
            {
              if( m < 0 && n < 0 ) { exit(1); /* invalid m and n */ }
            
              if( m == 0 ){ return n + 1; }
            
              if( m > 0 && n == 0 ){ return ackermann( m - 1, 1 ); }
            
              if( m > 0 && n > 0 ){ return ackermann( m - 1, ackermann(m, n - 1) ); }
            }
            

            m > 0 的多重比较是多余的(可以简化)。然而,让它们保持原样显示了 Ackermann 函数的标准定义。

            但是,除了斐波那契数之外,人们不必走得太远就能找到有趣的递归函数。

            您拥有最大公分母 (GDC) 函数、快速排序和始终典型的二分搜索算法。

            【讨论】:

              【解决方案15】:

              任何有层次的东西。 例如列出你老板下面的所有员工。

              【讨论】:

                【解决方案16】:

                递归在数学归纳法中找到了它的基础,应该这样教授。

                通过归纳定义函数可以通过列表处理清楚地公开。例如,关于fold 有很多话要说。

                然后,移动到树上。

                【讨论】:

                  【解决方案17】:

                  显然,它不是 C++,但这个概念是合理的:

                  PHP递归遍历嵌套多维数组:

                  public function recurse_me($collection) {
                      foreach ($collection as $value) {
                          if (is_array($value)) {
                              $this->recurse_me($value);
                          } else {
                              // process value.
                          }
                      }
                  }
                  

                  【讨论】:

                    【解决方案18】:

                    我记得我通过编写一个搜索word ladders 的程序理解递归。在给定的字典中。

                    【讨论】:

                      【解决方案19】:

                      学术例子是阶乘

                      n!

                      计算。 在现实生活中,您可以获得数学库。

                      【讨论】:

                      • 阶乘很适合描述如何递归工作。这是为什么你应该使用递归(在像C++这样的语言中)的一个不好的例子。
                      • @Henk 至少它比斐波那契更好。在函数式语言中,(尾)递归是您计算阶乘的方式!
                      • @Nick:实际上,这也是你计算斐波那契数的方式。
                      • @Donal 当然,但 循环 是在纯函数式语言中以尾递归方式完成的。计算斐波那契“递归方式”需要两次递归调用,因此不能尾递归。
                      • @Nick:错了,它需要更复杂的函数(可以包装)。这是 Erlang 中的一个示例,但可以简单翻译:en.literateprograms.org/…
                      【解决方案20】:

                      有些排序算法依赖于递归。

                      然后,还有二分查找,它是用递归实现的。

                      【讨论】:

                      • 这些解释递归有点复杂。
                      • @Gunner,一个写得很好的递归二分查找应该不超过十行代码。
                      【解决方案21】:

                      【讨论】:

                        【解决方案22】:

                        堆排序也是一个很好的例子。您可以在 Cormen、Rivest 和其他人的“算法简介”中阅读有关它的内容。很棒的书,我希望你能从中找到很多有趣的东西。

                        【讨论】:

                          【解决方案23】:

                          链接节点类型结构上的许多操作可以是递归的。其他人提到了 BST,但如果您不想解释它们是什么,请考虑在线性未排序列表中搜索最大值:

                          int MaxValue(Node node)
                          {
                              if (node == null)
                                  return 0;
                          
                              if (node.Next == null)
                                  return node.Value;
                          
                              int maxNext = MaxValue(node.Next);
                              return node.Value > maxNext ? node.Value : maxNext;
                          }
                          

                          列表(在本例中为链表)在现实世界中很容易解释;您的听众甚至不必具有编程背景。您可以简单地将其描述为一组未排序的框或数字列表。

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2012-03-28
                            • 2011-07-27
                            • 2012-11-19
                            • 2023-04-06
                            • 1970-01-01
                            • 2016-11-07
                            • 2012-02-16
                            • 1970-01-01
                            相关资源
                            最近更新 更多