什么是递归?
在任何编程中,凡是有一个函数或者方法自己调用自己,并且在具备一定条件后,可以正常退出,那么这就叫递归。递归其实和软件工程中的分治思想有异曲同工之妙。把大问题,拆分成小问题,然后再逐一解决汇总。但是递归往往具备一个特征,就是被拆分子任务的逻辑都是相似的。递归需要开辟栈空间,我之前的文章有写到,栈相当于弹夹,如果你的逻辑有bug,很有可能导致卡壳无法弹出子弹导致StackOverflow。当然,递归不一定是最优解,任何递归都可以不用递归实现程序逻辑,甚至在某些情况下可能优于递归。
递归思想
这里参考左程云老师左神根据求某个数组某个范围上最大值来说明最简单的递归思想:
public static int process(int[] arr, int L, int R) {
if (L == R) {
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(leftMax, rightMax);
}
- 相似的子任务
上图函数f,传递数组范围L,R也就是数组index为0~3的范围查找最大值并返回。首先 int mid = L + ((R - L) >> 1);找到数组中的中间位置的下标1,分为左右两对index分别获取最大值并比较。然后拆分为相似的子任务,那么就是不断的取mid中间位置,直到相等,没有可拆分的子任务为止,然后一个子任务一个子任务求出左右最大值比较返回最终最大值。这不就是类似于分治思想吗。
- 递归在栈的实现
每个拆分的子任务的变量,执行的方法压入栈中,直到求出结果,弹出。这就是为什么只要条件满足,那么递归一定要停止的原因,否则就是无限压栈,导致栈溢出。
时间复杂度
这里的时间复杂度为O(N),你可以看到Max、mid、全都是常数级别操作,那么复杂度取决于子任务中是否含有其他非常数级别操作。
这里a 为子任务的规模次数,T(N/b)为规模大小,O(N^d)为额外时间复杂度。那么本次举例的递归首先满足这个公式,子任务规模为2,规模大小为N/2,子任务没有任何额外复杂度计算,全是常数级别操作,所以a=2,b=2,d=0,那么复杂的符合第一个复杂度。