版权声明:本文为博主原创,未经博主允许不得转载。 https://blog.csdn.net/weixin_36904568/article/details/88741671
1. 递归
(一)递归与循环:
循环可以让性能变快,递归可以让程序容易理解。
(二)递归的特点:
- 子问题需与原始问题为同样的事,且更为简单;
- 不能无限制地调用本身,需有个出口,化简为非递归状况处理。
(三)递归的结构:
- 边界条件
- 递归前进段
- 递归返回段
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
(四)递归的本质:调用栈
- 在计算机中调用函数F1时,计算机将F1压入栈中,为F1分配内存用于存储变量。
- 在函数F1中调用函数F2时,计算机将F2压入栈中,为F2分配内存用于存储变量。此时的F1暂停执行,转而执行F2.
- 函数F2完成返回后,计算机将F2弹出栈,F1继续执行。
- 函数F1完成返回后,计算机将F1弹出栈,程序结束。
(五)递归求解的步骤:
- 找重复(在重复中找到一种划分,通过递推公式或者等价转化成子问题)
- 找变化的量(变化的量通常是转化成参数的)
- 找到出口(根据参数变化规律找到边界条件)
(六):递归解决的问题
单层递归 —— n的阶乘问题
- 找重复:不断相乘
- 找变化:n会不断减1
- 找出口:n = 1
public static int fac(int n){
if(n == 0)
return 0;
if(n == 1)
return 1;
return n*fac(n-1);
}
双分枝递归 —— 斐波那契数列:后一项为前两项的和的数列
- 找重复:不断对两个数求和
- 找变化:n不断减1和减2,找前两个数
- 找出口:n = 0
public static int fib(int num){
if(num == 1)
return 1;
if (num == 0)
return 0;
return fib(num-1)+fib(num-2);
}
双分枝递归 —— 上楼梯问题:
楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,共有多少种不同的走法?
- 找重复:走楼梯每次都会上台阶
- 找变化:每上台阶,剩余台阶数会减少
- 找出口:当走完台阶时结束
//走楼梯
public static int walkUp(int all){
if(all <= 0 ) //没有台阶
return 0;
if(all == 1) //有一个台阶,只有1种走法
return 1;
if (all == 2) //有两个台阶,可以走一步,也可以分别走两步
return 2;
return walkUp(all - 1)+walkUp(all -2); //有多个台阶,则考虑走一个台阶的方法和走两个台阶的方法的并集
}
汉诺塔问题
有a、b、c三个塔,初始状态是:在 a 塔上放了 n 个圆盘从上到下,从小到大。(大盘不允许放在小盘上)要求把 a 塔上的盘子放到 c 塔上。每次只能拿一个盘子。
解决:首先以c塔为中介,将前n-1个圆盘从a塔挪到b塔上。再将第n个圆盘从a移动到c塔上,最后以a塔为中介,将b塔上的n-1个圆盘移到c塔上。
- 找重复:不断的把圆盘,从一个塔移动到另一个塔(先从起点塔移到中间塔,再从中间塔移到终点塔)。
- 找变化:圆盘数量n不断减1
- 找出口: 当圆盘n = 1,移动最后一个盘
public static void move(int n,char from, char temp,char to){
if(n == 1) //移最后一个,把圆盘从A移到C
{
System.out.printf("move the %d :%s-->%s%n",n,from,to);
}
else
{
move(n-1,from,to,temp); //把A塔上编号1~n-1的圆盘移到B上,以C为辅助塔
System.out.printf("move the %d :%s-->%s%n",n,from,to); // 把第n个圆盘从A移到C
move(n-1,temp,from,to); //把B塔上编号1~n-1的圆盘移到C上,以A为辅助塔
}
}
2. 分治法
(一)分治的特点:
- 原问题可以被分解成性质类似的子问题
- 子问题互相独立,不存在依赖关系
- 子问题的解能够协助原问题得到最终解
(二)分治的思想:分解
- 找出简单的基线条件,即把问题进行极限化。
- 通过递归不断将问题分解,缩小规模,直到找到符合的条件,也就是找到问题的极限。
(三)分治法求解的步骤
- 划分问题:整个问题划分成多个性质类似却互相独立的子问题。
- 递归求解:递归调用求解各个子问题。
- 合并问题:合并子问题的解,形成原始问题的解。
(四)分治法解决的问题
计算一组数的和
- 划分问题:要求一组数的和,把这组数分为一个数和一部分数求和
- 递归求解:不断把一部分数分解,直到只剩一个数,它就是这组数的和
- 合并问题:把所有组的数加起来就是结果
//通过分治法计算一组数的和:如果数组中只有一个元素,那么它就是数组的和
public static int sum(int[] arr){
if(arr.length==1) //递归结束的条件,数组只有一个元素
return arr[0];
else //通过递归缩小数组的规模
return arr[arr.length-1]+sum(Arrays.copyOf(arr,arr.length-1));
}
求一组数的最大值
- 划分问题:要求一组数的最大值,把这组数分为一个数和一组数比较
- 递归求解:递归把剩下的一部分数分解,直到只剩一个数,它自己就是最大值
- 合并问题:把所有组的数互相比较就是结果
//使用分治法找出一组数的最大值:如果数组中只有一个元素,则它自己就是最大值
public static int find_max(int[] arr){
if(arr.length==1) //递归结束的条件:数组只有一个元素
return arr[0];
else //通过递归缩小规模
return Math.max(arr[arr.length-1],find_max(Arrays.copyOf(arr,arr.length-1)));
}
二分法:
- 划分问题:通过中间值把记录分为两部分,分别找关键字。
- 递归求解:不断把记录的记录分为两部分,找关键字。
- 合并问题:在最后的记录中找到问题的解
public static int binart_search(int[] arr,int key){
int index = -1,low = 0,high = arr.length-1,mid = 0;
while (low<=high)
{
mid = (low+high)/2; //中间值
if(arr[mid]>key) //比中间的小,修改high
high = mid-1;
else if(arr[mid]<key) //比中间的大,修改low
low = mid+1;
if (arr[mid]==key) //命中
{
index = mid;
break;
}
}
return index;
}
找硬币问题:
一共有m枚硬币,已知其中有一枚是假的,它比其他m-1枚都要轻。现在有一个天平,称多少次可以找到这枚硬币?
- 划分问题:把硬币堆分为3部分,分别称硬币堆。
- 递归求解:不断把硬币堆分为3部分,分别称硬币堆。
- 合并问题:如果天平平衡,那么假币一定在没称的那一堆里,如果天平不平衡,那么假币就在天平高起的那一堆里。