好的,我对Java中的递归感到困惑。说我有以下代码:
static int findShortestString(String[] paths, int lo, int hi) {
if(lo==hi)
return lo;
int minindex=findShortestString(paths,lo+1, hi);
if(safeStringLength(paths[lo])<safeStringLength(paths[minindex]))
return lo;
return minindex;
现在问题不在于代码本身,而在于递归是如何工作的。 minindex被设置为等于递归调用。所以第一次运行函数并尝试将minindex设置为某个东西时,它会这样做,然后函数调用自身。但if语句什么时候运行呢?只有当minindex真正拥有真正的价值时它才会运行吗?我只是无法绕过这个。如果minindex导致函数递归并递归,那么if语句什么时候会被检查?什么时候lo==hi
?我不明白:(
答案 0 :(得分:5)
minindex
返回之前, findShortestString
未分配,这在lo == hi
之前不会发生。
每次该方法调用自身时,它会将lo
和hi
之间的差异缩小1,因此最终它们将相等*并且将返回该值。
一个例子,paths = ["p1", "path2", "longpath3"]
:
lo = 0, hi = 2 lo != hi -> call findShortestString(paths, 1, 2) lo = 1, hi = 2 lo != hi -> call findShortestString(paths, 2, 2) lo = 2, hi = 2 lo == hi -> return lo (=2) lo = 1, hi = 2, minindex = 2 length of "path2" < length of "longpath3" -> return lo (= 1) lo = 0, hi = 2, minindex = 1 length of "p1" < length of "path2" -> return lo (= 0)
我试图使用越来越多的缩进来说明每个递归级别的变量值。在每次递归调用开始时,lo
,hi
和minindex
的先前值将被保存(在称为“堆栈”的结构中),而是使用新值。当方法的每次调用返回时,先前保存的值将从堆栈“弹出”以供使用,并从前一个返回值分配minindex
。
*除非lo&gt;嗨,首先,我猜...
答案 1 :(得分:2)
以下是执行游戏:
您自己致电findShortestString()
如果lo不等于喜事继续。否则他们会在这里停止并返回该函数。
再次调用findShortestString()
后,此函数实例中的所有内容都会完全停止,直到计算机有值minindex
(也就是函数返回)才会恢复。我们重新开始顶部的函数的新实例。在其中一个函数返回之前执行的唯一代码是方法调用之前的代码。这可以与while循环进行比较。
一旦其中一个函数实例具有lo==hi
并返回。
控制在此之前切换到函数实例,它将返回的lo
值分配给minindex
。
如果(safeStringLength(paths[lo])<safeStringLength(paths[minindex]))
,那么我们return lo
。另外,我们return minindex
。无论哪种方式,此函数实例都已完成,控件将返回到之前的那个。
现在调用的每个函数现在只在方法调用之后执行代码,因为该方法不会再被调用。我们正在解开一堆电话。所有返回现在都来自最后2个语句,因为顶部的代码不会再次执行。注意只有一个函数实例返回代码的顶部,终止while循环。所有其余的都在递归调用之后用函数部分中的return语句终止。
最后,最后一个函数返回,然后返回到最初调用该函数的代码。
以下是代码实际执行情况的更具可读性的版本:
在递归调用之前的代码中,所有发生的事情都是在lo==hi
之前创建一系列调用。每次调用函数时,lo为1更大。这是一组示例:
findShortestString(2,5);
findShortestString(3,5);
findShortestString(4,5);
findShortestString(5,5);
当它们展开时,每个函数实例比较索引lo处的字符串的字符串长度和使用最短字符串的前一索引的索引。
compare strings at indexes 2 and 5
if the string at 2 is smaller, compare the strings at indexes 2 and 4.
Otherwise, compare the strings with indexes at 3 and 5.
如果lo>hi
在开头,代码将继续运行,直到lo溢出整数并变为负数,然后直到lo最终一直到hi,或者4,94,967,296 - (原始的 - 原始的喜)。换句话说,in将需要长时间。要解决此问题,请在方法的开头添加一个检查,以便在lo>hi
时抛出异常。
可以更好地重写代码:
static int findShortestString(String[] paths, int lo, int hi) {
int indexWithShortestString=lo;
for( int i=lo; i<=hi-1; i++) {
//assumption: lo and hi are both valid indexes of paths
if (paths[i+1].length < paths[i].length)
indexWithShortestString=i+1;
}
}
答案 2 :(得分:1)
想一下堆栈。每次调用递归方法时,都会在堆栈顶部放置一个新的“框架”。该帧包含每个变量的自己的“槽”,独立于下面的帧中的那些。
最终,将创建一个新框架,其中lo
和hi
的值相等,并且该方法将返回而不会推送另一个框架。 (这称为“基本情况”。)当return
发生时,该帧从堆栈中弹出,其下面的帧继续在第二个if
语句处执行。最后,该框架也会弹出,并且恰好在下面的框架上也是如此,依此类推,直到执行返回到原来的调用者。
答案 3 :(得分:0)
每次findShortestString
调用自身时,最终会分配minindex
,然后在if
语句中使用该值。始终将最短字符串的索引设置为高于lo
的索引。
因此,如果存在具有10个findShortestString级别的调用堆栈,则minindex
被分配9次(第一次调用来自另一个函数)。
答案 4 :(得分:0)
这是一个非常混乱的递归函数。但你通常认为它是正确的。每次调用findShortestString()都会将函数推送到堆栈中。它会一直这样做,直到lo == hi。此时,堆栈被展开,相应的递归调用将被分配给它们相应的整数。
在此功能中,您似乎只会返回lo
。因为(safeStringLength(paths[lo])<safeStringLength(paths[minindex])
为真,您将返回lo
。或lo==hi
为真,您将返回lo
答案 5 :(得分:0)
为了声明
int minindex=findShortestString(paths,lo+1, hi);
要评估,方法调用findShortestString(paths,lo+1, hi)
必须返回一个值。因此,在此方法调用返回值之前,不会发生以下if
语句。但是,此方法可能会再次调用自身,并且您将获得嵌套效果。
答案 6 :(得分:0)
基本上,当调用return语句时,函数的执行结束。返回语句之后的所有内容都不再重要(或“存在”)。
因此,当第一个if语句为false时,局部变量minindex将仅存在于findShortestString函数的执行中。
独立处理findShortestString函数的每次执行,无论是递归调用还是从代码中的其他位置调用。即,findShortestString函数的不同执行可以在不同的路径返回并且具有它们自己的值和局部变量。根据输入值,它们可能会在第3,6或7行返回。
minindenx只存在于可以运行第4行的执行中,并且它被赋予findShortestString(paths,lo + 1,hi),如果代码正确则保证有值,否则你将获得无限递归,导致堆栈溢出(双关语无意)。