一.定义
计算机科学中,递归是一种解决计算问题的方法,其中解决方案取决于同一类问题的更小子集。
比如单链表递归遍历的例子:
void f(Node node){
if (node == null){
return;
}
f(node.next);
}
说明:
1.自己调用自己,如果说每个函数对应着一种解决方案,自己调用自己意味着解决方案是一样的(有规律的)
2.每次调用,函数处理的数据会比较上次缩减(子集),而且最后会缩减至无需继续递归
3.内层函数调用(子集处理)完成,外层函数才能算调用完成
二:思路
1.确定能否使用递归求解
2.推导出递推关系,即父问题与子问题的关系,以及递归的结束条件
注:深入到最里层叫做递,从最里层出来叫做归,在递的过程中,外层函数内的局部变量(以及方法参数)并未消失,归的时候还可以用到。
三:例题
1.求阶乘
private static int f(int n){
if (n == 1){
return 1;
}
return n*f(n-1);
}
2.反向打印字符
private static void f(int n,String str){
if (n == str.length()){
return;
}
f(n+1,str);
System.out.println(str.charAt(n));
}
3.二分查找
public int f(int[]a,int target,int i,int j){
if (i>j){
return -1;
}
int m = (i+j)>>>1;
if (target < a[m]){
return f(a,target,i,m-1);
} else if (a[m] < target) {
return f(a,target,m+1,j);
}else {
return m;
}
}
4.冒泡排序
//j代表未排序区域右边界
private void bubble(int[] a,int j){
if (j == 0){
return;
}
for (int i = 0;i < j;i++){
if (a[i] > a[i+1]){
int t = a[i];
a[i] = a[i+1];
a[i+1] = t;
}
}
bubble(a,j-1);
}
优化版:当某一次递归后所有值已经有序,就不需要再进行接下来的代码了
//j代表未排序区域右边界
//x是i在交换前i对应的位置,若未发生交换,则x不变,则x右边值都是有序的
private void bubble(int[] a,int j){
if (j == 0){
return;
}
int x = 0;
for (int i = 0;i < j;i++){
if (a[i] > a[i+1]){
int t = a[i];
a[i] = a[i+1];
a[i+1] = t;
x = i;
}
}
bubble(a,x);
}
5.插入排序
//low为未排序区域的左边界
private void insertion(int[] a,int low){
if (low == a.length){
return;
}
int t = a[low];//low位置的值
int i = low-1; //已排序区域指针
while(i >= 0 && a[i] > t){ //寻找小于t的第一个位置,没有找到插入位置
a[i+1]=a[i]; //空出插入位置,即将a[i]值赋予low,直到找到插入位置
i--;
}
//找到插入位置
if (i+1 != low){ //例如2 3 4 要插入4时,已经是有序的,不需要再赋值了
a[i+1]=t;
}
insertion(a,low+1);
}
6.斐波那契数列
(1)代码实现
public int f(int n){
if (n == 0){
return 0;
}
if (n == 1){
return 1;
}
int x = f(n-1);
int y = f(n-2);
return x+y;
}
(2)变体一:兔子问题
(3)变体二:青蛙爬楼梯
(4)优化版:记忆法
public int fibonaci(int n){
int[] cache = new int[n+1];//若n=5,则f(5)需存入cache[5],从0开始需要6个长度
Arrays.fill(cache,-1);//[-1,-1,-1,-1,-1,-1]
cache[0] = 0;
cache[1] = 1;//[0,1,-1,-1,-1,-1]
return f(n,cache);
}
public int f(int n,int[] cache){
if (cache[n] != -1){ //先在数组里找值
return cache[n]; //找到直接返回
}
int x = f(n-1,cache);
int y = f(n-2,cache);
cache[n] = x + y;//[0,1,?,-1,-1,-1]存入数组
return cache[n];
}
7.杨辉三角
//算出数字
private static int element(int i,int j){
if (j == 0 || i == j){
return 1;
}
return element(i-1,j-1)+element(i-1,j);
}
//打印空格
private static void printSpace(int n,int i){
int num = (n-1-i)*2;
for (int j=0;j<num;j++){
System.out.print(" ");
}
}
//打印数字
public static void print(int n){
for (int i=0;i<n;i++){
printSpace(n,i);
for (int j=0;j<=i;j++){
System.out.printf("%4d",element(i,j));
}
System.out.println();
}
}
8.杨辉三角优化:记忆法
提前把值存入数组,就不需要重复计算
//算出数字
private static int element(int[][] triangle,int i,int j){
if (triangle[i][j]>0){//先看数组里是否存在,开始时每个数都是0,若是有值则大于0
return triangle[i][j];
}
if (j == 0 || i == j){
triangle[i][j]=1;
return 1;
}
triangle[i][j] = element(triangle,i-1,j-1)+element(triangle,i-1,j);
return triangle[i][j];//将值存入数组
}
//打印空格
private static void printSpace(int n,int i){
int num = (n-1-i)*2;
for (int j=0;j<num;j++){
System.out.print(" ");
}
}
//打印数字
public static void print(int n){
int[][] triangle = new int[n][];//每行有多少列不确定
for (int i=0;i<n;i++){ //行
triangle[i] = new int[i+1];//确定列,根据规律可得j=i+1,从第0行开始算
printSpace(n,i);
for (int j=0;j<=i;j++){
System.out.printf("%4d",element(triangle,i,j));
}
System.out.println();
}
}