【问题标题】:How to think in recursive way?如何以递归方式思考?
【发布时间】:2013-07-10 18:45:10
【问题描述】:

为了理解贪婪方法和动态规划等高级算法概念,首先需要精通递归。

我对递归比较陌生。每当提出问题时,首先想到的就是使用迭代的解决方案。尽管我知道递归方法的含义以及它是如何工作的,但以递归的方式思考是非常困难的。

请回答以下问题以提供帮助:

1) 任何迭代方法都可以用递归代替吗?

例如,如何递归打印大小为n的数组中的元素?

for i 0 to n
 Print a[i]

2) 如何递归解决问题?步骤是什么?是否有任何提示可以确定问题可以递归解决?

例如:如果要求打印出一个字符串的所有子字符串

INPUT: CAT
OUTPUT: CAT,CA,A,AT,T

我可以快速想出一个迭代的方式。使用两个循环可以解决问题。

但是如何递归地解决它。如何确定一个问题可以递归地解决。

如果我的第一个问题的答案是肯定的,那么使用两次递归而不是迭代可以解决我的问题吗?

3) 谁能建议我一些材料/资源来彻底理解递归的概念?

【问题讨论】:

  • 你有一袋信和一条街的房子。你是说“对于每一所房子,检查堆顶的信件,然后交付,然后继续下一个房子”还是你说,“将第一组信件交付到街道上的第一所房子,从街道上移走那所房子,并重复,直到街上没有房子”?
  • 并不是每个问题在递归中都有意义——从数组中打印元素就是一个很好的例子,这更适合纯迭代。从 多维 数组中打印元素可能更适合递归方法

标签: string algorithm recursion iteration tail-recursion


【解决方案1】:

有一种思考递归的方法,它可以像迭代一样简单。

在迭代中,我们有一个循环。认为它有 4 个部分:

  1. 根据某些“控制”数据决定是继续还是停止,并作为逻辑条件进行评估。

  2. 完成工作的主体。有时,主体与下一部分结合在一起。

  3. 一种更改“控制”数据的方法。通常通过更换计数器。

  4. 再次调用构造(在本例中为循环)的方法。在 c 风格的语言中,这是由 for、while 或 do 语法提供的。

在递归中,我们有一个函数(有时是几个)。它们具有相同的 4 个部分:

  1. 根据某些“控制”数据决定是继续还是停止,并作为逻辑条件进行评估。控制数据通常作为参数传递给函数。

  2. 完成工作的主体。有时,主体与下一部分结合在一起。

  3. 一种更改“控制”数据的方法。通常通过更换计数器。

  4. 再次调用构造(在本例中为函数)的一种方式 - 这意味着调用函数(并记住传递更改后的“控制”数据。

这两个结构具有相同的部分也就不足为奇了,因为它们是等价的。

【讨论】:

  • 这是一种有趣的方式来思考这两个概念并将它们联系起来!感谢分享!
【解决方案2】:
  1. 是的,主要是。一般来说,递归是为程序员而不是计算机而完成的。有一些迭代方法在某些情况下可能比递归方法运行得更快,但迭代方法可能需要 300 行代码和递归 3 行。还有一些情况很容易弄清楚如何递归地编程,但是非常难以迭代编写,反之亦然。

  2. 一般而言,递归解决方案需要考虑函数。如果我们使用像 C++ 这样的东西,我们可以使用处理字符串引用和事物的解决方案,慢慢调整作为参数传递的字符串。但是,“两次递归”结束附近的点被误导了。这里的原则是,我们可以使用一种递归方法,而不是两次迭代。

  3. http://introcs.cs.princeton.edu/java/23recursion/ 这个网站(在 google 搜索中排名靠前)教授了很多关于递归的数学理论,并包含一个常见问题解答,它可能会给你一个更令人满意的答案。

    李>

【讨论】:

  • 感谢您的回答。您能解释一下如何递归打印数组中的所有元素吗?
  • 基本上,我们设置一个带有输入参数的函数,然后我们在更少的输入上调用这个相同的函数,在 CAT 的情况下,我们在 CAT 上调用它,然后在 CA 上调用它,然后是 C,然后是 A,然后在 AT 上调用它,然后是 A,然后是 T。按此顺序调用它的原因是因为递归代码就是这样工作的。我们还将通过简单地检查输出数据结构来实现检查以防止 A 被打印两次。
  • @RahulKurup 我给出了答案。
【解决方案3】:

让我们做一个简单的任务。打印从 1 到 10 的数字。我这里使用 Python2.7。

for i in range(1,11):
    print i

现在让我们尝试做同样的事情,使用递归。

>>> def print_me(n):
    if n > 0:
        print_me(n - 1)
        print n
    else:
        return

>>> print_me(10)
1
2
3
4
5
6
7
8
9
10

那我们怎么看呢?

  • Step1:想想我的界限。我需要两个。 1 和 10。接下来。
  • Step2:打印语句。这就是我们的动机。打印数字。和 我们希望它从 1 到 10。所以我需要先打印 1。
  • 第 3 步:我们首先编写一个函数来完成打印 作为参数传递的数字。让我们想想,主要 任务。

    def print_me(n): 打印n

  • 第 4 步:如果 n

    def print_me(n): 如果 n > 0: 打印 n 别的: 返回

  • 第 5 步:现在我想将 1 到 10 的数字传递给这个函数,但是 我们不想要 1 到 10 的循环,然后将其传递给我们的函数。我们 希望它以递归方式完成。

什么是递归? 简单来说,就是递归过程或定义的重复应用。

所以为了让它递归,我们需要调用函数本身。我们希望通过我们的 1 到 10 的范围。

def print_me(n):
    if n > 0:
        print_me(n - 1)
        print n
    else:
        return

总结: 所有递归调用都必须遵守 3 个重要规则:

  1. 递归算法,必须有一个基本情况。
  2. 一种递归算法,必须改变其状态并移向基数 案子。
  3. 递归算法必须以递归方式调用自身。

来源:交互式python

另一个用 Javascript 查找阶乘的程序:

function factorial(n){
    if (n == 1)
        {
        return 1;
        }
    else {
        return n * factorial(n-1);
        }
}

【讨论】:

    【解决方案4】:
    @Test
    public void testStrings() {
       TreeSet<String> finalTree = getSubStringsOf("STACK");
        for(String subString : finalTree){
            System.out.println(subString);
        }
    }
    
    public TreeSet<String> getSubStringsOf(String stringIn) {
        TreeSet<String> stringOut = new TreeSet<String>();
        if (stringIn.length() == 1) {
            stringOut.add(stringIn);
            return stringOut;
        } else {
            for (int i = 1; i < stringIn.length() ; i++) {
                String stringBefore = stringIn.substring(0, i);
                String stringAfter = stringIn.substring(i);
                stringOut.add(stringBefore);
                stringOut.add(stringAfter);
                stringOut.addAll(getSubStringsOf(stringBefore));
                stringOut.addAll(getSubStringsOf(stringAfter));
            }
            return stringOut;
        }
    }
    

    我不知道你是否需要解释。每次可能时,您都将字符串分成两部分。因此,Cat 被分成 CA,T 和 C,AT,您将它们添加到子字符串列表中,然后查找这些子字符串的每个子字符串。如果字符串是单个字符,则将单个字符添加到树中。

    编辑:这是堆栈的输出:

    A AC ACK C CK K S ST STA STAC T TA TAC TACK

    再次编辑:如您所见,每次运行方法 subString 时,都会在其中使用两次,除非它是单个字符串。因此复杂度为 O(n²)。对于“STACK”,程序的长度为 0.200 毫秒,“STACKSTACKSTACK”(3 次堆栈)为 2 秒,“STACKSTACKSTACKSTACKSTACK”理论上是 2^10 倍,因此为 2000 秒。

    【讨论】:

      【解决方案5】:

      这是我在 Python 中的简单测试:

      def p(l, index):
          if index == len(l):
              return
          else:
              print l[index]
              index = index + 1
              p(l, index)
      

      然后调用:

      p("123456", 0)
      

      【讨论】:

        【解决方案6】:

        这是在 c++ 中递归打印数组的代码

        #include<iostream>
        using namespace std;
        
        void print(int arr[],int n)
        {
        if(n==0)     //base case. function call hits the base case when n==0
        {
        cout<<arr[0]<<" ";
        return;
        }
        print(arr,n-1);   //function will be called recursively until it reaches the 
                            base case.
        cout<<arr[n]<<" ";
        }
                      //Driver function
        int main()
        {
        int arr[]={10,20,30,40,50}; //array has been initialized with values 
                                                       // 10,20,30,40,50 
        
        int n=sizeof(arr)/sizeof(arr[0]) ; //n stores the size of array,i.e, n=5; 
        print(arr,n-1);   //print function has been called with arr[] and (n-1) as 
                            parameters.
        return 0;
        }
        

        我希望它在某种程度上有所帮助

        【讨论】:

        • 请在您的代码中添加一些解释,以便其他人可以从中学习
        • 请不要鼓励使用#include&lt;bits/stdc++.h&gt;!请参阅:here
        • 谢谢,Adrian 让我知道,我主要是在做竞技编程时使用它,不过它在比赛中更节省时间和高效。
        【解决方案7】:

        CAT问题可以这样解决:(Python代码)

        s = "CAT"
        op = []
        def sub(s):
            # terminating condition.
            if (len(s) == 0) : return
        
            if s not in op : op.append(s)
            
            # slices from begning
            sub(s[1:])
            # slices from the end
            sub(s[:-1])
        
        sub(s)
        print(op)
        
        Output : ['CAT', 'AT', 'T', 'A', 'CA', 'C']
        

        【讨论】:

          【解决方案8】:

          我认为我们可以使用递归函数解决任何迭代问题,但它可能不是最有效的方法。这是一个在 Python3 中同时使用“for 循环”和“递归”打印列表中项目的简单示例:

          a = [ 1,2,3,4,5] 
          #iterative approach using a for loop
          for i in nums:
             print(i)
          
          #recursive approach
          def printing_list(nums):
              #base case
              if len(nums)==0:
                  return
              #recursive
              else:
                  print(nums[0])
              return printing_list(nums[1:])
          
          printing_list(nums)
          

          要递归地思考,最好先编写 for 循环,然后尝试在此基础上编写递归解决方案。 记住所有递归方法都需要一个基本案例来停止递归

          【讨论】:

            猜你喜欢
            • 2022-10-09
            • 1970-01-01
            • 1970-01-01
            • 2017-04-13
            • 2013-01-25
            • 2012-03-27
            • 2023-03-08
            • 1970-01-01
            • 2018-04-09
            相关资源
            最近更新 更多