递归与循环

理论上,任何递归都可以表述成循环的格式,任何循环都可以重写为递归形式。

递归要层层深入的调用,可能会产生栈溢出的问题  。因为它要用到栈,栈的大小在一般的语言中都是有限制的。 有些语言支持”尾递归“,这样就可以突破栈的限制,可以不用每次都压栈。java不支持尾递归,C语言可以用goto语句模拟出尾递归的效果,但一般不主张这么做,因为会降低代码的可读性。有些语言中没有循环语句,只能使用递归,比如Lisp。

如何把循环改成递归?

主要是要发现循环过程中的”相似性“。写一个方法要想实现递归调用,就是它直接或间接地调用自身,调用和被调用者这两个应用应该具有相似性。

例:

public   class   A

{

      public   static   void   main(String   []args)

      {

         for(int i=0;i<10;i++)

         {

            System.out.println(i);

         }

      }

}

打印从0到9的整数

public   class   A

{

      public   static   void   f()

      {

          ...

         f();

      }

      public   static   void   main(String   []args)

      {

         f();

      }

}

如果方法是无参的,它去调用自身,就回到了程序左括号开始执行,就会产生类似于死循环,程序永远结束不了。一个方法去调用自身不能是完全相同的环境,这样执行永远不能结束。必然会有一些变化,这个变化是由参数引起的

比如有一个参数n

//表示打印从0到n

 public   static   void   f(int   n)

{

       ...

      f();

}

多一个参数n,在调用的时候就可以调整参数使得每次调用条件有一点点不同,就可以在适当的时候终止递归,这就是不要忘记递归出口

比如说领导交给我们一项工作,我们原封不动的交给下属,这样是不对的

 public   static   void   f(int   n)

{

      f(n);

}

比如我只打印n,请另外一个人打印0~n-1

 public   static   void   f(int   n)

      f(n-1);   //打印0-(n-1)

      System.out.println(n);   //打印n

}

如果每次调用这个递归,这个递归会无限的调用下去。

当n=3时调用2,n=2时调用1,n=1时调用0,n=0时调用-1······这样循环会一直持续下去,这种递归的调用不会有终止,这样就没有出口。

在递归中一般总是要有一个出口,这里要判断在什么条件下调用递归,什么时候不调用。如果在某一层的递归中不再继续调用,这个时候递归就可以逐层返回了。当n=0时需不需要调用?n=1需不需要调用?如果n=1显然是需要调用的,因为0还没有执行。如果n=0,就不需要调用,所以n>0时候调用f(n-1)

//打印0~9

public class A

{

      public static void f(int n)

      {

            if(n>0)   f(n-1);

            System.out.println(n);     

      }

       public static void main(String[] args) 

       {

             f(9);

       }

}

递归的关键就是要找到相似性。

public class A

{

      public static void f(int n)

      {

            if(n>0)   f(n-1);

            System.out.println(n);     

      }

       //打印begin~end

      public static void f2(int begin,int end)

      {

            if(begin>end)

            System.out.println(begin);

           f2(begin+1,end);     

      }


       public static void main(String[] args) 

       {

             f2(0,9);

       }

}

递归的设计是很灵活的,并非只有一种解法。

循环改递归的关键是发现逻辑相似性,不要忘记递归出口。

如果没有明显的相似性,需要主动构造。

不能相似的原因很可能是缺少参数。填参数是主要技巧。

另一个例子:

public class B {
public static int addAll(int[]a)
{
int x=0;
for(int i=0;i<a.length;i++)
{
x+=a[i];
}
return x;

}
public static void main(String[] args)
{
int a[]={2,5,3,9,12,7};
int sum=addAll(a);
System.out.println(addAll(a));
}

}

改写成递归形式

public class B {
public static int f(int[]a,int begin)

{

                if(begin==a.length)  return 0;

int x=f(a,begin+1);

                return x+a[begin];


}
public static void main(String[] args)
{
int a[]={2,5,3,9,12,7};
int sum=f(a);
System.out.println(f(a));
}

}

C++循环与递归例子

#include<iostream>
using namespace std;
int addAll(int*a,int a_length)
{
int x=0;
for(int i=0;i<a_length;i++)
x+=a[i];
return x;
}
int f1(int *a,int a_length,int begin)
{
if(begin>=a_length)
return 0;
int x=f1(a,a_length,begin+1);
return x+a[begin];
}
int f2(int *a,int a_length,int end)
{
if(end<0)
return 0;
int x=f2(a,a_length,end-1);
return x+a[end];
}
int main()
{
int a[]={1,2,3,4};
int n=sizeof(a)/sizeof(a[0]);
//int m=addAll(a,n);
//int m=f1(a,n,0);
int m=f2(a,n,n-1);
cout<<m;
return 0;

}


判断两个字符串是否相同

public class A {
public static boolean isSameString(String s1,String s2)
{
return s1.equals(s2);
}
public static boolean f(String s1,String s2)
{
if(s1.length()!=s2.length())return false;
if(s1.length()==0)return true;
if(s1.charAt(0)!=s2.charAt(0))return false;
return f(s1.substring(1),s2.substring(1));
}
public static void main(String[]args)
{
System.out.println(isSameString("abc","abcd"));
System.out.println(f("abc","abcd"));
}


递归调用仅仅是被调函数恰为主调函数

注意每次调用的层次不同

注意每次分配形参并非同一个变量

注意返回的次序

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值