有关具有递归方法的代码的问题

时间:2016-01-18 05:46:19

标签: java string recursion substring

我对此代码有一些疑问

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的子字符串而不必添加第一个字符时,为什么代码不起作用?

1 个答案:

答案 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长度字符串。那不是那么难,是吗?

现在让我们来证明,如果函数适用于长度为ii >= 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)。我建议你尝试这样做(小心奇数/偶数长度),看它是否有效,并证明它是如何工作的。