【问题标题】:Reversing A String in C++ (Using Recursion)在 C++ 中反转字符串(使用递归)
【发布时间】:2020-06-28 21:15:11
【问题描述】:
#include <iostream>
#include <string>
using namespace std;
void ReverseString(string &S, int size)
{
    static int start = 0;
    if (start == size - 1 || start == size)
    {
        return;
    }
    else
    {
        swap(S[start++], S[size - 1]);
        ReverseString(S, size - 1);
    }
}
int main()
{
    cout << "enter a string to reverse" << endl;
    string s;
    getline(cin, s);
    cout << "Before Reversing" << endl;
    cout << s << endl;
    ReverseString(s, s.size());
    cout << "After Reversing" << endl;
    cout << s << endl;
    return 0;
}

我正在尝试尽可能多地确定递归,并且我正在尝试使用递归来反转字符串 一开始我不知道怎么做,尝试了很多不同的方法,但我看到了字符串反转的代码示例,但对我来说没有任何意义,所以我自己做了一个,但不太确定它,我只是征求意见,它干净实用吗??

谢谢

【问题讨论】:

  • 欢迎来到 Stack Overflow!如果你想帮助改进工作代码,你应该在CodeReview.SE 上发布。如果您决定这样做,请在此处删除问题。
  • 如果您不是要求审查工作代码,那么请解释它是如何不当行为的。还多次说“我尝试了很多。我读了很多。”并不真正意味着您正在展示研究成果,只要您不展示您尝试过的内容或列出并讨论您阅读的内容。所以你的问题缺乏细节和重点。
  • “我只是征求意见”考虑 Nathan 的评论。 Stack Overflow 不是为发表意见而设计的。

标签: c++ string function recursion


【解决方案1】:

任何人都可以一次反转一个字符串,但更酷的是反转字符串的每三分之一并交换外部三分之一。这减少了筹码深度,并在竞争中造成混乱。请注意,每个字符的最大递归堆栈深度为 N,而这是 N 的立方根。

#include <iostream>
#include <string>
using namespace std;

void ReverseRegion(string &s, int start, int sz)
{
  // regions < 2 need no action
  if (sz == 2) {
    char tmp = s[start];
    s[start] = s[start+1];
    s[start+1] = tmp;
  } else if (sz > 2) {
    int s3 = sz/3;
    ReverseRegion(s, start, s3);
    string tmp = s.substr(0,start) + s.substr(start+sz-s3,s3) + s.substr(start+s3, sz-2*s3) + s.substr(start,s3) + s.substr(start+sz);
    // cout << "do: " << tmp << "\n";
    s = tmp;
    ReverseRegion(s, start+s3, sz-2*s3);
    ReverseRegion(s, start, s3);
  }
}


void ReverseString(string &S)
{
  ReverseRegion(S, 0, S.size());
}

int main()
{
    cout << "enter a string to reverse" << endl;
    string s;
    getline(cin, s);
    cout << "Before Reversing" << endl;
    cout << s << endl;
    ReverseString(s);
    cout << "After Reversing" << endl;
    cout << s << endl;
    return 0;
}

【讨论】:

    【解决方案2】:

    它是……功能吗??

    如果你所说的“功能性”是指“它有效”,那么你告诉我。

    如果您的意思是“函数式”编程风格中的“函数式”,那么不,它不是。在函数式风格中,您不会就地修改参数,而是返回一个新值。同样依赖全局状态(即静态对象)是非常反功能的。

    这是一个例子:

    std::string
    ReverseString(std::string_view sv)
    {
        if (sv.empty())
            return "";
        std::string_view x  = sv.substr(0, 1)
        std::string_view xs = sv.substr(1);
        return ReverseString(xs) + x;
    }
    
    // usage
    s = ReverseString(s);
    

    将来,如果将Pattern matching 引入该语言,那么它可能会写成这样:

    std::string
    ReverseString(std::string_view sv)
    {
        inspect(sv) {
            "":      return "";
            [x:xs]:  return ReverseString(xs) + x;
        }
    }
    

    但是,目前的提案不建议引入对这样的匹配范围的支持,因此这是高度理论化的。

    【讨论】:

    • 注意 OP,如果你不控制字符串长度,这在生产级代码中是一个非常糟糕的主意。即使是你,在没有尾调用优化的情况下,迭代版本也会快如闪电。
    • 扩展@TanveerBadar 的评论:另请注意,在生产代码中实现任何具有递归的线性算法都是一个坏主意,除非您使用保证尾调用优化的函数式语言(即将函数转换为背后的迭代版本)
    【解决方案3】:

    您的带有静态变量的函数只能调用一次,因为在其递归调用之后,静态变量start 将不等于 0,因为它是必需的。所以这个函数不是“函数式”的。

    这是一个演示程序,展示了如何使用静态变量和不使用静态变量来编写函数。

    #include <iostream>
    #include <string>
    #include <utility>
    
    void ReverseString1( std::string &s )
    {
        static std::string::size_type i = 0;
    
        if ( not ( s.size() - 2 * i < 2 ) )
        {
            std::swap( s[i], s[s.size() - i - 1] );
    
            ++i;
            ReverseString1( s );
            --i;
        }
    }
    
    void ReverseString2( std::string &s, std::string::size_type pos = 0 )
    {
        if ( not ( s.size() - 2 * pos < 2 ) )
        {
            std::swap( s[pos], s[s.size() - pos - 1] );
            ReverseString2( s, pos + 1 );
        }
    }
    
    int main() 
    {
        std::string s( "Hello World!" );
    
        std::cout << s << '\n';
    
        ReverseString1( s );
    
        std::cout << s << '\n';
    
        ReverseString2( s );
    
        std::cout << s << '\n';
    
        return 0;
    }
    

    程序输出是

    Hello World!
    !dlroW olleH
    Hello World!
    

    【讨论】:

      【解决方案4】:

      局部静态变量很危险。因为它们的状态将在函数调用之间保持不变。在我的方法中,我使用slen 作为字符串的长度,使用currentIndex 作为字符串的最后一个交换索引。由于交换到字符串的中间就足够了,完成情况是(currentIndex == slen/2)。 我还添加了一些测试用例作为示例。(偶数长度,奇数长度,零情况和回文)

      #include <iostream>
      #include <string>
      using namespace std;
      void ReverseString(string &S, int currentIndex, int slen)
      {
          if (slen / 2 == currentIndex) return;
      
          swap(S[currentIndex], S[slen - 1 - currentIndex]);
          currentIndex++;
      
          ReverseString(S, currentIndex, slen);
      }
      
      void testReverseString() {
          string s = ""; 
          ReverseString(s, 0, s.length());
          assert(s == "");
      
          s = "ahmet"; 
          ReverseString(s, 0, s.length());
          assert(s == "temha");
      
          s = "ahaha"; 
          ReverseString(s, 0, s.length());
          assert(s == "ahaha");
      
          s = "haha"; 
          ReverseString(s, 0, s.length());
          assert(s == "ahah");
      }
      
      int main()
      {
          testReverseString();
          return 0;
      }
      

      【讨论】:

        【解决方案5】:

        在递归函数中使用函数局部 static 变量是个坏主意。递归函数应将其所有状态作为输入参数。

        这是一个简化版本,将逻辑分为两个功能。

        void ReverseString(string &S, int start, int end)
        {
           if ( start < end )
           {
              swap(S[start], S[end - 1]);
              ReverseString(S, start+1, end - 1);
           }
        }
        
        void ReverseString(string &S)
        {
           ReverseString(S, 0, S.size());
        }
        

        大多数时候,更高级别的函数只会调用第二个函数。如果只需要反转字符串的子集,则可以从更高级别的函数调用第一个函数。

        这是一个示例程序

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        void ReverseString(string &S, int start, int end)
        {
           if ( start < end )
           {
              swap(S[start], S[end - 1]);
              ReverseString(S, start+1, end - 1);
           }
        }
        
        void ReverseString(string &S)
        {
           ReverseString(S, 0, S.size());
        }
        
        int main()
        {
            string s = "The string to reverse" ;
        
            cout << "Before Reversing" << endl;
            cout << s << endl;
        
            ReverseString(s);
            cout << "After Reversing" << endl;
            cout << s << endl;
        
            ReverseString(s, 0, 7);
            cout << "After Reversing a subset" << endl;
            cout << s << endl;
            return 0;
        }
        

        及其输出

        Before Reversing
        The string to reverse
        After Reversing
        esrever ot gnirts ehT
        After Reversing a subset
        reverse ot gnirts ehT
        

        https://ideone.com/9nMlsP查看它。

        【讨论】:

        • 谢谢,所以现在我明白了我不应该在递归函数中使用堆栈变量,但我不明白为什么,你能再澄清一点吗?问候
        猜你喜欢
        • 1970-01-01
        • 2015-09-27
        • 1970-01-01
        • 2012-09-24
        • 2013-03-20
        • 1970-01-01
        • 2017-07-20
        • 2012-04-01
        相关资源
        最近更新 更多