【问题标题】:Confused about basic recursion in C++对 C++ 中的基本递归感到困惑
【发布时间】:2017-10-17 01:39:51
【问题描述】:
void print(int p)
{
    if (p==0)
    {
       return;
    }

    print(p-1);
    cout<<p;
    return;
}

这是一个基本函数,它从你输入的任何数字开始计数,所以如果你发送 6,它会打印 123456。

我已经盯着这个看了半个小时,我不知道它是如何工作的。为什么递归调用 print(p-1); 不会导致声明 if(p==0) 并在到达 cout&lt;&lt;p 之前返回?

cout&lt;&lt;p 是如何被访问的?我完全理解将 cout&lt;&lt;p; 放在递归调用之前并像 654321 一样打印时它是如何工作的。

【问题讨论】:

  • 如果你切换coutprint(p-1),那么情况和现在一样,真的。为了到达底部的return;,它需要经过print(p-1),否则它永远不会完成。这里也是一样。
  • 确实在 if 处返回。但它确实返回到下一个更高的 print 调用,而不是 main。然后这个打印调用在它等待的地方继续。
  • 如果您将 print(p-1) 扩展为完整功能并查看会发生什么,您可能会发现它很有帮助

标签: c++ recursion


【解决方案1】:

您似乎认为return 总是返回到 main(或您调用 print 的任何地方),和/或递归调用会替换自身。这不是真的。

如果您调用print(6),它当然以if 开头,然后以print(p-1); 继续。此时,开始调用print(5),但之前的print(6) 仍然存在并等待print(5) 完成。

print(5) 也在做同样的事情,导致调用print(4),而print(5) 等待print(4)。等等……

在某些时候,print(1) 调用 print(0)if 在调用另一个 print 之前立即结束函数。现在,当print(0) 结束时,print(1) 继续其代码,即cout。之后,print(1) 结束,导致print(2) 继续工作,依此类推……

最后,当print(5) 结束时,最顶层的嵌套调用(print(6))继续,在其中的cout 之后,它可以返回到 main(或从其他地方调用它) .

【讨论】:

    【解决方案2】:

    递归调用的工作方式类似于stack。如果我们尝试通过将您的调用扩展到print 并将p 替换为它的值来可视化这一点,我们会得到如下结果:

    print(4)
    {
        if (4==0)
            return;
        print(3)
        {
            if (3==0)
                return;
            print(2)
            {
                if (2==0)
                    return;
                print(1)
                {
                    if (1==0)
                        return;
                    print(0)
                    {
                        if (0==0)
                            return;  // This return is called
                        print(-1)    // Line skipped
                        cout<<0;     // Line skipped
                        return;      // Line skipped
                    }
                    cout<<1;
                    return;
                }
                cout<<2;
                return;
            }
            cout<<3;
            return;
        }
        cout<<4;
        return;
    }
    

    注意return 语句只留下了当前版本的print,这是堆栈上的当前函数。这意味着我们最终会一直返回创建的堆栈。

    【讨论】:

      【解决方案3】:

      虽然还有其他答案,而且都是正确的,但我会尝试解释更多的事情。在 C++ 中使用这种类型的递归仅用于学术目的,但是像 LISP 这样的语言没有循环并且依赖于递归。

      在递归函数中,需要考虑三个关键要素:

      1. 必须有一个条件,当满足时结束递归。
      2. 函数中必须有一个点(可能不止一个),该点是函数调用自身的地方。当它这样做时,调用应该“更接近” end 条件。例如,倒计时,调用会减少参数。
      3. 当函数在此过程中执行某些操作时,该部分工作可能会在调用递归之前或之后进行。根据调用的完成方式,我们会得到一个递归策略或其他策略。您可能听说过深度优先搜索广度优先搜索

      例如。如果你这样做:

      if(p<=0)
        return;
      
      print(p-1);
      cout << p;
      

      您的打印顺序与以下相反:

      if(p<=0)
        return;
      
      cout << p;
      print(p-1);
      

      如果您首先递归然后打印,则您使用的是深度优先策略。递归将一直持续到满足 end 条件,然后打印完成。所以第一个打印将是更接近 end 条件的打印;然后是下一个;等等。你可以弄清楚在另一种情况下会发生什么。

      还值得注意的是,每次递归时,某种堆栈(顺便说一下,以 LIFO 顺序工作)必须保持所有先前递归的状态。当您的问题需要大量递归时,您最终可能会使用所有可用资源。所以在像 C++ 这样的语言中,最好使用一个只保留一个 state 的循环。

      但是,当您必须解决某些问题时,递归会派上用场。想到的一个是解析表达式,其中某些位与某些其他位具有不同的优先级。喜欢1+2*3。在这种情况下,应首先评估2*3,然后将结果添加1+6 以产生7。这称为递归下降解析器

      【讨论】:

      • 顺便说一句,这似乎是Code Review 的问题。
      【解决方案4】:

      想想print(1):它通过if语句,然后调用print(0);返回,然后通过调用cout 继续调用print(1)

      【讨论】:

        猜你喜欢
        • 2013-05-15
        • 2020-08-14
        • 1970-01-01
        • 2015-09-09
        • 2014-07-28
        • 1970-01-01
        • 2021-12-15
        • 2018-01-11
        • 1970-01-01
        相关资源
        最近更新 更多