【问题标题】:Recursive function to reverse find [closed]反向查找的递归函数[关闭]
【发布时间】:2014-08-31 02:16:57
【问题描述】:

char 是递归函数。它不应该有任何 l

【问题讨论】:

  • 这是一个家庭作业问题,因为除了作为学习递归的练习之外,谁愿意递归地这样做?
  • 如果你无法开始你的家庭作业,你应该向你的老师寻求帮助。他们显然还没有为您提供教育而获得报酬。
  • 想想你想停止递归的原因。然后考虑如何在每次递归调用期间访问每个字符。
  • 听起来你的教授要求你实现strrchr

标签: c++ recursion


【解决方案1】:

这是一个非常简单的递归实现。可悲的是,它不是尾递归。

全面实施:

char *rfind(char* str, char ch) { 
     if (*str == '\0')
         return NULL; 
     char * pos = rfind(str + 1, ch);
     if (pos != NULL)
         return pos;
     if (*str == ch)
         return str;
     return NULL;
}

基本情况:str 是零字符的字符串:

     if (*str == '\0')
         return NULL; 

递归调用:

     char * pos = rfind(str + 1, ch);

判断我们是想要字符串剩余部分的结果,还是当前位置:

     if (pos != NULL)
         return pos;
     if (*str == ch)
         return str;
     return NULL;

【讨论】:

  • 您好,我对这种递归有点陌生,但是在您的代码中,它指向 ch 的最后一次出现还是第一次出现?根据我的问题,它应该指向最后一次出现。不过还是非常感谢!
  • @AnitejRao 这很容易验证。 (p - s + 1) 会告诉你返回字符的位置。
  • 我的代码返回一个指针,指向strch 的最后一个实例。如果找不到ch,则返回NULL
  • @AnitejRao - 提示 1 - 在开始搜索之前,前 3 步旋转到行尾的 0。
【解决方案2】:

递归不使用循环。

因此,如果您的函数名为 rfind(...),那么在 rfind 中您必须完成以下所有 3 项:

  • 没有找到就终止 -- 确定您已经到达 str 的开头,然后以 nullptr 退出

  • 以 find 结束——在 str 中的某个位置识别“ch”,然后退出返回该位置(一个 char*)

  • 继续搜索 -- 通过递归调用 rfind(...),并返回递归调用返回的值。


edit - 只是为了好玩,这里有一个 3 参数 rfind 我认为更具可读性

注意:没有循环、退出情况和尾递归。

// find _last_ occurrance of ch in str, starting at indx
char*  rfind(char*  str,  char ch, int indx)
{
   // Req 1: terminate when not found, return nullptr
   if(indx < 0)       return (nullptr);  

   // Req 2: terminate when found, return char*
   if(ch == str[indx]) return(&str[indx]) 

   // Req 3: continue search, return search results
   return ( rfind (str, ch, (indx-1)) );    // keep looking
}

[去掉-3参数rfind用法]


编辑 - 为了完成这个版本,我提供以下内容。结果包括

a) 2个参数rfind,

b) 尾递归,

c) 一些测试代码

我以随意阅读的形式呈现以下内容。您必须添加前向声明或适当地重新安排代码以进行编译。

test_rfind() 的用法:

注意 test_rfind() 有 2 个参数传入 rfind()

int t122()
{
    char str[] = "t123abcdefg*o4";

    size_t strSize = strlen(str);
    std::cout << "\n       strSize = " << strSize << "            " << (void*)str
              << "\n       01234567890123" << std::endl;

    for (size_t i = 0; i < strSize; ++i) {
       test_rfind(str, str[i]);
    }
    test_rfind(str, 'z');
    test_rfind(str, '0');  // digit '0'
    test_rfind(str, 'K');
    // ...
 }

test_rfind 调用 rfind(), 并筛选出 null str 和 null tgt 为测试用户提供反馈:

void test_rfind(char* str, char tgt)
{
   do // not part of the recursion, not really a loop, just a test simplification
   {
      if (0 == str) { std::cout << "       str is null " << std::endl;  break; }

      // ===================================================================

      char* pos = rfind(str, tgt);  // 2 parameter invocation - see below

      // ===================================================================

      if (nullptr == pos) {
         std::cout << "rfind('" << std::setw(14) << str
                << "', '" << tgt << "') :           "
                   << "  char '" << tgt
                   << "' not found" << std::endl;
         break;
      }

      // else found
      std::cout << "rfind('" << std::setw(14) << str
                << "', '" << tgt << "') = "
                << (void*)pos
                << std::setw(20) << pos
                << "    last '"  << pos[0]
                << "' at indx: " << (pos - str) << std::endl;
   }while(0);
}

这是您需要的 2 参数 rfind()。

惊喜!它只是做一些验证,然后使用现有的 3 参数 rfind()。

// two parameter
char*  rfind(char*  str,  char tgt)
{
   // pre-validation
   if (0 == str) return(nullptr); // one null check here, rather than 'inside' recursion

   // pre-validation - tbr: check range (0 <= char <= 127)
   // allow (0 == tgt): a char can be 0

   // now use the 'just for fun' 3 parameter rfind
   return ( rfind(str, tgt, strlen(str)) );  // use tail recursion
}

我发现这种在递归过程中的“中间步骤”处理了几个前(有时是后)验证。这样可以避免这些事情使实际的递归函数复杂化。

第 3 个参数 rfind() 位于此答案的开头附近,并且未更改。

猜猜看——它提供了一个通常必要的功能:在字符串中查找最后一个目标字符,但是您可以在字符串中的任何位置开始搜索。这样,如果您的字符串有 2 个 tgt 字符 - 您使用 2 个参数 rfind() 找到最后一个 ch,然后使用第 3 个参数(而不是缩短 str)找到那个之前的 tgt。

注意:当两个函数的签名是唯一的时,C++ 允许两个函数具有相同的标识符。因此 2 参数和 3 参数版本共享名称“rfind()”。

测试结果:

       strSize = 14            0xbff8f4bd
       01234567890123
rfind('t123abcdefg*o4', 't') = 0xbff8f4bd      t123abcdefg*o4    last 't' at indx: 0
rfind('t123abcdefg*o4', '1') = 0xbff8f4be       123abcdefg*o4    last '1' at indx: 1
rfind('t123abcdefg*o4', '2') = 0xbff8f4bf        23abcdefg*o4    last '2' at indx: 2
rfind('t123abcdefg*o4', '3') = 0xbff8f4c0         3abcdefg*o4    last '3' at indx: 3
rfind('t123abcdefg*o4', 'a') = 0xbff8f4c1          abcdefg*o4    last 'a' at indx: 4
rfind('t123abcdefg*o4', 'b') = 0xbff8f4c2           bcdefg*o4    last 'b' at indx: 5
rfind('t123abcdefg*o4', 'c') = 0xbff8f4c3            cdefg*o4    last 'c' at indx: 6
rfind('t123abcdefg*o4', 'd') = 0xbff8f4c4             defg*o4    last 'd' at indx: 7
rfind('t123abcdefg*o4', 'e') = 0xbff8f4c5              efg*o4    last 'e' at indx: 8
rfind('t123abcdefg*o4', 'f') = 0xbff8f4c6               fg*o4    last 'f' at indx: 9
rfind('t123abcdefg*o4', 'g') = 0xbff8f4c7                g*o4    last 'g' at indx: 10
rfind('t123abcdefg*o4', '*') = 0xbff8f4c8                 *o4    last '*' at indx: 11
rfind('t123abcdefg*o4', 'o') = 0xbff8f4c9                  o4    last 'o' at indx: 12
rfind('t123abcdefg*o4', '4') = 0xbff8f4ca                   4    last '4' at indx: 13
rfind('t123abcdefg*o4', 'z') :             char 'z' not found
rfind('t123abcdefg*o4', '0') :             char '0' not found
rfind('t123abcdefg*o4', 'K') :             char 'K' not found

我最近对递归代码的 -O3 优化印象深刻。

  • 在某些尾递归中,编译器完全删除了递归调用并返回,作为没有发生堆栈溢出的证据,在 -O0 编译时,可执行文件总是崩溃。

  • 在至少一个可重复的测试中,得到的完全优化的递归实现比相应的完全优化的循环实现快 50%。

【讨论】:

    猜你喜欢
    • 2014-12-08
    • 1970-01-01
    • 2019-08-13
    • 2018-04-19
    • 2014-02-22
    • 1970-01-01
    • 2017-03-23
    • 2010-11-28
    • 2020-09-26
    相关资源
    最近更新 更多