阶乘
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、通过变换能将一些递归转化为非递归,从而迭代求出结果。
注:后两种方法在时空复杂度上均有较大改善,但其适用范围有限。