leetcode刷题-递归(持续更新)
递归
什么是递归
一种计算过程,如果其中每一步都要用到前一步或前几步的结果,称为递归的。用递归过程定义的函数,称为递归函数,例如连加、连乘及阶乘等。凡是递归的函数,都是可计算的,即能行的 。
古典递归函数,是一种定义在自然数集合上的函数,它的未知值往往要通过有限次运算回归到已知值来求出,故称为“递归”。它是古典递归函数论的研究对象
简单来说就是一种反复调用自己来求解的一种算法。
递归题目
先写一些简单的题目来入门
题一:
有一对刚出生的兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数。
解题思想:
这是一道经典的递归算法题,但是我们可以不用递归的思想进行描述该题目,
我们建立一个二维数组第一个数字为成熟的兔子,第二个数字为未成熟的兔子。
public static int find(int month){
//定义的number二维数组 第一个数字为成熟的兔子
//number的第二个数字为未成熟的兔子
int number[][] = new int[2][100];
//先定义初始的兔子
number[0][0] = 0;
number[1][0] = 1;
number[0][1] = 0;
number[1][1] = 1;
number[0][2] = 1;
number[1][2] = 1;
for(int i =3;i<month ;i++){
//已成熟兔子上两次已成熟的兔子之和,这里的第一个为上上个月成熟的兔子,因为这个时候成熟的兔子产下的新兔子已经成熟,第二个数字为上次的已成熟的兔子
number[0][i]=number[0][i-2]+number[0][i-1];
//新出生的兔子为上次的老兔子和上次的新兔子
number[1][i]=number[0][i-1]+number[1][i-1];
}
//返回所有的兔子
return number[0][month-1]+number[1][month-1];
}
得到的结果为
1 1 2 3 5 8 13 21 34 55
我们可以查看规律,第三个月开始,每个月的兔子数量为上两个月的和
所以我们可以用递归写出新的算法
public static int find2(int month){
if(month==1 || month==2){
return 1;
}
return find2(month-1)+find2(month-2);
}
题二:
这也是一道基础且经典的算法题,解题思路也同样简单,但是还是有一些缺陷的,使用递归算法会相当耗时。
static int temp = 0;
public static void find(int n){
if(n<0)return ;
if(n==0)temp+= 1;
find(n - 2);
find(n -1);
}
所以我们可以使用斐波拉契的算法来解题
我们知道青蛙在上一个台阶的时候只会有两种方法来到这个台阶,一种是爬一个楼梯到这里,还有一种方法就是爬两个台阶到这里,所以到现在这个台阶的方法就是上一个台阶的总和 加上 上两个台阶的总和。
public static int find(int n){
int a = 1;
int b = 2;
int sum = a+b;
//循环n-1是因为从0开始
for(int i = 0 ; i<n-1 ;i++){
a = b;
b = sum;
sum = a+b;
}
//a才是当前台阶的总和
return a;
}
这个题目的整体思路就是,首先求出总和,排除掉总和%k不等于0的集合,并找到最大的值。
然后开始进行算法,首先准备一个长度为k的数组,其中每个值都总和除以k,从最大值开始循环找另一个值,如果他们相加大于总和/k,那么进行排除,开始进行下一个,如果加起来小于总和/k,那么就加起来再开始找下一个,如果一直找下一个都找不到,那么就开始下一个数字,如果这个数字找不到任何匹配都无法满足总和除以k那么就可以返回false了。
private boolean backtracking(int[] nums, int k, int target, int cur, int start, boolean[] used) {
// 返回条件
if (k == 0) return true;
if (cur == target) {
// 构建下一个集合
return backtracking(nums, k-1, target, 0, 0, used);
}
for (int i = start; i < nums.length; i++) {
if (!used[i] && cur+nums[i] <= target) {
used[i] = true;
if (backtracking(nums, k, target, cur+nums[i], i+1, used)) return true;
used[i] = false;
}
}
return false;
}
public boolean canPartitionKSubsets(int[] nums, int k) {
// 注意nums[i] > 0
int sum = 0, maxNum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
if (maxNum < nums[i]) maxNum = nums[i];
}
if (sum % k != 0 || maxNum > sum/k) return false;
boolean[] used = new boolean[nums.length];
return backtracking(nums, k, sum/k, 0, 0, used);
}
我们来看一道经典的算法题
class Solution {
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
hanoi(A.size(), A, B, C);
}
public void hanoi(int n, List<Integer> A, List<Integer> B, List<Integer> C){
if(n == 1){
C.add(A.get(A.size() - 1));
A.remove(A.size() - 1);
}else{
//把A经过辅助C放到B上
hanoi(n - 1, A, C, B);
//把A放到C上
C.add(A.get(A.size() - 1));
A.remove(A.size() - 1);
//把B经过辅助A放到C上
hanoi(n - 1, B, A, C);
}
}
}
你后面可以再回头看看这个题目,我就不写题解了,因为这个题必须要反复去阅读才能理解,一段时候后不去理解,全都忘了
这个题目看起来比较简单,其实和台阶问题属于同一类问题,但是为什么我要放在这里,是因为这个题目是不可以用递归进行解题的,会造成超时问题,所以这里我们用两种解法来解
法一:递归
Set set = new TreeSet<Integer>();
public static void test(int temp,int s,int l,int sum){
if(temp==0){
tree.add(sum);
}else {
test(temp-1,s,l,sum+s);
test(temp-1,s,l,sum+l);
}
}
法二:简化递归
我们可以把递归简化以下
public int[] divingBoard(int shorter, int longer, int k) {
// 木板数量小于1,无解
if (k < 1) {
return new int[]{};
}
// 两块板子长度一样只有一种解
if (shorter == longer) {
return new int[]{shorter * k};
}
int[] res = new int[k + 1];
// 枚举较长木板的数量,那么(k-m)即是短板的数量
for (int i = 0; i <= k; i++) {
res[i] = longer * i + shorter * (k - i);
}
return res;
}
这个算法比较简单,用了递归,但是某些测试用例是无法通过的,所以也记录下来
public static double pow(int n,double x,double temp){
if(n==0){
return x;
}else {
return pow(n-1,x*temp,temp);
}
}
public static double depow(int n,double x,double temp){
if(n==1){
return x;
}else {
return pow(n-1,x/temp,temp);
}
}
这个算法没搞明白,以后有机会再看看
public static double myPow(double x, int n) {
if(n == 0) return 1;
if(n == 1) return x;
if(n == -1) return 1 / x;
double half = myPow(x, n / 2);
double mod = myPow(x, n % 2);
System.out.println(half+" "+mod);
return half * half * mod;
}