递归的定义
递归(recursion)是一种解决问题的有效方法,在递归过程中,函数将自身作为子例程调用。
递归的思想是把一个大型复杂问题层层转化为一个与原问题规模更小的问题,问题被拆解成子问题后,递归调用继续进行,直到子问题无需进一步递归就可以解决的地步为止。
找到数组中的最大值
函数演示
public class GetMax{
public static int getMax(int[] arr){
return process(arr, 0, arr.length - 1);
}
//arr[L...R]范围上求最大值
public static int process(int[] arr, int L, int R){
if (L == R){//arr[L...R]范围上只有一个数,直接返回, base case
return arr[L];
}
int mid = L + ((R - L) >> 1); //中点
int leftMax = process(arr, L, mid);
int rightMax = process(arr, mid + 1, R);
return Math.max, RightMAX);
}
}
取数组中点时防止溢出
取数组L和R位置的中点
一般思路:
mid = (L + R) / 2;
分析:如果R足够大,那(L+R)就会溢出,得到的结果为负。
所以为了防止溢出,可以采用下面的方法:
mid = L + (R - L) / 2;
因为R未溢出,L也未溢出,且R>L,所以(R-L)也未溢出。
此方法可有效的防止溢出。
当然此方法可以变为:
mid = L + ((R - L) >> 1);
通过位运算以提高运算速度。
理解递归
画图理解
其实上面找数组中最大值的过程我们可以用画图来理解
通过把大的问题层层分解为一个个的小问题进行解决,并且在计算的过程中我们会发现,我们总是在产生子问题的时候优先进行子问题的计算,也就是说把母问题压入栈中,等到最小单元的问题得到解决后,在层层解决之前存入的更大单元的问题。
递归两个属性
任何递归必须具备以下两个属性
基本情况
基本情况(bottom cases),基本情况用于保证程序调用及时返回,不在继续递归,保证了程序可终止。
递推关系
递推关系(recurrentce relation),可将所有其他情况拆分到基本案例。
递归的优点
优雅性
相比其他解法(比如迭代法),使用递归法,你会发现只需少量程序就可描述出解题过程,大大减少了程序的代码量,而且很好理解。递归的能力在于用有限的语句来定义对象的无限集合。
反向性
由于递归调用程序需要维护调用栈,而栈(具有后进先出的特征,因此递归程序适合满足取反类需求。比如字符串取反,链表取反等相关有趣的算法问题。
递推关系
递归程序可以较明显的发现递推关系,反过来也可以这么说,具有递推关系的问题基本都可以通过递归求解(当然也许有性能更佳的解法,但递归绝对是一种选择)。递推关系常见问题有杨辉三角、阶乘计算
递归的时间复杂度估计
利用master公式进行递归的时间复杂度的估计
其中a为子问题的数量,N/b为子问题占母问题的规模,O(N^d)为其它所有的计算的时间复杂度。
满足子问题等规模的递归问题,则可以使用master公式计算时间复杂度
在知晓参数a,b,d的情况下,计算的结果有且仅有以下三种情况:
待续