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

来源:互联网 发布:淘宝售后是做什么的 编辑:程序博客网 时间:2024/05/01 08:04
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:改写此程序,要求将逆波兰表达式转换成常规表达式输出。可以包含多余的括号。


原创粉丝点击