简析递归算法

简析递归

递归是编程过程中比较重要,也是比较简单的算法之一,简单的递归思路,往往比较容易理解,而略微复杂的递归则容易让人头痛。

递归主要分两种

  1. 直接递归
    直接递归函数里面调用本身这个函数,实现递归
  2. 间接递归。
    间接递归是两个,或两个以上的函数互相调用,正好形成一个循环,例如:A调用B,而B又调用A,在某种条件下,会实现间接递归。自然也可以是多个函数组成间接递归,不过考虑到逻辑问题,一般都不会这么绕。

直接递归

public int Factorial( n)
{
if(n==0||n==1)
return 1;
else
return n * Factorial(n-1);
}

这是一个直接递归求阶乘的例子,自然求阶乘用递归不是一个好的方法,完全可以用循环来代替。由于这是一个单层次的问题,而用递归把每一次阶乘都当作一个层次,而每一层都只有个元素,有些得不偿失

看一下这个遍历文件夹的例子

    public static void getFileDir(File file){
        //如果是文件夹,那就是直接递归,如果不是那就直接输出文件名
        if(file.isDirectory()) {
            File[] files=file.listFiles();
            for(int i=0;i<files.length;i++){
                main.getFileDir(files[i]);
            }
        }
        else{
            System.out.println(file.getAbsolutePath());
        }
    }

思路相当的简单,但是你只要给一个文件夹目录,就可以遍历出这个文件夹下面的所有文件名,而每一层都是一个目录,同一层下有多个元素,就像一颗树一样。

遍历的思想就体现出来了,树形结构的遍历不就是递归吗!而线性结构的遍历就是循环.

说一下递归的深度问题,有时候需要获得递归的深度,好分组,像在爬虫中就是使用递归的思想,但是如何分类呢?没错,就是根据深度来分,但如何知道这是第几层呢(深度)。来让我们小改一下程序

    public static void getFileDir(File file,int deep){
        if(file.isDirectory()) {
            File[] files=file.listFiles();
            for(int i=0;i<files.length;i++){
                main.getFileDir(files[i],deep+1);
            }
        }
        else{
            System.out.println(file.getAbsolutePath()+"    深度为:"+deep);
`
    }
    }

没错仅仅加了一个参数,deep,传入1就可以了,多么简单,第二层输出的是deep+1,第三层就是((deep+1)+1),那么第一层的deep会变吗?没有变化!第二层永远都会是二,我们没有对deep做过赋值,只是改变传入的参数而已(在前面遇到有不理解此问题的朋友,所有特意说明一下)

接下来看一下间接递归的例子

var  time = 24*60*60*2; //倒计时两天的时间,自己设定
            //输出信息
            function begin(){
                var today=new Date()
                var day =today.getDate()
                var dat=today.getMonth()
                var future=day+2
                document.getElementById('now').innerHTML="现在时间"+dat+"月"+day+"倒计时开始"

                leasttime()

                document.getElementById("future").innerHTML="预计结束时间"+dat+"月"+future

            }
            //时间倒计时函数
            function leasttime(){

                var  ho=time/(60*60);
                var  mi=(time/60)%(60)
                var  se=time%60
                mi=parseInt(mi)
                ho=parseInt(ho)
                ho=checkTime(ho)
                se=checkTime(se)
                mi=checkTime(mi)
                time-=1;
                document.getElementById("last").innerHTML=ho+":"+mi+":"+se
                //倒计时结束
                if(time==0){
//              //重置计时器 ,再次开始计时
                time=30;
                begin()
                }
                setTimeout("leasttime()",1000);
            }
            //将时间的格式转化一下
            function checkTime(i)
            {
                if (i<10) 
                  {i="0" + i}
                  return i
            }

这是一个使用javascript完成的一个循环倒计时的计时器,由于在javascript中是解释执行的,而且由于在主线程中,无法使用死循环执行循环倒计时的效果,浏览器会卡死并报错。

总结一下递归的构成条件

  1. 子问题须与原始问题为同样的事,且更为简单;
  2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。

递归的使用
1 遍历树形结构
2. 像javascript中间接递归的特例。
递归算法一般用于解决三类问题:
1.数据的定义是按递归定义的。(Fibonacci函数)
2.问题解法按递归算法实现。
这类问题虽则本身没有明显的递归结构,但用递归求解比迭代求解更简单,如Hanoi问题。
3.数据的结构形式是按递归定义的。
如二叉树、广义表等,由于结构本身固有的递归特性,则它们的操作可递归地描述。
递归的缺点:
递归算法解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。

如何设计递归算法
1.确定递归公式
2.确定边界(终了)条件
练习:
用递归的方法完成下列问题
1.求数组中的最大数
2.1+2+3+…+n
3.求n个整数的积
4.求n个整数的平均值
5.求n个自然数的最大公约数与最小公倍数
6.有一对雌雄兔,每两个月就繁殖雌雄各一对兔子.问n个月后共有多少对兔子
7.已知:数列1,1,2,4,7,13,24,44,…求数列的第 n项.
练习:
1.计算ackerman函数值:
n+1 m=0
ack(m,n)={ ack(m-1,1) m<>0,n=0
ack(m-1,ack(m,n-1)) m<>0,n<>0
求ack(5,4)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值