数据结构---递归

递归算法:是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。


递归算法解决问题的特点
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。

递归算法所体现的“重复”一般有三个要求:

一是每次调用在规模上都有所缩小(通常是减半);

二是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);

三是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。


递归的应用:

1.变位数(全排列单词)

思想:假设对n个字母的单词全排列

1、全排列最右边n-1个单词

2、轮换所有的n个字母(即单词左移一位,保证每个字母有机会排在开头)

3、重复以上步骤n次

public class ArrangementWord {

	private static int count;// 记录输出个数

	public static void permutation(char[] a, int low, int high) {
		if (low == high) {
			System.out.print(++count + ":");
			for (int i = 0; i <= high; i++) {
				System.out.print(a[i]);
			}
			System.out.print("  ");
			if (count % 5 == 0)
				System.out.println();
		} else {
			for (int j = low; j <= high; j++) {
				swap(a, j, low);
				permutation(a, low + 1, high);
				swap(a, j, low);
			}
		}
	}

	private static void swap(char a[], int c, int d) {
		char temp;
		temp = a[c];
		a[c] = a[d];
		a[d] = temp;
	}

	public static void main(String[] args) {
		char[] words = { 'q', 'w', 's', 'c' };
		permutation(words, 0, words.length - 1);
	}

}


2.汉诺塔

有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:
每次只能移动一个圆盘;
大盘不能叠在小盘上面。
提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。
问:如何移?最少要移动多少次?

	public static void main(String[] args) {
		hannuota(3, 'A', 'B', 'C');// 从A通过B移到C
	}

	/**
	 * 
	 * @param n
	 *            盘子个数
	 * @param from
	 *            A盘
	 * @param inter
	 *            中间盘B
	 * @param to
	 *            C盘
	 */
	public static void hannuota(int n, char from, char inter, char to) {
		if (n == 1)
			System.out.println("disk " + n + " from " + from + " to " + to);
		else {
			hannuota(n - 1, from, to, inter);
			System.out.println("disk " + n + " from " + from + " to " + to);
			hannuota(n - 1, inter, from, to);
		}
	}

3. 斐波那契数列问题

题目:写一个函数,输入n,求斐波那契数列的第n项。斐波那契数列定义如下:


解法一:递归,效率很低的解法

	public static long Fabonacci(int n) {
		if (n <= 0)
			return 0;
		if (n == 1)
			return 1;
		return Fabonacci(n - 1) + Fabonacci(n - 2);
	}
解法二:简单的迭代方法,从下往上计算,首先根据f(0)和f(1)计算出f(2),再根据f(1)和f(2)计算出f(3)……以此类推。很容易理解,时间复杂度是O(n).
	public static long Fabonacci1(int n) {
		int[] result = { 0, 1 };
		if (n < 2)
			return result[n];
		long currOne = 0;
		long currTwo = 1;
		long res = 0;
		for (int i = 2; i <= n; ++i) {
			res = currOne + currTwo;
			currOne = currTwo;
			currTwo = res;
		}
		return res;
	}


相关题目:

1.一只青蛙一次可以跳上1级台阶,也以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
分析:
可以看做斐波那契问题。如果只有1级台阶,只有一种情况。如果有2级台阶,则有两种方法:一次是分两次跳,每次跳1级;另一种是一次跳2级。
对于一般情况,把n级台阶的跳法看成n的函数f(n)。n>2时,第一次跳的时候就有两种不同的选择:一是第一次只跳1级,此时跳法数目等于后面n-1级台阶的跳法数目,即f(n-1);另外一种是选择第一次跳2级,此时跳法数据等于后面的n-2级台阶的跳法数目,即为f(n-2)。因此,n级台阶的不同跳法总数是f(n)=f(n-1)+f(n-2)。


2.疯狂跳台阶:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
分析:
假设现在n=10,方法数记为f(10)。如果第一下跳了1级,那么剩下的方法为f(9),如果第一下跳了2级,那么剩下的方法是f(8),依次类推……,如果第一下跳了9级那么剩下的方法是f(1),如果第一下就直接跳上10级,那么就只有一种方法。
因此,f(10)=f(9)+f(8)+f(7)+f(6)+f(5)+f(4)+f(3)+f(2)+f(1)+1;
刚开始f(1)=1,f(2)=2  ... 


3.矩形覆盖:我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
分析:
假设现在是有8个2*1的小矩形去覆盖一个2*8的大矩形。我们把覆盖方法数记为f(8)。用第一个1*2的小矩形去覆盖大矩形的最左边有两个选择,竖着放或者横着放。当竖着放的时候,右边还剩下2*7的区域,这种情况下的覆盖方法是f(7)。接下来考虑横着放的情况。当用1*2的小矩形横着放在左上角的时候,左下角必须横着放一个1*2的小矩形,而在右边还剩下2*6的区域,这种情况下的覆盖方法记为f(6)。
因此f(8)=f(7)+f(6)。可以看出,这仍然是斐波那契数列



参考来源:

剑指offer——斐波那契数列相关问题总结



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值