算法期末整理——递归篇
文章目录
一、定义
1、在调用一个函数的过程中又出现直接或间接调用该函数本身,称为函数的递归(Recursion)调用,这种函数称为递归函数。
2、若p函数定义中调用p函数,称之为直接递归。若p函数定义中调用q函数,而q函数定义中又调用p函数,称之为间接递归。
二、递归要素
三、递归特点
1、优点
结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。
2、缺点:
递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。
注:在某些递归算法中可消除递归调用,使其转化为非递归算法。
四、使用递归
1、能用递归解决的问题应该满足
(1) 可以转化为一个或多个子问题来求解,而这些子问题的求解方法与原问题完全相同,只是在数量规模上不同。
(2) 递归调用的次数必须是有限的。
(3) 必须有结束递归的条件来终止递归。
2、何时使用递归
(1) 定义是递归的(阶乘、斐波那契数列等)。
(2) 数据结构是递归的(单链表、二叉树等)。
(3) 问题的求解方法是递归的(汉诺塔、回溯法等)。
五、经典案例
1、求阶乘
package recursion;
import java.util.Scanner;
//阶乘函数
public class test1 {
public static void main(String args[]){
int n;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
fac(n);
System.out.println(fac(n));
}
}
public static int fac(int n){
if(n==0)
return 1;
return n*fac(n-1);
}
}
2、斐波那契数列
package recursion;
import java.util.Scanner;
//斐波那契数列
public class test2 {
public static void main(String args[]){
int n;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
Fibonacci(n);
System.out.println(Fibonacci(n));
}
}
public static int Fibonacci(int n){
if(n<=1)
return 1;
return Fibonacci(n-1)+Fibonacci(n-2);
}
}
例1 青蛙跳台阶
问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法?
解决:符合Fibonacci数列规律,用其求解。
代码:略。
例2 铺地板
问题:对于一条长为2×n的长方形过道,需要用1×2的瓷砖铺满,求铺设方案的数量m。例如:当n=3时,过道为2×3的长方形,瓷砖的铺设方案共有三种,如下图所示。
解决:符合Fibonacci数列规律,用其求解。
代码:略。
例3 XP的楼梯
问题:XP是个淘气的孩子,他最近迷上了跳楼梯。他可以一次跳一级,也可以一次跳两级,他居然还能够一次跳三级楼梯(危险动作,请勿模仿)。某次,XP在跳完楼梯后突然想到一个问题,如果有n级楼梯,他从第一级开始往上跳,一直跳到第n级共有多少种不同的方案?你能帮他解决这个问题吗?当然,如果只有一级楼梯,很明显他只有一种选择。
解决:符合Fibonacci数列的类似规律。
package recursion;
import java.util.Scanner;
public class test5 {
public static void main(String args[]){
int n;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
fac(n);
System.out.println(fac(n));
}
}
public static int fac(int n){
if(n==1)
return 1;
if(n==2)
return 1;
if(n==3)
return 2;
if(n>3)
return fac(n-1)+fac(n-2)+fac(n-3);
return 0;
}
}
例4 一只小蜜蜂
问题:有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。其中,蜂房的结构如下所示。
输入整数a和b表示起点和终点,输出a到b可能的线路数。例如:输入1和2,输出1;输入3和6,输出3。
解决:符合Fibonacci数列规律。
import java.util.Scanner;
public class Main{
public static void main(String args[]){
int a,b,n,s;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
a = sc.nextInt();
b = sc.nextInt();
n = b-a;
s = fibonacci(n);
System.out.println(s);
}
}
public static int fibonacci(int n){
if(n<=1)
return 1;
else
return fibonacci(n-1)+fibonacci(n-2);
}
}
3、汉诺塔问题
package recursion;
import java.util.Scanner;
//汉诺塔问题
public class test3 {
static int m = 1;
public static void main(String args[]){
int n;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
char a = 'A';
char b = 'B';
char c = 'C';
m = 1;
hanoi(n,a,b,c);
System.out.println();
//注意换行
}
}
public static void hanoi(int n,char a,char b,char c){
if(n>0){
hanoi(n-1,a,c,b);
move(n,a,c);
hanoi(n-1,b,a,c);
}
}
public static void move(int n,char a,char c){
System.out.println("第"+m+++"步:"+n+"号盘从"+a+"柱移至"+c+"柱");
//第1步:1号盘从A柱移至C柱,注意符号:
}
}
4、整数划分
求正整数n的不同划分个数,比如4的划分为下图情况。
package recursion;
import java.util.Scanner;
//整数划分
public class test10 {
public static void main(String args[]){
int n,m;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
m = sc.nextInt();
up(n,m);
System.out.println(up(n,m));
}
}
public static int up(int n,int m){
if(n<1||m<1)
return 0;
if(n==1||m==1)
return 1;
if(n<m)
return up(n,n);
if(n==m)
return up(n,n-1)+1;
return up(n,m-1)+up(n-m,m);
}
}
5、全排列问题
例1 字母全排列
问题:编写一个程序,使用递归算法输出一个一维字符数组中所有字符的全排列,假设字符都不一样。例如{‘a’,‘b’,‘c’}的全排列为(a,b,c), (a,c,b), (b,a,c), (b,c,a), (c,a,b), (c,b,a)。
package recursion;
import java.util.Scanner;
public class test7 {
public static void main(String args[]){
int n;
char[] list = new char[]{'a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p',
'q','r','s','t','u','v','w','x','y','z'};
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
allist(list,0,n);
}
}
public static void allist(char list[],int k,int n ){
int i ,j;
char t;
if(k==n){
for(i=0 ; i<n ; i++){
System.out.print(list[i]);
}
System.out.println();
}
for(j=k ; j<n ; j++){
{
t = list[k];
list[k] = list[j];
list[j] = t;
}
allist(list,k+1,n);
{
t = list[j];
list[j] = list[k];
list[k] = t;
}
}
}
}
例2 1-9加法
问题:使用1-9这9个数字,每个数字各出现一次,如何组成如下数学公式:□□□ + □□□ = □□□。即:组成三个三位数,其中两个数之和等于第三个数。
代码:略。
例3 九组数分数
问题:使用1-9这9个数字,每个数字各出现一次。如何组成一个分数,其值恰好为1/3?
package recursion;
import java.util.Scanner;
public class test9 {
static int a,b;
static int[] arr = new int[2];
static int c = 0;
public static void main(String args[]){
int list[] = {1,2,3,4,5,6,7,8,9};
Scanner sc = new Scanner(System.in);
a = list[0]*1000+list[1]*100+list[2]*10+list[3];
b = list[4]*10000+list[5]*1000+list[6]*100+list[7]*10+list[8];
alllist(list,0);
}
public static void alllist(int list[],int k){
if(k==list.length){
//a,b要在这里面赋值才有结果出来。
a = list[0]*1000+list[1]*100+list[2]*10+list[3];
b = list[4]*10000+list[5]*1000+list[6]*100+list[7]*10+list[8];
if(a*3==b){
System.out.println(a+"/"+b);//结果按顺序排,这里没排懒。
}
}
int i,t;
for(i=k ; i<list.length ; i++){
{
t = list[k];
list[k] = list[i];
list[i] = t;
}
alllist(list,k+1);
{
t = list[i];
list[i] = list[k];
list[k] = t;
}
}
}
}
6、补充
例1 递归求和
问题:使用递归编写一个程序,求以下数列的前n项:s=1-1/2+1/3-1/4+1/5-1/6+…
package recursion;
import java.text.DecimalFormat;
import java.util.Scanner;
//oj练习2D题
public class test4 {
public static void main(String args[]){
double n;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextDouble();
DecimalFormat df = new DecimalFormat("0.000000");//注意:精确到小数点后六位
System.out.println(df.format(sum(n)));
}
}
public static double sum(double n){
if(n==1)
return 1;
if(n>1)
return sum(n-1)+Math.pow(-1, n-1)*(1/n);
return 0;
}
}
例2 数字求和
问题:使用递归编写一个程序,计算一个正整数中所有数字之和。例如输入234,输出9。
package recursion;
import java.util.Scanner;
//oj练习2F题
public class test6 {
public static void main(String args[]){
int n;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
System.out.println(take(n));
}
}
public static int take(int n){
int s = n%10;
if(n<10)
return s;
if(n>=10)
return s+take(n/10);
return 0;
}
}
例3 倒序输出
问题:使用递归编写一个程序,逆序输出一个正整数。例如输入1234,输出4321(不含前导0)。
package recursion;
import java.util.Scanner;
public class test8 {
static int a = 0;
static int b = 0;
public static void main(String args[]){
int n;
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
n = sc.nextInt();
a = b = 0;
p(n);
System.out.println();
}
}
public static int p(int n){
int s = n%10;
if(n<10){
System.out.print(s);
}else{
if(s==0&&a==b){
a++;
b++;
}else{
b++;
System.out.print(s);
}
return p(n/10);
}
return 0;
}
}