您的位置:首页 > 编程语言 > Java开发

几个简单递归问题(菲波那契数列 ,二叉树,逆波兰式)

2013-04-29 15:12 411 查看
9.2 例题:菲波那契数列  

 

问题描述

菲波那契数列是指这样的数列:数列的第一个和第二个数都为 1,接下来每个数都等于前面 2 个数之和。给出一个正整数 a,要求菲波那契数列中第 a 个数是多少。

输入数据

第 1 行是测试数据的组数 n,后面跟着 n 行输入。每组测试数据占 1 行,包括一个
正整数 a(1 <= a <= 20)。

输出要求

n 行,每行输出对应一个输入。输出应是一个正整数,为菲波那契数列中第 a 个数

的大小。

输入样例

4

5

2

19

1

 

输出样例

5

1

4181

1

解题思路:这个题目要求很明确,因为 a的规模很小,所以递归调用不会产生栈溢

出的问题。设第 n 项值为 f(n),则  f(n) = f(n-1)+f(n-2)。已知 f(1)=1,f(2)=1,则从第3

项开始,可以用公式求。

import java.util.Scanner;

/*斐波那契数列

 * 1 1 2 3 5 8 13.....

 * */

public class Test9_2 {

    /**

     * @param args

     */

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        Scanner sc=new Scanner(System.in);

        int n=sc.nextInt();

        System.out.println(f(n));

    }

    

    public static int f(int n)

    {

        if(n==1||n==2)

            return 1;

        return f(n-1)+f(n-2);

    }

}

思考题9.2:在n比较大(比如,n>10)的时候,函数f的执行次数和下面哪一项比

较接近?

A) n^2     B) n^3   C) 2^n   D) n!

9.3 例题:二叉树  

问题描述


 

如上图所示,由正整数 1, 2, 3, ...组成了一棵无限大的二叉树。从某一个结点到根结点(编号是 1 的结点)都有一条唯一的路径,比如从 10 到根结点的路径是(10, 5, 2, 1),

从 4 到根结点的路径是(4, 2, 1),从根结点 1 到根结点的路径上只包含一个结点 1,因此路径就是(1)。对于两个结点 x 和 y,假设他们到根结点的路径分别是(x1, x2, ... ,1)和(y1,

y2, ... ,1)(这里显然有 x = x1,y = y1),那么必然存在两个正整数 i 和 j,使得从 xi  和 yj开始,有 xi = yj  ,xi + 1 = yj + 1,xi + 2 = yj + 2,... 现在的问题就是,给定 x 和y,要求 xi(也就是 yj)。

 

 输入数据

输入只有一行,包括两个正整数 x 和 y,这两个正整数都不大于 1000。

 

输出要求

输出只有一个正整数 xi。

 

输入样例

10 4

 

输出样例

2

 

解题思路

这个题目要求树上任意两个节点的最近公共子节点。分析这棵树的结构不难看出,不论奇数偶数,每个数对 2 做整数除法,就走到它的上层结点。 我们可以每次让较大的一个数(也就是在树上位于较低层次的节点)向上走一个结点,直到两个结点相遇。如果两个节点位于同一层,并且它们不相等,可以让其中任何一个先往上走,然后另一个再往上走,直到它们相遇。设 common(x, y)表示整数 x 和 y的最近公共子节点,那么,根据比较 x 和y 的值,我们得到三种情况:1) x 与y 相等,则 common(x, y)等于 x 并且等于y;2)x
大于 y,则 common(x, y)等于 common(x/2, y);3)x 大于 y,则 common(x, y)等于 common(x y/2);

import java.util.Scanner;

//二叉树,求另个结点到根节点的路径中重复的位置

public class Test9_3 {

    /**

     * @param args

     */

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        Scanner sc=new Scanner(System.in);

        int x=sc.nextInt();

        int y=sc.nextInt();

        System.out.println(f(x,y));

    }

    public static int f(int x,int y)

    {

        if(x==y)

            return x;

        else if(x>y)

            return f(x/2,y);

        else

            return f(x,y/2);

    }

    

}

实现中常见的问题

问题一:有一种比较直观的解法是对于两个给定的数,分别求出它们到根节点的通路上的所有节点的值,然后再在两个数组中寻找数码最大的公共节点。这种做法的代码

比较繁琐,容易在实现中出错;

问题二:代码实现逻辑不明晰,造成死循环等错误,例如,有人只将其中一个数不停地除以 2,而不理会另外一个数。

 

9.4 例题:逆波兰表达式   

问题描述

逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式 2 + 3 的逆波兰表示法为+ 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4 的逆波兰表示法为* + 2 3 4。本题求解逆波兰表达式的值,其中运算符包括 +  -  *  /   四个。

输入数据

输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数

输出要求

输出为一行,表达式的值。 

输入样例

* + 11.0 12.0 + 24.0 35.0

输出样例

1357.000000

解题思路

这个问题看上去有些复杂,如果只是简单地模拟计算步骤不太容易想清楚,但是如果用递归的思想就非常容易想清楚。让我们根据逆波兰表达式的定义进行递归求解。在递归函数中,针对当前的输入,有五种情况:1)输入是常数,则表达式的值就是这个常数;2)输入是’+’,则表达式的值是再继续读入两个表达式并计算出它们的值,然后将它们的值相加;3)输入是’-’;4)输入是’*’; 5)输入是’/’;后几种情况与 2)相同,只是计算从’+’变成’-’,’*’,’/’。  

import java.util.Scanner;

//给一个用逆波兰式表示的数学式子,求出结果

public class Test9_4 {

    /**

     * @param args

     */

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        System.out.println(cal());

    }

    

    public static float cal()

    {

        Scanner sc=new Scanner(System.in);

        String c=sc.nextLine();//每当输入一个字符后要按回车键

        //System.out.println(c);

        if(c.equals("+"))

            //System.out.print("("+cal()+"+"+cal()+")")将逆波兰式转化成常规表达式输出

            return cal()+cal();            

        if(c.equals("-"))

            return cal()-cal();    

        if(c.equals("*"))

            return cal()*cal();

        if(c.equals("/"))

            return cal()/cal();

        return Float.parseFloat(c);

    }

}

 实现中常见的问题

问题一:不适应递归的思路,直接分析输入的字符串,试图自己写进栈出栈的程序,写得逻辑复杂后,因考虑不周出错;

问题二:不会是用 atof()函数,自己处理浮点数的读入,逻辑复杂后出错。  

 

思考题9.4:改写此程序,要求将逆波兰表达式转换成常规表达式输出。可以包含多余的括号。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java 递归