如何以递归方式思考?
Posted
技术标签:
【中文标题】如何以递归方式思考?【英文标题】: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) 谁能建议我一些材料/资源来彻底理解递归的概念?
【问题讨论】:
你有一袋信和一条街的房子。你是说“对于每一所房子,检查堆顶的信件,然后交付,然后继续下一个房子”还是你说,“将第一组信件交付到街道上的第一所房子,从街道上移走那所房子,并重复,直到街上没有房子”? 并不是每个问题在递归中都有意义——从数组中打印元素就是一个很好的例子,这更适合纯迭代。从 多维 数组中打印元素可能更适合递归方法 【参考方案1】:有一种思考递归的方法,它可以像迭代一样简单。
在迭代中,我们有一个循环。认为它有 4 个部分:
根据某些“控制”数据决定是继续还是停止,并作为逻辑条件进行评估。
完成工作的主体。有时,主体与下一部分结合在一起。
一种更改“控制”数据的方法。通常通过更换计数器。
再次调用构造(在本例中为循环)的方法。在 c 风格的语言中,这是由 for、while 或 do 语法提供的。
在递归中,我们有一个函数(有时是几个)。它们具有相同的 4 个部分:
根据某些“控制”数据决定是继续还是停止,并作为逻辑条件进行评估。控制数据通常作为参数传递给函数。
完成工作的主体。有时,主体与下一部分结合在一起。
一种更改“控制”数据的方法。通常通过更换计数器。
再次调用构造(在本例中为函数)的一种方式 - 这意味着调用函数(并记住传递更改后的“控制”数据。
这两个结构具有相同的部分也就不足为奇了,因为它们是等价的。
【讨论】:
这是一种有趣的方式来思考这两个概念并将它们联系起来!感谢分享!【参考方案2】:是的,主要是。一般来说,递归是为程序员而不是计算机而完成的。有一些迭代方法在某些情况下可能比递归方法运行得更快,但迭代方法可能需要 300 行代码和递归 3 行。还有一些情况很容易弄清楚如何递归地编程,但是非常难以迭代编写,反之亦然。
一般而言,递归解决方案需要考虑函数。如果我们使用像 C++ 这样的东西,我们可以使用处理字符串引用和事物的解决方案,慢慢调整作为参数传递的字符串。但是,“两次递归”结束附近的点被误导了。这里的原则是,我们可以使用一种递归方法,而不是两次迭代。
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 个重要规则:
-
递归算法,必须有一个基本情况。
一种递归算法,必须改变其状态并移向基数
案子。
递归算法必须以递归方式调用自身。
来源:交互式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<bits/stdc++.h>
!请参阅: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 循环,然后尝试在此基础上编写递归解决方案。 记住所有递归方法都需要一个基本案例来停止递归
【讨论】:
以上是关于如何以递归方式思考?的主要内容,如果未能解决你的问题,请参考以下文章