我对使用堆栈将递归函数(尾部和非尾部)转换为迭代函数的一般方法的含义有很大的困惑.我举了这个简单的例子来说明这一点:

int sigma1(int n) {
  if(n == 0) 
    return 0;
  else 
    return n + sigma1(n-1);
}

以下哪个版本正确反映了该方法:

版本1

int sigma1_iter(int n) {
  stack* s = create_stack(n);
  push(s, n);
  int res = 0;
  while(!is_empty(s)) {
    int elm = pop(s);
    if(elm == 0)
      return res;
    else {
      res += n;
      push(s, n-1);
    }
  }
}

版本2

int sigma1_iter(int n) {
  stack* s = create_stack(n);
  push(s, n);
  int res = 0;
  while(!is_empty(s)) {
    if (n > 0) {
      n--;
      push(s, n);
    }
    else {
      int elm = pop(s);
      if(elm == 0)
        return res;
      else {
        res += n;
    }
  }
}

此外,在带有累加器的西格玛的尾递归版本的情况下:

int sigma2(int n, int res) {
  if (n == 0) 
    return res;
  else
    return sigma2(n-1, n+res);
}

人们将如何转换它呢?我正在考虑为Res创建第二个堆栈,但这可能会如何工作?

推荐答案

当你问问题的时候,一定要记minimal, reproducible example分.这有助于每个人理解问题并生成和测试潜在的答案.

如果您创建了这样一个示例,很明显,迭代解决方案的版本1永远不会返回,而版本2总是返回0,这两个版本都不是正确的候选.

递归可能很有挑战性,但我认为在这个例子中,我们可以通过一种通用的方法将递归函数转换为迭代函数.

让我们看一下sigma1的递归实现,

int sigma1(int n) {
    if (n == 0)
        return 0;
    else
        return n + sigma1(n - 1);
}

在幕后,调用该函数会 for each 递归调用生成一个新的堆栈框架(假设编译器没有对其进行优化).这些调用帧中的每一个将具有该帧的自变量n的值,该值将在递归的每一级递减1.我们可以用同样的方式考虑迭代版本,将值n存储在显式std::stack<int>上,而不是由调用堆栈自动处理它.

当递归终止并且调用帧展开时,本地值n被加到前一次调用的结果中,从而创建要返回的新结果.同样,我们可以从显式std::stack<int>中弹出值,并将它们添加到迭代版本中的运行总计中.

该函数将如下所示,

int sigma1_stack(int n) {
    std::stack<int> stack;
    for (int i = n; i > 0; --i)
        stack.push(i);

    int res{};
    while (not stack.empty()) {
        res += stack.top();
        stack.pop();
    }

    return res;
}

现在,在这个特定的场景中,您可以将两个循环简化为没有堆栈的单个迭代循环.当然,情况不会总是这样.

int sigma1_iter(int n) {
    int res{};
    for (int i = n; i > 0; --i)
        res += i;
    return res;
}

我采用了您发布的代码,并将其更改为使用std::stack<int>进行编译,因为您没有发布完整的示例.我还为sigma1sigma2以及相应的输出添加了正确的迭代版本.希望这能对你有所帮助.

示例代码

#include <iostream>
#include <stack>

using std::cout, std::endl;

int sigma1(int n) {
    if (n == 0)
        return 0;
    else
        return n + sigma1(n - 1);
}

int sigma1_iter_v1(int n) {
    std::stack<int> stack;
    stack.push(n);

    int res = 0;
    while (not stack.empty()) {
        int elm = stack.top();
        stack.pop();
        if (elm == 0)
            return res;
        else {
            res += n;
            stack.push(n - 1);
        }
    }

    return res;
}

int sigma1_iter_v2(int n) {
    std::stack<int> stack;
    stack.push(n);

    int res = 0;
    while (not stack.empty()) {
        if (n > 0) {
            n--;
            stack.push(n);
        } else {
            int elm = stack.top();
            stack.pop();
            if(elm == 0)
                return res;
            else {
                res += n;
            }
        }
    }

    return res;
}

int sigma1_iter(int n) {
    int res{};
    for (int i = n; i > 0; --i)
        res += i;
    return res;
}

int sigma1_stack(int n) {
    std::stack<int> stack;
    for (int i = n; i > 0; --i)
        stack.push(i);

    int res{};
    while (not stack.empty()) {
        res += stack.top();
        stack.pop();
    }

    return res;
}

int sigma2(int n, int res = 0) {
    if (n == 0)
        return res;
    else
        return sigma2(n - 1, n + res);
}

int sigma2_iter(int n) {
    int res{};
    for (int i = n; i > 0; --i)
        res += i;
    return res;
}

int main(int argc, const char *argv[]) {
    cout << "sigma1         : " << sigma1(20) << endl;
    cout << "sigma1_iter   : " << sigma1_iter(20) << endl;
    cout << "sigma1_stack  : " << sigma1_stack(20) << endl;
    // This never returns
    // cout << "sigma1_iter_v1 : " << sigma1_iter_v1(20) << endl;
    cout << "sigma1_iter_v1 : " << "never returns" << endl;
    cout << "sigma1_iter_v2 : " << sigma1_iter_v2(20) << endl;

    cout << endl;
    cout << "sigma2         : " << sigma2(20) << endl;
    cout << "sigma2_iter    : " << sigma2_iter(20) << endl;
    return 0;
}

输出

sigma1         : 210
sigma1_iter    : 210
sigma1_stack   : 210
sigma1_iter_v1 : never returns
sigma1_iter_v2 : 0

sigma2         : 210
sigma2_iter    : 210

C++相关问答推荐

自定义malloc实现上奇怪的操作系统依赖行为

为什么静态说明符为内联函数生成外部定义?

为什么在C中设置文件的位置并写入文件,填充空字符?

C:scanf(%d&q;,...)输入只有一个减号

X86/x64上的SIGSEGV,由于原始内存访问和C中的DS寄存器之间的冲突,在Linux上用TCC编译为JIT引擎

如何在C中使printf不刷新标准输出?

CSAPP微型shell 实验室:卡在sigprocmask

GTK3按钮信号错误

C语言中的外部关键字

为什么realloc函数在此代码中修改变量?

为什么我无法访问C语言中的文件

与外部SPI闪存通信时是否应禁用中断?

C编译和运行

浮点正零何时不全为零?

在我的函数中实现va_arg的问题

我可以使用Windows SDK';s IN6_IS_ADDR_LOOPBACK等,尽管没有文档?

在分配内存后使用指针是未定义的行为吗?

保存有符号整数结果的变量是否会溢出(后增量的副作用),并且此后从未在任何表达式中使用过它,是否会导致 UB?

可以从指针数组中的值初始化指针吗?

K&R 练习 1-24