1、基本介绍:【递归用的是数学归纳法】
- 递归可以用来解决阶乘、斐波那契数列、汉诺塔、杨辉三角的存取、字符串反转、字符串全排列、二分查找、树的深度求解在内的八个经典问题。
- 递归函数就是直接或间接调用自身的函数,也就是自身调用自己,并且有去(递去)有回(归来)。
- “有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。
- “有回”是指 : 这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。
2、递归的三要素:
- 明确递归终止条件;
- 给出递归终止时的处理办法;
- 提取重复的逻辑,缩小问题规模。
3、递归与循环的区别:
- 单从算法设计上看,递归和循环并无优劣之别。然而,在实际开发中,因为函数调用的开销,递归常常会带来性能问题,特别是在求解规模不确定的情况下;而循环因为没有函数调用开销,所以效率会比递归高。
- 递归求解方式和循环求解方式往往可以互换。
- 递归转换为循环的步骤:
○ 自己建立“堆栈(一些局部变量)”来保存这些内容以便代替系统栈,比如树的三种非递归遍历方式;
○ 把对递归的调用转变为对循环处理。
4、经典的递归算法:
4.1、阶乘问题:
public class Factorial {
public static void main(String[] args) {
System.out.println(factorial_recursive(9));
System.out.println(factorial_loop(9));
}
/**
* 递归解决
* @param n 阶乘数目
* @return
*/
public static long factorial_recursive(int n) {
if (n == 1) { // 递归终止条件
return 1; // 简单情景
}
return n * factorial_recursive(n - 1); // 相同重复逻辑,缩小问题的规模
}
//--------------------------------我是分割线-------------------------------------
/**
* 循环解决
* @param n 阶乘数目
* @return
*/
public static long factorial_loop(int n) {
long result = n; //建立局部变量
while (n > 1) {
n--;
result = result * n;
}
return result;
}
}
4.2、斐波纳契数列 :【黄金分割数列】
- 指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
- 在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*),这个数列从第3项开始,每一项都等于前两项之和。
- 这里面包含了许多重复计算,所以需要重新找规律进行优化处理,防止重复计算:以1,1开头的斐波那契数列的第六项正是以1,2开头的斐波那契数列的第五项,而以1,2开头的斐波那契数列的第五项也正是以2,3开头的斐波那契数列的第四项,……..,以此内推。时间复杂度是O(n)。
public class FibonacciSequence {
public static void main(String[] args) {
System.out.println(fibonacci_recursive(10));
System.out.println(optimizeFibonacci_recursive(1, 1, 10));
System.out.println(fibonacci_array(10));
}
/**
* 普通递归解法,未考虑到重复计算的优化
*
* @param n
* @return
*/
public static int fibonacci_recursive(int n) {
if (n > 0) {
if (n == 1 || n == 2) {
return 1;
}
return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2);
}
return -1;
}
//--------------------------------我是分割线1-------------------------------------
/**
* 对普通递归法的优化,时间复杂度是O(n)
*
* @param first 数列的第一项
* @param second 数列的第二项
* @param n 目标项:我们想求的以first, second开头的数列的第几项
* @return
*/
public static int optimizeFibonacci_recursive(int first, int second, int n) {
if (n > 0) {
if (n == 1) { // 递归终止条件
return first; // 简单情景
} else if (n == 2) { // 递归终止条件
return second; // 简单情景
} else if (n == 3) { // 递归终止条件
return first + second; // 简单情景
}
return optimizeFibonacci_recursive(second, first + second, n - 1); // 相同重复逻辑,缩小问题规模
}
return -1;
}
//--------------------------------我是分割线2-------------------------------------
/**
* 循环解法
*
* @param n
* @return
*/
public static int fibonacci_loop(int n) {
if (n == 1 || n == 2) {
return 1;
}
int result = -1;
int first = 1; // 自己维护的"栈",以便状态回溯
int second = 1; // 自己维护的"栈",以便状态回溯
for (int i = 3; i <= n; i++) { // 循环
result = first + second;
first = second;
second = result;
}
return result;
}
//--------------------------------我是分割线3-------------------------------------
/**
* 使用数组存储斐波那契数列
*
* @param n
* @return
*/
public static int fibonacci_array(int n) {
if (n > 0) {
int[] arr = new int[n]; // 使用临时数组存储斐波纳契数列
arr[0] = arr[1] = 1;
for (int i = 2; i < n; i++) { // 为临时数组赋值
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n - 1];
}
return -1;
}
}
4.3、杨辉三角的取值:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
- 杨辉三角形第n层(顶层称第0层,第1行,第n层即第n+1行,此处n为包含0在内的自然数)正好对应于二项式〖(a+b)〗^2 的展开系数。
- 例如第二层1 2 1是幂指数为2的二项式〖(a+b)〗^2 展开形式a^(2 )+2ab+b^2 的系数。
public class PascalTriangle {
public static void main(String[] args) {
System.out.println(getValue(6,3));
}
/**
* 递归解法
*
* @param x 指定行
* @param y 指定列
* @return
*/
public static int getValue(int x, int y) {
if (y <= x && y >= 0) {
if (y == 0 || x == y) { // 递归终止条件
return 1;
} else {
return getValue(x - 1, y - 1) + getValue(x - 1, y);
}
}
return -1;
}
}