递归
- 我的github博客地址: fanfan999的github博客,可以通过github博客联系我哈哈哈.
两个小例子:
- 举例1: 在一个数组中找到最大值
- 我先找
左边最大值
和右边最大值
(以下标中间为界),两者相比较,较大的就是全局最大值 - 我直接贴代码了哈:
//结束条件 if (L >= R) { return array[L]; } //找到中间位置,划分为左右两边 int mid = (L + R) / 2; //左边最大值 int maxLeft = getMax(array, 0, mid); //右边最大值 int maxRight = getMax(array, mid + 1, R); //返回左右两边最大值 return Math.max(maxLeft, maxRight);
- 我先找
- 怎么理解呢?
- 我画了个图:
- 我画了个图:
- 举例2:冒泡排序的递归实现:
- 因为冒泡排序的原理就是
每一圈
都让数组中的前后两个元素比较,大的放后面,小的放前面,所以我们这里采用递归就要分为两步:- 说明,这里的start第一圈就是下标0,end在第一圈就是数组长度
- 每一圈递归,其实就是遍历数组,让其两两比较,代码如下:
if (start < end - 1) { if (array[start] > array[start + 1]) { swap(start, start + 1, array); } bubbleSort(array, start + 1, end); } else { return; }
- 让数组不断变成一个更小的数组(
因为每一圈结束最后一个都是最大的
),可以理解为递归圈数,代码如下:
bubbleSort(array, start, end - 1);
- 完整代码我放github了,要的话自己可以看看:***冒泡排序的递归版***
- 这个就拿来当作理解就可以了,正式用的时候还是循环,不要用递归!不要用递归!不要用递归!
- 因为冒泡排序的原理就是
- 注意:
- 任何递归行为都可以变成非递归.
- 递归实际上就是在不停的压栈弹栈,很浪费空间.
- 循环基本都可以变递归,不过不建议使用递归,递归容易出问题,还不好理解,占用空间还多,除了代码简练一点.
递归时间复杂度怎么分析:
- 看这样一个用于估计递归时间复杂度一个通式(叫做
master
公式):T(N) = a * T(N / b) + O(N^d)
.- 这个式子是用做第一次划分的过程的时候的,后面的划分不用管.
- 其中,
T(N)
表示样本量为N的情况下的时间复杂度,你就理解为T
就是Time
的缩写;a
表示子过程的次数;N / b
表示子过程的样本量;O(N^d)(这是N的d次方哈)表示除了调用子过程以后所需要的时间复杂度
,下面我们以求最大值为例进行分析. - 由于我们把整个数组划分为了
左右两个部分
,两边的样本量都是(N/2)
,所以说左边的时间复杂度就是T(N/2)
,而又有左右两个部分,所以一共要两个T(N/2)
的时间复杂度,即2*T(N/2)
. - 除了调用子过程意外,我们只需要对数组中元素两两比较,这个是常数级别的操作,所以最后一项
O(N^d)
为O(1)
. - ***从这里我们可以看出,上述求最大值的时间复杂度为**T(N) = 2T(N/2) + O(1),就相当于
a=2,b=2,d=0
***.
- master的公式是有适用范围的:那就是划分子过程的时候,划分的
子过程的规模
必须是一样的,才可以用master公式.
- 对满足这个公式的算法有几个快捷使用公式:
- log(b,a) > d --> 复杂度为O(N^log(b,a)).
- log(b,a) == d --> 复杂度为O(N^d * logN),(
logN
表示以10为底). - log(b,a) < d --> 复杂度为O(N^d).
- 注意:
- 这里的
log(b,a)
都是表示以b为底,a为指数,读作以b为底的a的对数
. - 以1为底的任何数的对数都是0.
- 1的对数永远是0.
- 这里的
- 以求最大值为例进行分析:
- 最大值的时间复杂度为T(N) = 2*T(N/2) + O(1),就相当于
a=2,b=2,d=0
,所以log(b,a) == log(2,2)
,其值为1
,是大于d=0
的,所以其具体复杂度为O(N^log(b,a)) => O(N^1) == O(N)
.
- 最大值的时间复杂度为T(N) = 2*T(N/2) + O(1),就相当于