【问题标题】:C++ Recursion problems <confused>C++递归问题<困惑>
【发布时间】:2011-01-16 05:11:27
【问题描述】:

我正在努力理解递归,我想我已经搞定了...我正在尝试构建一个搜索函数(如 std::string.find()),它可以在给定字符串中搜索另一个字符串例如:

给定(大)字符串:“ru the running cat”

搜索(小)字符串:“运行”

我正在尝试为我正在搜索的单词返回一个索引(在上面的情况下它将是 **7)我拥有的递归方法如下 - 我似乎无法得到它正确返回索引。

调用递归函数:

index = index_of(imput.c_str(), search.c_str())

递归方法:

int index_of( const char * i, const char * s) {
 int j;
 if (*s == '\0') { return; }
 if (*i == '\0') {
  return NULL;
 }
 else if ( *i == *s ) {
  index_of((i++), (s++));
 }
 else {
  j += index_of((i++), s);
 }
 return j;
}

另一个可预见的问题是,当(就像在示例中一样 - 好吧,它很糟糕,但我需要一个可以工作的)它到达“ru_”时它仍然卡在“”(SPACE)上 -> 我将如何让它到“重置's指针?

【问题讨论】:

  • 首先,将 int i 重命名为 int j (或其他) - 你已经有了 const char* i !
  • 很好,我什至没有注意到 (int i -> j)
  • 第一个if 语句返回什么值?

标签: c++ pointers recursion search


【解决方案1】:

首先,您的代码中有两个is。那甚至不应该编译。

另外,index_of((i++), (s++)); 实际上等同于:

index_of(i, s); 
++i;
++s;

换句话说,您使用与第一次调用相同的参数来调用 index_of。 (它永远不会返回到 ++i)。

另外,纯粹是风格,但由于返回类型是一个 int,你应该返回 0,并保存 NULL 以用于指针。

【讨论】:

    【解决方案2】:

    这里的主要问题是您使用的是后增量,并且 (i++) 的结果是 i 。你必须使用 ++i

    【讨论】:

      【解决方案3】:

      这就是我的做法,通过将所有内容移动到不同的功能中来简化一切。未经测试,但希望它能给你一个想法。

      /**
       * Checks if one string starts with another string. This returns an
       * incorrect result if it is called where prefix is an empty string.
       */
      bool starts_with_impl(const char* haystack, const char* prefix) {
          if ( *prefix == 0 ){ 
              //reached the end of prefix without having found a difference in characters
              return true;
          }else if ( *haystack == 0 || *prefix != *haystack ){
              //either prefix is longer than haystack or there is a difference in characters.
              return false;
          }
          //move along the haystack and prefix by one character
          return starts_with_impl(++haystack, ++prefix);
      }
      /**
       * Wrapper around starts_with_impl that returns false if prefix is an empty string
       */
      bool starts_with(const char* haystack, const char* prefix) {
          return *prefix ? starts_with_impl(haystack, prefix) : false;
      }
      
      int get_substr_impl(const char* haystack, const char* const needle, int index) {
          if ( *haystack == 0 ){
              //reached the end of haystack with no match, -1 is no string found
              return -1;
          }else if ( starts_with(haystack, needle) ){
              //we have found a substring match.
              return index;
          }
          //move along haystack by one character
          return get_substr_impl(++haystack, needle, ++index);
      }
      /**
       * Wrapper function for the above to hide the fact we need an additional argument.
       * I am avoiding using a default argument as it makes a messy api
       */
      int get_substr(const char* haystack, const char* const needle) {
          return get_substr_impl(haystack, needle, 0);
      }
      

      来自 cmets

      get_substr_impl 方法中有 2 个 const ......

      故意的。

      // means that the data is constant, in other words I can't change the value of needle
      const char* needle;
      
      //means that as well as the data being constant 
      //I can't change the address that the pointer points to.
      const char* const needle;
      

      我不会从 main 方法调用 get_substr_impl 并使用 get_substr 中给出的相同参数吗?

      我将它拆分为 get_substr_impl 有一个额外的(必需的)参数 int index 是函数内部工作所必需的,并且应该始终从 0 开始。虽然您可以调用 get_substr_impl("abc", "a", 0);,但它看起来更好,并且调用get_substr("abc", "a"); 更容易理解并且避免了错误(比如调用get_substr_impl("abc", "a", 1);

      【讨论】:

      • 您在get_substr_impl 方法中有2 个const ......而*other 来自哪里/转到? --- 从main 方法中,我不会只用get_substr 中给出的相同参数调用get_substr_impl 吗? --- 那会是为了错误处理吗?
      • @Eamon 如果 needle 是指向 \0 的指针,它将返回 -1,如果 needle 是 0 则行为未定义。
      • 所有字符串都以空字符串开头。
      • 确实很棒的参数名称,顺便说一句。清晰的名称是工作的一半:-) - 我也会使用它们!
      • @Eamon 你对空的needle 的问题是正确的,所有代码都通过编译器运行,所以应该更正确。
      【解决方案4】:
      1. 不要更改任何状态变量。您的代码不应包含运算符++anywhere。您不是在尝试循环数据结构并以某种方式更改局部变量 - 您每次都尝试生成一个全新但较小的问题。因此,所有这些 ++ 运算符(无论是增量前还是增量后)都是危险信号。
      2. 您有多个子问题。(...所以单函数递归并不理想)。

        让我们系统地看一下。

        假设您有一个有效的index_of,并且您只想使用比您的输入更短的输入来调用它,并且 haystack 和 needle 都不是空的。那么两件事之一可能是:

        • 大海捞针以与针相同的字母开头,您只需要更深入地查看即可验证这一点。
          - 如果验证成功会发生什么 - 如果验证失败怎么办?这是index_of 子问题吗?

        • ...否则草垛开始错误,您需要深入研究。
          - 深入大海捞针是 index_of 子问题吗?

        请注意,如果 haystack 开始 正常,并不一定意味着它以 full 搜索字符串开头 - 如果它开始正常但 从完整的搜索字符串开始,你真的不需要继续寻找。这意味着“starts-with”子问题与 index-of 子问题根本不同:

        • index_of:这里,在索引 0 处找不到搜索字符串意味着您需要进一步查找。
        • starts_with:此处,在索引 0 处找不到搜索字符串意味着您需要停止。

        可能startswith(haystack, needle):= 0==index_of(haystack, needle),但显然index_of 是一个更复杂的问题,需要更昂贵的解决方案 - 所以你不应该这样做;一开始也比较混乱。

      3. 确定你的基本情况 - 当针或干草堆是空的时,这意味着什么?

      4. 好名字有助于澄清事情,而递归在最好的时候很难阅读 - 例如,Yacoby 的回复在这里有一些有意义的选择。

      总结

      我不会为你解决你自己的难题,而是回顾一下一些提示......

      • 避免更改局部变量:您试图定义一个子问题,然后为那些更新、更短的参数调用正确的函数。不要使用副作用,它们会使事情变得非常复杂。
      • 递归并不一定意味着只有一个函数A 调用A - 它可以是任何最终终止的循环调用图;您可以使用更多功能
      • 如果 needle 和 haystack 以相同的字母开头,这并不意味着整个 needle 都在 haystack 的开头 - 如果不是,您仍然需要继续搜索
      • 此行错误:if (*s == '\0') { return 1; }

      【讨论】:

      • 如果不使用++ 运算符,我应该如何增加指针?很好的解释,谢谢!
      • 不要“增加”指针 - 传递代表子字符串的 new 指针 - 即类似于以下内容:if-not-found-here: return index_of(haystack+1, needle);
      • 顺便说一句,“与此类似”的东西会返回不正确的索引;您仍然需要逻辑来计算它以补偿不同的字符串位置以及找不到索引的可能性...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 2013-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多