递归过程中到底发生了什么?

发布于 2024-09-15 01:32:15 字数 27 浏览 5 评论 0原文

谁能告诉我递归程序中到底发生了什么..?

Can anyone tell me what exactly happens in a Recursion Program.. ??

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

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

发布评论

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

评论(6

抹茶夏天i‖ 2024-09-22 01:32:16

与迭代相反,递归是一种算法或函数设计,其中函数调用自身。这可以使函数更容易理解,但也会使其变慢,因为必须创建一个新的堆栈。此外,堆栈内存使用量将随着每次调用而线性增加。

另一方面,迭代在一个函数中循环遍历所有内容,将时间复杂度保持在 O(n),并且空间复杂度固定并且不会随着每次迭代而增加。

例如,考虑一个将连续数字相加的函数。请注意,有一个公式可以通过一个简单的计算(时间复杂度为 O(1))来完成此操作,但我们仅以此为例。

递归函数可能如下所示:

long consecutive(long a) {
    return a > 1 ? a + consecutive(a - 1) : a + 1;
}

通过此递归调用,您可能很快就会耗尽堆栈内存。然而,迭代模型对此更好:

long consecutive(long a) {
    long result = 0, i;
    for (i = 1; i <= a; i++) result += i;
    return result;
}

Recursion, as opposed to iteration, is an algorithm or function design where a function calls itself. This could make a function a lot easier to understand, but makes it slower because a new stack must be created. Also, the stack memory usage will increase linearly with each call.

Iteration, on the other hand, loops through all in one function, keeping the time complexity at O(n), and the space complexity fixed and not increasing with each iteration.

For example, consider a function that adds consecutive numbers. Note that there's a formula to do this in one simple calculation (with O(1) time complexity) but let's just do this as an example.

The recursive function might look like this:

long consecutive(long a) {
    return a > 1 ? a + consecutive(a - 1) : a + 1;
}

You could run out of stack memory very quickly with this recursive calling. However, the iterative model is better for this:

long consecutive(long a) {
    long result = 0, i;
    for (i = 1; i <= a; i++) result += i;
    return result;
}
这样的小城市 2024-09-22 01:32:16

递归程序可能如下所示:

def f(i):
    if i > 100:
        return 100
    else:
        return f(i+1)

print f(7)

(python)

问题是,会发生什么?答案是最好从函数的角度来思考这个问题并尝试将它们写出来。

因此,我们有语句 print f(7)。为了找出它的计算结果,我们通过该函数运行 7 并发现它的计算结果为 f(8)。好的,很好,但是它的评估结果是什么? f(9)。我故意在这里有一个 exit 子句 - 最终 i 在循环中将等于 100,这将是返回的值。会发生什么:

  • f(7) = ?
  • f(7) = f(8) = ?
  • f(7) = f(8) = f(9 ) = ?
  • f(7) = f(8) = f(9) = f(10) = ?
  • ...
  • f( 7) = f(8) = ... = 100
  • 所以 f(7) 是 100

这是一个相当简单的示例,但确实很好地演示了递归。

A recursive program might look like this:

def f(i):
    if i > 100:
        return 100
    else:
        return f(i+1)

print f(7)

(python)

The question is, what happens? The answer is it is better to think about this in terms of functions and try to write them out.

So, we have the statement print f(7). In order to find out what this evaluates to, we run 7 through the function and find out it evaluates to f(8). Ok great, but what is does that evaluate to? f(9). I have an exit clause in here deliberately - eventually i will equal 100 in the loop and that will be what is returned. What happens:

  • f(7) = ?
  • f(7) = f(8) = ?
  • f(7) = f(8) = f(9) = ?
  • f(7) = f(8) = f(9) = f(10) = ?
  • ...
  • f(7) = f(8) = ... = 100
  • so f(7) is 100

This is a fairly trivial example but does demonstrate recursion quite well.

£噩梦荏苒 2024-09-22 01:32:16

递归示例:

function foo(doRecurse)
{
    alert("Foo was called");

    if (doRecurse)
    {
        foo(false);
    }
}

foo(true);

在此示例中,当 foo 被以 true 调用时,它会再次以 false 调用自身。因此,您将收到两条带有上述代码的警报消息。函数foo调用函数foo的地方就是递归。

An example of recursion:

function foo(doRecurse)
{
    alert("Foo was called");

    if (doRecurse)
    {
        foo(false);
    }
}

foo(true);

In this example, when foo gets called with true, it calls itself again with false. So, you will get two alert messages with the above code. The bit where the function foo calls the function foo is recursion.

岁月流歌 2024-09-22 01:32:16

正如其他人提到的,递归只是一个调用自身的方法。这有几个优点,但也有一些缺点。

作为示例,阶乘的典型计算:

迭代版本:

int factorial(int factor)
{
    int result = 1;
    int current_factor = 1;
    while (current_factor < factor)
    {
      result *= current_factor;
      ++current_factor;
    }
    return result;
}

递归版本:

int factorial(int factor)
{
  if (factor == 1) return 1;
  return factor * factorial(factor - 1);
}

正如您所看到的,代码更短并且更容易阅读(如果您习惯递归)。

现在让我们检查堆栈:

  • 交互版本:3 个整数变量(因子、当前因子、结果)
  • 递归版本:
    1:1 变量的阶乘(因子)
    2 的阶乘:2 个变量(因子和阶乘的返回值(因子 - 1))
    3 的阶乘:3 个变量(因子,阶乘(因子 -1)的返回值,它具有另一个阶乘(因子 - 1)的返回值)
    ...

它看起来大致像这样

factorial(5):
  factorial(4):
    factorial(3):
      factorial(2):
        factorial(1):
        1
      2+1
    3+2+1
  4+3+2+1
5+4+3+2+1

正如你所看到的,递归需要更多的堆栈空间(你必须添加返回指针、变量的压入和弹出以及函数调用所需的其他一些东西)并且通常会更慢。空间问题可以通过使用尾部优化递归来减少,但这对这篇文章来说太大了,但为了完整性,优化版本将如下所示:

int factorial(int current, int end, int *result) // result is a pointer, so changes
                                               // on result affect the original variable
{
  if (current > end) return;
  *result = *result * current;
  factorial(current + 1, end, result);
}

递归真正有用的唯一地方是(恕我直言)遍历树结构。

As others mentioned recursion is just a method calling itself. This has several advantages but also some disadvantages.

As Example the typical calculation of factorials:

Iterative version:

int factorial(int factor)
{
    int result = 1;
    int current_factor = 1;
    while (current_factor < factor)
    {
      result *= current_factor;
      ++current_factor;
    }
    return result;
}

Recursive version:

int factorial(int factor)
{
  if (factor == 1) return 1;
  return factor * factorial(factor - 1);
}

As you can see the code is a little shorter and slightly easier to read (if you are used to recursion).

Now lets check the stack:

  • Interative version: 3 integer variables (factor, current_factor, result)
  • Recursive version:
    factorial of 1: 1 variable (factor)
    factorial of 2: 2 variables (factor and return value of factorial(factor - 1))
    factorial of 3: 3 variables (factor, return value of factorial(factor -1) which has the return value of another factorial(factor - 1))
    ...

It looks rougly like this

factorial(5):
  factorial(4):
    factorial(3):
      factorial(2):
        factorial(1):
        1
      2+1
    3+2+1
  4+3+2+1
5+4+3+2+1

As you can see recursion requires more space on the stack (you have to add the return pointers, push and pop of variables and some other stuff a function call requires) and it is usually slower. The space problem can be reduced by using tail-optimized recursion, but thats too big a topic for this post, but just for completeness, an optimized version would look like this:

int factorial(int current, int end, int *result) // result is a pointer, so changes
                                               // on result affect the original variable
{
  if (current > end) return;
  *result = *result * current;
  factorial(current + 1, end, result);
}

The only place where recursion is really helpful is (IMHO) walking through tree structures.

小猫一只 2024-09-22 01:32:16

函数调用自身。这可以用来代替迭代,但通常效率较低,因为必须为每个函数调用分配新的堆栈。

A function calls itself. This can be used instead of iteration, but is generally less efficient as a new stack has to be allocated for each function call.

爱已欠费 2024-09-22 01:32:15

函数会一遍又一遍地调用自身,直到满足某些条件。

一个愚蠢的例子:如何递归地走 100 步:

 function walk(steps) {
     if (steps > 0) { // not there yet
        take 1 step
        walk(steps - 1); // we're 1 step closer, now walk the rest of the way
     } else { // we're there already, don't walk any further than requested
        echo "You're there!"
     }
 }

 walk(100); // go!

这就是发生的情况:

 walk(100):
   take 1 step
   walk(99):
     take 1 step
     walk(98):
       take 1 step
       walk(97):
         (...)
                   walk(2):
                     take 1 step
                     walk(1):
                       take 1 step
                       walk(0):
                         // "steps > 0" no longer true, use the "else" branch
                         echo "You're there!"

可以使用循环(“迭代”)完成同样的事情:

function walk(steps) {
  for (c=steps; c > 0; c--) { // note the condition check - "c > 0"
    take 1 step
  }
  echo "You're there!"
}

walk(100); // go!

程序流程会有点不同,但结果是相同的:

walk(100):
  c=100
  take 1 step
  c=99
  take 1 step
  c=98
  take 1 step
  (...)
  c=2
  take 1 step
  c=1
  take 1 step
  c=0
  // "c > 0" no longer true, exit loop
  echo "You're there!"

请注意, 不能说递归和迭代哪个更好。在这个例子中,迭代(循环)比递归写起来更简单,也更容易理解;在其他情况下(例如遍历树结构),递归可能是更好的解决方案。

A function calls itself over and over, until some condition is met.

A silly example: How to walk 100 steps recursively:

 function walk(steps) {
     if (steps > 0) { // not there yet
        take 1 step
        walk(steps - 1); // we're 1 step closer, now walk the rest of the way
     } else { // we're there already, don't walk any further than requested
        echo "You're there!"
     }
 }

 walk(100); // go!

This is what happens:

 walk(100):
   take 1 step
   walk(99):
     take 1 step
     walk(98):
       take 1 step
       walk(97):
         (...)
                   walk(2):
                     take 1 step
                     walk(1):
                       take 1 step
                       walk(0):
                         // "steps > 0" no longer true, use the "else" branch
                         echo "You're there!"

Note that the same thing could be accomplished with a loop ("iteratively"):

function walk(steps) {
  for (c=steps; c > 0; c--) { // note the condition check - "c > 0"
    take 1 step
  }
  echo "You're there!"
}

walk(100); // go!

The program flow would be a bit different, yet the result is the same:

walk(100):
  c=100
  take 1 step
  c=99
  take 1 step
  c=98
  take 1 step
  (...)
  c=2
  take 1 step
  c=1
  take 1 step
  c=0
  // "c > 0" no longer true, exit loop
  echo "You're there!"

It cannot be said whether recursion or iteration is always better. In this example, iteration (loop) is simpler to write and easier to understand than recursion; in other cases (e.g. traversing tree structures), recursion may be a better solution.

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