即使没有基本情况,为什么递归也能正常工作?

时间:2017-05-24 09:38:51

标签: c recursion

我正在学习递归,所以我试图创建一个程序,以递归方式反转数组并使用分而治之的技术(我不确定这是否分而治之? ),所以我的问题是,我想知道为什么如果我删除第11行程序仍然正常工作,但如果我删除第16行,它将无限进行。
我检查了我的调试器,我知道为什么它是无限循环,因为每个堆叠的帧索引仍然小于正确,这使得循环无限循环,所以我的问题,为什么递归调用从检查while条件开始并跳过第11行?这对我来说很奇怪,因为我只是学习了一些递归的基础知识,所以这对我来说很容易让人困惑。所以在我的代码中,第16行被认为是基本情况?因为我从教程中学到了递归需要一个基本案例。

#include <stdio.h>
#define size 10
void swap(int *a, int *b)
{
    int temp= *b;
    *b = *a;
    *a = temp;
}
void revcur (int arr[], int left, int right)
{
    if (left>=right) return; //This program still works even if i delete this line or comment 
    while (left<right)
    {
        swap(&arr[left],&arr[right]);
        revcur(arr,left+1,right-1);
        return; //This program will go to infinite recursive if i delete this
    }
}
int main()
{
    int arr[size]={1,2,3,4,5,6,7,8,9,10};
    revcur(arr,0,size-1);
    int i; for (i=0; i<size; i++)
    {
        printf("%d ",arr[i]);
    }
}

5 个答案:

答案 0 :(得分:2)

假设left = 2且right = 1。如果你离开第11行会发生什么,条件为真,你将立即退出函数(return)。当你删除第11行时,你将进入while循环。 while语句检查left是否小于右边,而不是右边。因此,它会跳过while循环中的所有代码,这些代码会将您带到函数的末尾,因此它将自动返回。这与将第11行留在原位的情况相同。

当你删除第16行时它无限循环的原因是因为你没有更新&#34; left&#34;和&#34;对&#34;一个新的价值。因此,如果左边确实小于正确,它将进入循环,因为左和右的值永远不会改变,下一次它到达&#34;而#34;声明,&#34;左&#34;仍将低于&#34;对&#34;因此循环将无限期地继续。

答案 1 :(得分:2)

此代码模式:

while (condition)
{
    // do something
    return;
}

完全等同于

if (condition)
{
    // do something
    return;
}

换句话说,您的基本案例是!condition

您可以用

替换您的实现
void revcur(int arr[], int left, int right)
{
    if (left>=right) return;

    swap(&arr[left],&arr[right]);
    revcur(arr,left+1,right-1);
}

这将完全等效(while从未实际循环)。

您的版本似乎有迭代等效的痕迹:

void revcur(int arr[], int left, int right)
{
    while (left<right) {
        swap(&arr[left++],&arr[right--]);
    }
}

答案 2 :(得分:1)

你的功能过于复杂,实际上只是这样做:

void revcur(int arr[], int left, int right)
{
  if (left<right)
  {
    swap(&arr[left], &arr[right]);
    revcur(arr, left + 1, right - 1);
  }
}

原始函数中的while循环永远不会循环多次,因此具有 while实际上毫无意义;写if只是一种奇怪的方式。

考虑以下功能。

void foo(int x)
{
  printf("x = %d\n",x );

  if (x > 0)
    foo(x - 1);
}


void bar(int x)
{
  printf("x = %d\n", x);

  while (x > 0)
  {
    bar(x - 1);
    return;
  }
}

foobar执行的操作完全相同,只是bar正在滥用while循环使用它作为if,就像您在你的功能。

现在尝试从return函数中删除bar,看看会发生什么。

答案 3 :(得分:0)

你的功能是void。所以你最后一次在递归中调用它。它不会进入while循环,因此它不会再次调用该函数,从而结束递归。因为ypu在你的while循环中有一个返回值,我猜你可以用if语句替换它,它基本上会做同样的事情,你可以摆脱return

答案 4 :(得分:0)

本声明

if (left>=right) return; //This program still works even if i delete this line or comment 

只是多余的。

如果left >= right为真,则while语句left < right的条件为false,并且不会执行while循环。

所以你可以删除if语句。

void revcur (int arr[], int left, int right)
{
    while (left<right)
    {
        swap(&arr[left],&arr[right]);
        revcur(arr,left+1,right-1);
        return; //This program will go to infinite recursive if i delete this
    }
}

但是你可能不会删除return语句,因为在这种情况下循环将是无限的,因为在循环内部变量leftright本身的值不会改变,并且如果最初计算结果为true,则while语句的条件将始终求值为true。

另一方面,使用while语句是没有意义的,因为在任何情况下都假定循环体只会因return语句而执行一次。 while语句的条件很重要。

所以你可以用while语句替换if语句。

例如

void revcur (int arr[], int left, int right)
{
    if ( left < right)
    {
        swap(&arr[left],&arr[right]);
        revcur(arr,left+1,right-1);
    }
}

考虑到如果数组只包含一个元素,那么它将与自身交换,该函数将有一个冗余的递归调用。

该函数可以重写为只有两个参数,并且没有冗余的递归调用。

下面是一个演示程序,演示如何使用两个参数编写函数。

#include <stdio.h>

void swap( int *a, int *b )
{
    int temp= *b;
    *b = *a;
    *a = temp;
}

void reverse( int a[], size_t n )
{
    if ( !( n < 2 ) )
    {
        swap( &a[0], &a[n-1] );
        reverse( a + 1, n - 2 );
    }
}

int main(void) 
{
    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    const size_t N = sizeof( a ) / sizeof( *a );

    for ( size_t i = 0; i < N; i++) printf( "%d ", a[i] );
    putchar( '\n' );

    reverse( a, N );

    for ( size_t i = 0; i < N; i++) printf( "%d ", a[i] );
    putchar( '\n' );

    return 0;
}

程序输出

1 2 3 4 5 6 7 8 9 10 
10 9 8 7 6 5 4 3 2 1