1,什么是递归
自己调用自己,解决同一类问题
2,递归的代码实现(简单 -> 复杂)
package com.rojer.recursion;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 递归方法
*/
public class Factorial {
/**
* 使用递归计算阶乘 1*2*3*4*5
*
* @param n 进行几位的阶乘
*/
public static int factorial(int n) {
if (n == 0) {
return 1; // 基准条件
} else {
return n * factorial(n - 1); // 递归步骤
}
}
// public static void main(String[] args) {
// int result = factorial(5);
// System.out.println("5的阶乘 = " + result);
// }
/**
* 递归的二分查找法
*
* @param array 遍历的正序数组
* @param target 目标值
* @return 对应目标值在数组的下标
*/
public static int binarySearch(int[] array, int target) {
return factorial2(array, target, 0, array.length - 1);
}
private static int factorial2(int[] array, int target, int left, int right) {
if (left > right) {
return -1;
}
int mid = left + (right - left) / 2;
if (array[mid] == target) {
return mid;
} else if (array[mid] > target) {
return factorial2(array, target, left, mid - 1);
} else {
return factorial2(array, target, mid + 1, right);
}
}
// 测试方法
// public static void main(String[] args) {
// int[] array = {1, 3, 5, 7, 9, 11, 13, 15};
// int target = 11;
// int index = binarySearch(array, target);
//
// if (index != -1) {
// System.out.println("目标值 " + target + " 找到,索引位置为 " + index);
// } else {
// System.out.println("目标值 " + target + " 未找到");
// }
// }
/**
* 递归的冒泡排序法
*
* @param array 排序数组
*/
public static void bubbleSortByFactorial(int[] array) {
bubbleSort1(array, array.length - 1);
}
private static void bubbleSort1(int[] array, int index) {
if (index == 0) {
return;
}
int x = 0;
for (int i = 0; i < index; i++) {
if (array[i] > array[i + 1]) {
int temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
x = i;
}
}
bubbleSort1(array, x);
}
// public static void main(String[] args) {
// int[] array = { 2, 21, 13, 4, 45, 16, 7, 11, 9 };
// bubbleSortByFactorial(array);
// System.out.println(Arrays.toString(array));
// }
/**
* 冒泡排序基本写法
*/
public static void bubbleSort(int[] array) {
int n = array.length;
boolean swapped;
for (int i = 0; i < n - 1; i++) {
swapped = false;
for (int j = 0; j < n - 1 - i; j++) {
if (array[j] > array[j + 1]) {
// 交换相邻元素
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
swapped = true;
}
}
// 如果没有发生交换,说明数组已经有序
if (!swapped) break;
}
}
/**
* 插入排序之递归写法
*
* @param array
*/
public static void insertionSortByFactorial(int[] array) {
insertion(array, 1);
}
private static void insertion(int[] array, int index) {
if (index == array.length) {
return;
}
int t = array[index];
int i = index - 1; // 已排序区域指针
while (i >= 0 && array[i] > t) {
array[i + 1] = array[i];
i--;
}
if (i + 1 != index) {
array[i + 1] = t;
}
insertion(array, index + 1);
}
public static void main(String[] args) {
int[] array = {2, 21, 13, 4, 45, 16, 7, 11, 9};
insertionSortByFactorial(array);
System.out.println(Arrays.toString(array));
}
/**
* 插入排序基本写法
*/
public static void insertionSort(int[] array) {
int n = array.length;
for (int i = 1; i < n; ++i) {
int key = array[i];
int j = i - 1;
// 将大于key的元素向右移动一个位置
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j = j - 1;
}
array[j + 1] = key;
}
}
/**
* 斐波那契数列
*/
public static int fibonacci(int n) {
if (n <= 1) {
return n; // 基准条件
} else {
return fibonacci(n - 1) + fibonacci(n - 2); // 递归步骤
}
}
/**
* 优化后的斐波那契数列
*/
// 优化斐波那契数列的计算,可以使用记忆化技术来缓存已经计算过的值,从而避免重复计算。这种方法可以显著提高计算效率,
// 特别是在计算较大斐波那契数时。以下是使用Java实现记忆化斐波那契数列的代码示例。
// 使用HashMap来缓存已经计算过的斐波那契数
private static Map<Integer, Long> memo = new HashMap<>();
// 记忆化的斐波那契数列计算方法
public static long fibonacci2(int n) {
if (n <= 1) {
return n;
}
// 如果缓存中已经存在该值,则直接返回
if (memo.containsKey(n)) {
return memo.get(n);
}
// 递归计算斐波那契数,并将结果存入缓存
long result = fibonacci2(n - 1) + fibonacci(n - 2);
memo.put(n, result);
return result;
}
// 动态规划的斐波那契数列计算方法
public static long fibonacci3(int n) {
if (n <= 1) {
return n;
}
long[] fib = new long[n + 1];
fib[0] = 0;
fib[1] = 1;
for (int i = 2; i <= n; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
}
return fib[n];
}
/**
* 汉诺塔问题
*
* @param n 圆盘个数
* @param fromRod 源
* @param toRod 借
* @param auxRod 目
*/
public static void solveHanoi(int n, char fromRod, char toRod, char auxRod) {
if (n == 1) {
System.out.println("Move disk 1 from " + fromRod + " to " + toRod);
return;
}
solveHanoi(n - 1, fromRod, auxRod, toRod);
System.out.println("Move disk " + n + " from " + fromRod + " to " + toRod);
solveHanoi(n - 1, auxRod, toRod, fromRod);
}
}
3,递归的优缺点
优点
-
简洁和清晰:
- 递归使代码更容易理解和维护。对于某些问题(如树和图的遍历、汉诺塔问题、斐波那契数列等),递归的代码往往比迭代的代码更为直观和简洁 。
-
自然适应分治策略:
- 递归非常适合解决分治问题。许多经典的算法(如快速排序、归并排序等)通过递归方式来分解问题,简化了问题的解决过程 。
-
处理复杂数据结构:
- 递归在处理诸如树、图等复杂数据结构时非常高效。递归调用可以方便地遍历这些结构,无需显式使用栈或队列 。
缺点
-
性能问题:
- 递归调用会占用栈空间,对于深度递归,可能会导致栈溢出。特别是对于没有尾递归优化的语言,这个问题更加严重 。
-
潜在的效率低下:
- 在某些情况下,递归可能会导致重复计算,增加时间复杂度。例如,简单的递归计算斐波那契数列会导致大量重复计算,时间复杂度为指数级别O(2^n) 。通过记忆化或动态规划可以解决这个问题,但这些技术可能会增加实现的复杂性。
-
调试困难:
- 递归函数的调试相对复杂,特别是在递归层次较深时,理解和跟踪递归调用栈可能会变得困难 。
4,力扣中的递归题
简单:
104, 110, 112, 226, 617, 938
中等:
236, 322, 394, 437, 543, 654, 687
困难:
124,979