递归算法的几个常见问题

阶乘

package section1.recursive;
import java.util.Scanner;
public class Factorial {
	public static int cal(int n){
		if(n==0){
			return 1;
		}else{
			return n*cal(n-1);
		}
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int n=cin.nextInt();
		System.out.println(cal(n));
	}
}

菲不尼茨数列

F(n)=F(n-1)+F(n-2) (n>1)

package section1.recursive;
import java.util.Scanner;
public class Fibonacci {
	public static int cal(int n){
		if(n<=1){
			return 1;
		}else{
			return cal(n-1)+cal(n-2);
		}
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int n=cin.nextInt();	//输入第几个菲不尼茨数
		System.out.println(cal(n));
	}
}

排列问题

输入举例(第一行:元素个数,第二行:排列元素):
3
1 2 3
输出举例(所有排列):
1 2 3
1 3 2
2 1 3
2 3 1
3 2 1
3 1 2
思想:将第k个元素与后面的每个元素进行交换

package section1.recursive;
import java.util.Iterator;
import java.util.Scanner;
public class Permutation {
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int n=cin.nextInt();
		int[] list = new int[n];
		for (int i = 0; i < n; i++) {
			list[i] = cin.nextInt();
		}
		perm(list,0,n-1);
	}
	static int num = 0;
	public static void perm(int[] list,int k,int m){	//k是所给数列排列开始的地方,m为结束的地方
		if(k==m){	//每次递归到最后的时候k=m
			for (int i = 0; i < list.length; i++) {	//每个递归都背着一个list数组
				System.out.print(list[i]+" ");
			}
			System.out.println();
		}else{
			for (int i = k; i <= m; i++) {	//将第k个元素与后面的每个元素进行交换
				swap(list,k,i);
				perm(list,k+1,m);
				swap(list,k,i);	//注意交换之后还要还原,即在当前list[k]与后面每个元素交换时保持当前list[k]每次交换时还是这个list[k]
			}	
		}
	}
	public static void swap(int[] list,int a,int b){	//数组元素交换
		int temp = list[a];
		list[a] = list[b];
		list[b]=temp;
	}
}

整数划分

输入举例(第一个是n,第二个是m):
6 3
输出举例(整数n不大于m的划分的种类数)
7
思路:正整数n的最大加数n1不大于m的划分由n1=m的划分和n1≤m-1 的划分组成

package section1.recursive;
import java.util.Scanner;
public class IntegerPartition {
	public static int q(int n,int m){	//n:要划分的整数,m:划分出来的部分不能大于m
		if((n<1)||(m<1)){	//输入零或负数无效,划分的种类数为0
			return 0;
		}else if((n==1)||(m==1)){	//n或m为1,只有一种可能,即划分的种类数为1,加数全为1
			return 1;
		}else if(n<m){	//最大加数大于了正整数n(m不能大于n)
			return q(n,n);
		}else if(m==n){	//这种情况下不能用最后一个式子计算,单独列出来
			return q(n,m-1)+1;
		}else{	//一般情况,正整数n的最大加数n1不大于m的划分由n1=m的划分和n1≤m-1 的划分组成(详解可看ppt)
			return q(n,m-1)+q(n-m,m);	
		}
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int n = cin.nextInt();
		int m=cin.nextInt();
		System.out.println(q(n,m));
	}
}

组合

找出n个自然数(1,2,…,n)中取r个数的组合
输入举例(第一个数:代表1到几,第二个数:代表取几个数):
5 3
输出举例
543
542
541
532
531
521
432
431
421
321
思想:产生几个数就进行几次递归,每次递归都在上一次递归的基础上往后一个数开始

package section1.recursive;
import java.util.Scanner;
public class Combination {
	public static void comb(int m, int k, int[] arr) {	//m:代表1到几,k:代表取几个数,arr代表现阶段此轮递归产生的数组
		for (int i = m; i >= k; i--) {
			arr[k - 1] = i;	//一次递归产生一个数
			if (k != 1) {	//k代表要取几个数,取几个数就递归几次,即一次递归产生一个数
				comb(i - 1, k - 1, arr);	//因为产生的每组数中不能存在重复的,因此每轮递归产生下一个属时都要往后一位
			} else {
				for (int j = arr.length - 1; j >= 0; j--) {	//输出此次递归最后产生的数组
					System.out.print(arr[j]);
				}
				System.out.println();
			}
		}
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		int m = cin.nextInt();
		int k = cin.nextInt();
		int[] arr = new int[k];
		comb(m, k, arr);
	}
}

递归小结

优点:结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。
缺点:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。
解决方法
在递归算法中消除递归调用,使其转化为非递归算法。
1、采用一个用户定义的栈来模拟系统的递归调用工作栈。该方法通用性强,但本质上还是递归,只不过人工做了本来由编译器做的事情,优化效果不明显(常用方法!)。
2、用递推来实现递归函数。
3、通过变换能将一些递归转化为非递归,从而迭代求出结果。
注:后两种方法在时空复杂度上均有较大改善,但其适用范围有限。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值