编写递归功能将给定的字符串转换为代表的数字

发布于 2025-02-12 10:03:58 字数 504 浏览 1 评论 0原文

在此代码中,我想通过使用递归函数字符串 转换为整数,但它在Negetive中给出了输出。

#include <iostream>

using namespace std;

int convert1(string s) {
    if (s.length() == 1) {
        int i = s[0] - '0';
        return i;
    }

    int so = convert1(s.substr(0, s.length() - 1));
    int num = s[s.length()] - '0';
    int ans = so * 10 + num;
    return (int)ans;
}

int main()
{
    string s;
    cin >> s;
    cout << convert1(s) << endl;
}

In this code, I want to convert the string into integer by using recursive function but it is giving output in negetive.

#include <iostream>

using namespace std;

int convert1(string s) {
    if (s.length() == 1) {
        int i = s[0] - '0';
        return i;
    }

    int so = convert1(s.substr(0, s.length() - 1));
    int num = s[s.length()] - '0';
    int ans = so * 10 + num;
    return (int)ans;
}

int main()
{
    string s;
    cin >> s;
    cout << convert1(s) << endl;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

迷荒 2025-02-19 10:03:58

如果您尝试打印s [s.length()]每次调用convert1时,您会发现它是打印0。然后,您从中减去'0''的值(48)。

假设我们尝试转换“ 12”

长度是不是 1,因此我们调用convert1 on “ 1”

是长度1的,所以我们返回1

因此,如果so1,并且s [s.length()] is 0,则num-48so * 10 + num评估1 * 10-48-38

对于两个数字的输入,您将始终看到第一个数字时间10,减48。对于三位数的数字,您会看到(第一个数字 * 10负48)乘以10个减去48。此模式继续进行。如果第一个数字足够大,则乘以10个减去48的数字会产生正数。如果足够大,正面数字会继续通过递归传播。如果它们的变小大于48,那么一旦结果为负,它将变得更大,因为负数会更递归呼叫。


与您做事的方式相反,您可以在Convert1中使用累加器参数。

int convert1(string s, int acc=0) {
    if (s.length() == 1) {
        return acc * 10 + (s[0] - '0');
    }

    return convert1(s.substr(1, s.length() - 1), 
                    acc * 10 + (s[0] - '0'));
}

每次递归调用该函数时,我们都会通过将其乘以10并添加第一个数字的值来更新累加器,并通过取下字符串的“尾巴”来更新字符串。

或者更好,但与原始内容相距甚远,我们在字符串为空时返回累加器。

int convert1(string s, int acc=0) {
    if (s.empty()) return acc;

    return convert1(s.substr(1, s.length() - 1), 
                    acc * 10 + (s[0] - '0'));
}

此尾部递归的好处是,此功能(如果由像样的编译器正确优化)可以在恒定的堆栈空间中运行。尽管在这种情况下是学术性的,因为int类型将在堆栈溢出之前溢出。

If you try printing s[s.length()] each time you call convert1, you'll see that it's printing 0. And then you're subtracting the value of '0' (48) from that.

Let's say we try to convert "12".

The length is not 1, so we call convert1 on "1".

That is of length 1, so we return 1.

So, if so is 1, and s[s.length()] is 0, then num is -48 and so * 10 + num evaluates to 1 * 10 - 48 which is -38.

For a two digit number input, you will always see the first digit times 10, minus 48. For a three digit number, you'll see (the first digit * ten minus 48) times 10 minus 48. This pattern continues on. If the first digit is large enough, it times 10 minus 48 creates a positive number. If that's large enough, positive numbers continue to propagate through the recursion. If they ever get smaller than 48, then once the result is negative, it will just get larger as a negative number the more recursive calls are made.


As opposed to the way you have done things, you can employ an accumulator parameter in convert1.

int convert1(string s, int acc=0) {
    if (s.length() == 1) {
        return acc * 10 + (s[0] - '0');
    }

    return convert1(s.substr(1, s.length() - 1), 
                    acc * 10 + (s[0] - '0'));
}

Each time the function is recursively called, we update the accumulator by multiplying it by ten and adding the value of the first digit, and update the string by taking the "tail" of the string.

Or better, but a little bit further from your original, we return the accumulator when the string is empty.

int convert1(string s, int acc=0) {
    if (s.empty()) return acc;

    return convert1(s.substr(1, s.length() - 1), 
                    acc * 10 + (s[0] - '0'));
}

The benefit of this tail recursion is that this function (if properly optimized by a decent compiler) can run in constant stack space. Though it's academic in this case as the int type will overflow before a stack overflow.

梦里寻她 2025-02-19 10:03:58

正常的递归方式

int main()
{
    string s;
    cin >> s;
    cout << strtol(s.c_str(), nullptr, 10) << endl;
}

由于某种原因,

int convert_string(const char* string, int length){
   if (length == 0) {
      return 0;
   }

   int output = string[0] - '0';
   output *= pow(10, length - 1);
   output += convert_string(&string[1], --length);
   return output;
}

int main()
{
    std::string s;
    std::cin >> s;
    std::cout << convert_string(s.c_str(), s.length()) << std::endl;
}

The normal way

int main()
{
    string s;
    cin >> s;
    cout << strtol(s.c_str(), nullptr, 10) << endl;
}

Recursive for some reason

int convert_string(const char* string, int length){
   if (length == 0) {
      return 0;
   }

   int output = string[0] - '0';
   output *= pow(10, length - 1);
   output += convert_string(&string[1], --length);
   return output;
}

int main()
{
    std::string s;
    std::cin >> s;
    std::cout << convert_string(s.c_str(), s.length()) << std::endl;
}
皇甫轩 2025-02-19 10:03:58

计算最后一个数字时,

int num = s[s.length()] - '0';

您将对字符串的范围访问。将其更改为

int num = s[s.length() - 1] - '0';

,您的代码有效。效率低下。

您可以在每次递归中创建一个新的子字符串。有两种改进的方法:

  1. 制作一个辅助功能,可以使字符串的副本复制,然后调用递归功能。递归功能采用std :: String&amp; S然后可以使用s.pop_back()。作为一方面,助手功能可以轻松处理以“ - ”开头的字符串。无论如何,你应该有这个助手。

  2. 将参数更改为std :: string_view然后,它只是字符串中的引用,子字符串只会收缩该引用。字符串从未复制。实际上,这是唯一的变化,只需更改std :: String std :: String_view and done。

我认为std :: string_view是要走的方法,但需要C ++ 17。将其与助手相结合,以处理“ - ”。

接下来的事情是s [s.length()-1],可以写为s.back()。可以保管一些打字,没有逐个错误的风险。

那么您终止递归的状况过于复杂。您可以

if (s.empty()) return 0;

以额外的递归电话为代替编写。另一方面,这允许解析“”。不确定哪种方式更快,更简单的测试可能会平衡额外的递归调用。我喜欢它,因为它不会重复数字转换代码,而是键入的较少。

接下来,如果您使用std :: String_view,则s.substr(0,s.length() - 1)可以更改为使用s.remove_suffix(1);

所有这些结合在一起给了您:

int convert1(std::string_view s) {
    if (s.empty()) return 0;

    int num = s.back() - '0';
    s.remove_suffix(1);
    int so = convert1(s)
    int ans = so * 10 + num;
    return ans;
}

要考虑的还有一件事,但这是您算法的完整重写:

您的递归不是尾部递归。这消耗了与字符串长度成比例的堆栈空间。在解析“ int”时,这不是一个问题,这将是更大问题的问题。或者,如果有人进入一个非常大的数字“ 14654645656348756 ... 65464”。

要解决这个问题,您必须扭转算法。与其像迭代性那样从前面切碎数字,将它们从前面带走:

int iterative_convert(const std::string &s) {
    int acc = 0;
    for (const auto c : s) acc = acc * 10 + (c - '0');
    return acc;
}

要使此递归您必须将中间结果传递给累积递归。因此,它变成了:

int convert1(std::string_view s, int acc = 0) {
    if (s.empty()) return acc;
    acc = 10 * acc + (s.front() - '0');
    return convert1(s.substr(1), acc);
}

不确定您是如何提出背对面算法的,但是前后会产生更好的代码。

注意:缺少需要C ++ 17,或者如果您愿意,也可以使用std :: String :: const_iterator(需要将s.cend()作为额外参数)或良好的旧const char *p = s.c_str();

When you compute the last digit

int num = s[s.length()] - '0';

you have an out-of-bounds access to the string. Changing that to

int num = s[s.length() - 1] - '0';

and your code works. Inefficiently.

You create a new substring in every recursion. There are 2 ways to improve on that:

  1. make a helper function that makes a copy of the string and then calls the recursive function. The recursive function takes a std::string & s and then can use s.pop_back(). As a side benefit the helper function can easily deal with strings starting with '-'. You should have this helper anyway.

  2. change the argument to std::string_view then it's just a reference into the string and substring will just shrink that reference. The string is never copied. That's actually the only change, just change std::string to std::string_view and done.

I think the std::string_view is the way to go but requires c++17. Combine that with the helper for dealing with '-'.

The next thing is s[s.length() - 1], which can be written as s.back(). Safes a bit of typing and no risk of an off-by-one error.

Then your condition for terminating the recursion is overly complex. You can write

if (s.empty()) return 0;

instead at the cost of an extra recursion call. On the other hand this allows parsing "". Not sure which way is faster, the simpler test might balance the extra recursive call. I like it because it doesn't repeat the digit conversion code and is less to type.

Next, if you are using std::string_view, then the s.substr(0, s.length() - 1) expression can be changed to using s.remove_suffix(1);.

All combined this gives you:

int convert1(std::string_view s) {
    if (s.empty()) return 0;

    int num = s.back() - '0';
    s.remove_suffix(1);
    int so = convert1(s)
    int ans = so * 10 + num;
    return ans;
}

One more thing to consider but it's a complete rewrite of your algorithm:

Your recursion isn't tail recursive. This uses up stack space proportional to the length of the string. While for parsing an "int" this shouldn't be a problem it will be a problem for larger problems. Or if someone just enters a really big number "14654645656348756...65464".

To solve this you have to turn your algorithm around. Instead of chopping digits of at the end take them from the front like you would doing this iteratively:

int iterative_convert(const std::string &s) {
    int acc = 0;
    for (const auto c : s) acc = acc * 10 + (c - '0');
    return acc;
}

To make this recursive you have to pass the intermediate result as accumulator down the recursion. So it becomes this:

int convert1(std::string_view s, int acc = 0) {
    if (s.empty()) return acc;
    acc = 10 * acc + (s.front() - '0');
    return convert1(s.substr(1), acc);
}

Not sure how you came up with the back to front algorithm but front to back results in better code.

Note: Short of needing c++17, or if you prefer, you can also use std::string::const_iterator (requires passing s.cend() as extra argument) or good old const char *p = s.c_str();

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文