我对此代码有一些疑问
public static String reverseString( String s )
{
if ( s.length() == 0 )
return "";
String firstChar = s.substring( 0, 1 );
String reverseRest = reverseString( s.substring( 1 ) );
String result = reverseRest + firstChar;
return result;
}
public static void main( String[] args )
{
String a = "The sky's the limit!";
System.out.println( reverseString( a ) );
}
问题1:终止情况究竟如何? s.length如何等于0?
问题2:为什么代码需要拥有" firstChar"能够扭转字符串?当reverseString接收0的子字符串而不必添加第一个字符时,为什么代码不起作用?
答案 0 :(得分:5)
问题1:终止情况究竟如何?怎么样 将s.length等于0?
如果您阅读the docs on String.substring
,那么您将了解它返回以指定字符开头并一直延伸到结尾的子字符串。由于您指定起始字符1,并且字符从0开始编号,substring(1)
本质上意味着"从第二个字符开始#34;。这样,字符串长度恰好减少了嵌套调用的一个字符。当然它最终会达到0!从长度为1的字符串中取substring(1)
时,它将达到0,因为这样的字符串"从第二个字符开始#34;表示空子字符串。
问题2:为什么代码需要拥有" firstChar"能够 扭转字符串?为什么反向串时代码不起作用 接受子串为0而不必添加第一个字符?
嗯,这就是递归的全部意义所在。如果您取第一个字符,则反转字符串的其余部分,然后将该字符附加到反向字符串的 end ,如果不是反转字符串,您会得到什么?< / p>
或者,如果你想要严谨,这是一件光荣的事,那么让我们以正确的方式去做。让我们首先证明这个函数正确地反转了长度为0的字符串。
反向空字符串是空字符串本身。由于代码在输入字符串为空时显式返回空字符串(顺便说一下,为了清楚起见,最好用length() == 0
替换isEmpty()
),这证明该函数适用于0长度字符串。那不是那么难,是吗?
现在让我们来证明,如果函数适用于长度为i
(i >= 0
)的字符串,那么它也适用于长度为i + 1
的字符串。假设s.length() == i + 1
。我们取第一个字符,然后拨打reverseString( s.substring( 1 ) )
。此调用的参数是一个长度为i
的字符串,因为它恰好比s
短一个字符。由于我们假设我们的函数适用于长度为i
的字符串,因此结果是从字符1(第二个)开始的字符串的正确反转子字符串。然后,我们将s
的第一个字符附加到此字符串,从而使s
长度i + 1
正确反转。
我们已经证明它适用于0
长度,因此从我们的第二个证据可以看出,它必须在长度1
中正常工作。但由此可见,它也适用于2
长度。依此类推......这就是你如何证明递归函数的工作原理。
现在如果你没有添加第一个字符会发生什么。假设该函数完美地用于较短的字符串(substring(1)
),则最终会使字符串缩短一个字符。一个较短的字符串显然不与原始字符相反,因此证明如果您只是反向substring(1)
而不向结果添加任何内容,则此函数可能无法正常工作
如果在递归调用中传递substring(0)
会怎样?这是另一个有趣的问题。在我们的证明的第二部分中,我们假设该函数适用于较短的字符串。在这种情况下,substring(0)
不是更短的字符串。实际上,它本身就是原始字符串,因此传递substring(0)
等同于传递s
。所以我们的证据不再适用。此外,由于我们正在进行相同的通话,它会一次又一次地自我调用,直到递归变得过深时得到StackOverflowError
。
因此,整个想法的基础是将手头的任务分解成更小的部分,substring(0)
不是一个小部分。另一个有趣的任务是将字符串分成两半而不是仅仅切掉一个字符,然后返回reverseString(right) + reverseString(left)
。我建议你尝试这样做(小心奇数/偶数长度),看它是否有效,并证明它是如何工作的。