【问题标题】:How do I calculate the sum of all elements before "x" element recursively?如何递归计算“x”元素之前的所有元素的总和?
【发布时间】:2018-10-08 07:37:24
【问题描述】:

我正在编写一个程序,以递归方式计算 x 元素之前的所有元素的摘要。它总是返回 0。请你帮帮我 - 当我在 main 中调用 SumBeforeX 函数时,你能解释一下它是如何有序地工作的吗? 这是我的代码和

#include <stdio.h>
int SumBeforeX(int a[], int n, int x)
{
    int i = 0;
    static int s = 0;
    if (n == 0)
        return 0;
    if (a[i] == x)
        s+=SumBeforeX(a, i -1, x) +a[i-1];
    return s;

}
void main()
{
    int a[] = {2,6,13,17,47,8};
    printf("%d",SumBeforeX(a,6,13));
    _getch();
}

【问题讨论】:

  • 不管某些编译器允许什么,main() 的所有有效签名都具有int 的返回类型
  • 我建议您花一些时间来learn debugging in general,更具体地说,如何使用调试器逐行执行您的代码。
  • i-1 等于 - 1
  • 您需要重新考虑如何使用ni。它们应该以某种方式连接。
  • 递归调用中的static 违背了这个概念。在您的情况下,也使其成为 一次性使用功能

标签: c


【解决方案1】:

user3629249 给出的答案已经解释了为什么 SumBeforeX() 总是返回 0 并且 Felix Palmen 的答案谈到了解决问题的递归和非递归方法.这个答案只是为了让您了解使用尾递归计算"x"元素之前的总和:

int SumBeforeX(const int *a, int n, int x, int sum)
{
    if (n == 0) return 0;
    if (x == *a) return sum;

    return SumBeforeX(a + 1, n - 1, x, sum + *a);
}

在这里您可以看到我在函数SumBeforeX 中又添加了一个参数sum,并将元素的总和累加到其中。你可以这样称呼它:

printf("%d\n", SumBeforeX(a, 6, 13, 0));

看来你正在学习递归,熟悉尾递归也不错。


如果函数返回后除了返回其值之外没有任何事情可做,则称函数调用是尾递归的。尾递归函数可以很容易地转换为迭代函数,因此编译器还可以通过消除递归来优化此类函数的代码,这意味着尾递归调用在恒定堆栈空间中运行,即它们不需要创建新堆栈递归调用自己时的帧。检查以下以获得更好的想法:
1) Tail Recursion
2)How exactly does tail recursion work?

【讨论】:

  • 即使x 不是a[] 的元素,这也会定义函数。可能是件好事,但我很不确定这是否是 OP 原始代码的意图;)
  • 顺便说一句,虽然我不确定在这里打开这个罐子是个好主意,如果你这样做,我认为技术解释 为什么 尾递归可能更可取也不错。它可以归结为帮助编译器消除递归,因为您不希望它出现在生成的机器代码中......
  • @FelixPalmen:我同意你的观点,但我是这样看待的——OP 的代码将数组中的元素数量作为递归函数的参数传递,并对其进行一些检查,所以我相信,OP 想对数组元素的数量进行一些验证。谢谢,正如你所建议的,我添加了一个关于尾递归的简要说明和一些参考资料。可能会有所帮助。
  • 它提供了必要的上下文!至于这个 n 参数,我的印象是 OP 试图将它用于终止条件,在没有真正理解它们的情况下看到了类似的示例。但当然你的解释也可能是正确的——我们不能从发布的代码中确定
【解决方案2】:

我猜预期的解决方案有点像这样:

int sumBeforeX(const int *a, int x)
{
    if (x == *a) return 0;           // termination condition
    return *a + sumBeforeX(a+1, x);  // recursive step
}

为了理解这一点,请注意,数组不能在 C 中传递,因此将它们在函数参数中的类型调整为相应的指针类型,并改为传递指向第一个元素的指针。我在上面的sn-p中明确了这个,直接写了指针类型。

const 只是一点点改进:该函数永远不会通过这个指针修改你的数组,所以也要明确这一点。

还要注意这个实现如何从不分配任何东西。这是函数式编程的典型属性,递归尤其适用于函数式编程。函数调用没有副作用


旁注:不要在现实世界的 C 代码中递归地编写它,如果幸运的话,编译器会优化递归(在这种简单的情况下,一个好的编译器应该这样做),但这不应该依赖,如果递归仍然存在,性能通常会受到影响,如果你的递归变得非常深,你可能会遇到堆栈溢出的风险。因此,为了完整起见,真实世界的代码应该看起来更像这样:

int sumBeforeX(const int *a, int x)
{
    int sum = 0;                  // accumulator to replace recursion
    while (*a != x) sum += *a++;
    return sum;
}

【讨论】:

  • 我认为如果 (x==*a) ,它必须返回 a[0]。你能解释一下我调用这个函数时它是如何工作的吗?
  • @user3121023 这不是规范的一部分,所以我不这么认为......看起来更像n 应该是数组中的当前索引,这真的不需要.这个函数的合约要求x实际上是a的成员(就像puts()的合约要求参数以0字节结尾)
  • @F.Wu 如果您要在此处返回 a[0](或简单的 *a 相同),您将总结所有元素包括 x。根据你的问题,这不是你想要的?
  • @user3121023 当然可以,但是根据文本中的规范是否有意义?我会说不,这只是做一些类似于 OP 可能见过的其他递归函数的混乱尝试。
【解决方案3】:

关于:当我在 main 中调用 SumBeforeX 函数时,你能解释一下它是如何有序工作的吗?

#include <stdio.h>
int SumBeforeX(int a[], int n, int x)
{
    int i = 0;
    static int s = 0;
    if (n == 0)
        return 0;
    if (a[i] == x)
        s+=SumBeforeX(a, i -1, x) +a[i-1];
    return s;

}
void main()
{
    int a[] = {2,6,13,17,47,8};
    printf("%d",SumBeforeX(a,6,13));
    _getch();
}

第一次执行SumBeforex()

  1. n 的值为 6,所以不能提前退出
  2. s的值为0
  3. i 的值为 0,所以a[i] 的值为 2
  4. x 的值为 13
  5. 所以`if(a[i] == x) 是假的
  6. 因此,不会发生递归
  7. 所以 0 返回到 'main()`
  8. 所以打印的值为0

【讨论】:

    猜你喜欢
    • 2019-09-26
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 2020-07-17
    • 2013-12-23
    相关资源
    最近更新 更多