【问题标题】:Questions regarding a code with recursive methods关于带有递归方法的代码的问题
【发布时间】:2016-04-23 05:27:29
【问题描述】:

我对此代码有一些疑问

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的子字符串而不需要添加第一个字符时代码不起作用?

【问题讨论】:

  • 对不起,第一个问题应该提到,如果没有终止情况,代码不起作用,为什么看起来没有它代码可以正常工作时,为什么还要这样做?
  • 如果没有终止条件,它最终会取一个空字符串的子字符串并崩溃。
  • @Tran- 所以你的意思是说代码在没有这种条件的情况下可以正常工作,if ( s.length() == 0 ) return ""; ?你真的在没有这个条件的情况下运行它吗?
  • @EvanKnowles 当然我了解代码的基本前提,但如果我还掌握了代码的“假设”部分,那将真的有助于我理解。
  • @SabirKhan 是的,我确实尝试在删除该部分的情况下运行它,但我得到了“线程中的异常”java.lang.StringIndexOutOfBoundsException:字符串索引超出范围:java.lang 中的 1 .String.substring(String.java:1963) 在 TC1.reverseString(TC1.java:8) "

标签: java string recursion substring


【解决方案1】:

问题 1:终止环境究竟是如何运作的?如何 s.length 会永远等于 0 吗?

如果你读过the docs on String.substring,那么你就会明白它返回的是从指定字符开始一直延伸到结尾的子字符串。由于您指定了起始字符 1,并且字符从 0 开始编号,因此substring(1) 本质上意味着“从第二个字符开始”。这样,对于嵌套调用,字符串长度恰好减少了一个字符。当然最终会达到0!当您从长度为 1 的字符串中获取 substring(1) 时,它将达到 0,因为对于这样的字符串,“从第二个字符开始”意味着一个空子字符串。

问题2:为什么代码需要有“firstChar”才能 反转字符串?为什么reverseString时代码不起作用 接受0的子字符串,不必添加第一个字符?

嗯,这就是递归的全部内容。如果你取第一个字符,反转字符串的其余部分,然后将该字符附加到反转的其余字符串的 end,如果不是反转的字符串,你会得到什么?

或者,如果你想严谨,这是一件光荣的事情,那就让我们以正确的方式去做吧。我们先证明这个函数正确地反转了长度为 0 的字符串。

反转的空字符串是空字符串本身。由于当输入字符串为空时代码显式返回一个空字符串(顺便说一下,为了清楚起见,最好将length() == 0替换为isEmpty()),这证明该函数适用于0长度的字符串。那没那么难,不是吗?

现在让我们证明,如果该函数适用于长度为i (i >= 0) 的字符串,那么它也适用于长度为i + 1 的字符串。假设s.length() == i + 1。我们取第一个字符,然后调用reverseString( s.substring( 1 ) )。这个调用的参数是一个长度为i 的字符串,因为它正好比s 短一个字符。由于我们假设我们的函数对长度为i 的字符串非常有效,因此结果是我们字符串的正确反转子字符串,从字符 1(第二个)开始。然后我们将s 的第一个字符附加到该字符串,从而使长度为i + 1s 正确反转。

我们已经证明它适用于长度 0,因此从我们的第二个证明可以得出它也必须适用于长度 1。但由此可知,它也适用于长度2。等等,等等……这就是你证明递归函数工作的方式。

现在如果你不添加第一个字符会发生什么。假设该函数对于较短的字符串 (substring(1)) 非常有效,那么您最终会得到一个短一个字符的字符串。一个短一个字符的字符串显然 不是 是原始字符串的反面,因此证明如果您只反转 substring(1) 而没有在结果中附加任何内容,则此函数可能无法工作。

如果你在递归调用中传递substring(0) 会发生什么?这是另一个有趣的问题。在我们证明的第二部分中,我们假设该函数适用于较短的字符串。在这种情况下,substring(0) 不是较短的字符串。其实就是原来的字符串本身,所以传递substring(0)就相当于只传递s。所以我们的证明不再起作用了。此外,由于我们正在进行相同的调用,它会一次又一次地继续调用自己,直到递归变得太深时你得到StackOverflowError

所以整个想法是基于将手头的任务分解成更小的部分,substring(0) 并不是一个更小的部分。另一项有趣的任务是将字符串分成两半,而不是仅仅切掉一个字符然后返回reverseString(right) + reverseString(left)。我建议你尝试这样做(注意奇数/偶数长度),看看它是否有效,并证明它是如何工作的。

【讨论】:

    猜你喜欢
    • 2014-08-14
    • 1970-01-01
    • 2020-11-12
    • 1970-01-01
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    相关资源
    最近更新 更多