递归思想求arr中最大值,递归函数先调用处理几部分,然后整理和归并信息返回

递归思想求arr中最大值,递归函数先调用处理几部分,然后整理和归并信息返回

提示:递归思想,非玄学,必定要学会的递归思想,这是极其重要的算法范畴


递归思想:递归函数

递归函数f(arr,L,R):
最简单的二分递归:把arr(N长度)的L–R范围分为2部分,每一个部分数据规模是N/2
(1)先去递归调用左边部分:f(arr,L,mid),得到一个结果x1
(2)再去递归调用右边部分:f(arr,mid+1,R),得到一个结果x2
(3)最后,整理和归并x1和x2,得到f(arr,L,R)的结果,返回
(4)当L=R时,就剩一个元素,返回仅有一个元素时应该返回的结果,这就是base case:基本状况。
在这里插入图片描述


一、举例:

求无序arr中的最大值
示例:3 2 1 5 4 2 6
返回最大值:6


二、解题

暴力索引

直接o(n)索引找,也是可以的

递归寻找

当然,递归找找试试:
按照上面的递归思想,分2部分!
f(arr,L,R)代表,请寻找并返回arr中L–R上的最大值!
在这里插入图片描述
(1)mid=0+6/2=3,递归掉用f(0–3),f(4–6)
(2)在f(0–3)这里,又将其分为2部分:mid=0+3/2=1,递归掉用f(0–1),f(2–3)
与此同时,f(4–6)又可以分为2部分:mid=4+6/2=5,递归调用f(4,5),f(6,6)
(3)显然,此时f(6,6)满足了基本条件L=R,故返回一个最大值,自然也就是它本身:f(6,6)=6;
(4)继续划分:
f(0–1)可以划分为f(0,0)=3,f(1,1)=2;
f(2–3)可以继续划分为f(2,2)=1,f(3,3)=5;
f(4,5)可以继续划分为f(4,4)=4,f(5,5)=2;
目前他们全部都是到了叶节点了
也都拿到了各自最大值,开始返回结果:
(5)f(0–1)=max(f(0,0)=3,f(1,1)=2)=3
f(2–3)=max(f(2,2)=1,f(3,3)=5)=5
f(4,5)=max(f(4,4)=4,f(5,5)=2)=4
继续返回整合:
f(0–3)=max(f(0–1),f(2–3))=3
f(4–6)=max(f(4,5),f(6,6))=6
最终:
f(0,6)=max(f(0–3),f(4–6))=6
这就是递归之后的结果
在这里插入图片描述
手撕代码:

//复习:递归寻找最大值
    public static int maxVal(int[] arr, int L, int R){
        //base case
        if (L == R) return arr[L];//就本身

        //L<R:左右递归调用2次
        int mid = L + ((R - L) >> 1);//中点
        int leftMax = maxVal(arr, L, mid);
        int rightMax = maxVal(arr, mid + 1, R);

        //整理归并返回
        return Math.max(leftMax, rightMax);
    }

    public static void test2(){
        int[] arr = {3,2,1,4,5,6};
        int ans = maxVal(arr, 0, arr.length - 1);
        System.out.println(ans);
    }

    public static void main(String[] args) {
        test2();
    }

结果:6

递归思想的时间复杂度

根据上面的例子:
不妨设,一个递归函数,里面要调用几次?a=2次
每一次调用递归,其数据规模为N/b,这里b=2,因为每次都是均分的
除了两次递归调用意外的操作,max整理归并返回的复杂度为o(n^d),这里d自然是0,因为max操作就o(1)
则,我们得到递归函数的时间复杂度计算公式T(n)

T(n)=aT(N/b)+o(n^d)

最终:
Master复杂度公式
死记硬背!!!
(1)log(b,a) > d,则T(n)=o( n^(log(b,a) )
(2)log(b,a) = d,则T(n)=o( n^d * log(n) )
(3)log(b,a) < d,则T(n)=o( n^d )
我们用一个直接点的,看图:
在这里插入图片描述
总之,log(b,a)越大,d越小,复杂度越小,
log(b,a)越小,d越大,复杂度越大,
log(b,a)是中间值,则复杂度中间带log(n)
所以master公式与log(b,a)成反比,与d成正比

比如:此题:
a=2,【这是递归调用的次数】
b=2,【每次递归调用问题的规模是N/2,b=2】
d=0,max归并整理操作只需要o(1),所以o(n^d)=o(n0)=o(1)
我们求一下:
log(b,a)=log(2,2)=1
显然,log(b,a)=1
>d=0
d小,复杂度就小
T(n)= o( n^(log(b,a) ) = o(n
1)=o(n)
和暴力索引一样,但是我们这里就是为了将递归思想的。

其实,常规情况下,无论abd怎么取值,无非就三种情况:
在这里插入图片描述
上面的情况1已经有了a=2,b=2,d=0
上面的情况2:那就不妨设log(b,a)=d,也就是说,a=2,b=2,d=1
d=1意味着递归最后还有一个o(n)的遍历骚操作【因为o(n^d)=o(n)】
则T(n)= o( n^d * log(n) ) = o( n * log(n) )
上面的情况3 ,log(b,a)<d,也就是说,a=2,b=2,d=2
d=2意味着递归最后还一个o(n^2)的骚操作,那挺狗的,复杂度就很高了
T(n)=o( n^d ) = o(n**2)

因此呢,大致就记住这三种情况,很少有例外,有的话,我们再说!


总结

提示:重要经验:

1)递归思想并非玄学,其实就是分块去求结果,最后归并整理信息返回
2)Master公式要牢记,并理解常用的3中时间复杂度

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
一、设计分析 最大子段和问题是一个经典的动态规划问题,其解法有多种,其包括使用归并排序的方法。本文将介绍使用归并排序解决最大子段和问题的算法。 该算法的基本思路是将原问题分解成若干个子问题,对每个子问题解,然后将这些子问题的解合并起来得到原问题的解。具体来说,将原数组分成两部分,分别出左右两部分的最大子段和,然后出跨越点的最大子段和,最终得到整个数组的最大子段和。 二、算法描述与程序 算法描述如下: 1. 定义函数 `MaxSubArraySum(arr, left, right)`,其 `arr` 表示待解的数组,`left` 和 `right` 分别表示数组的左右边界。 2. 如果 `left == right`,则返回 `arr[left]`。 3. 计算点 `mid = (left + right) / 2`。 4. 分别递归解左半部分和右半部分的最大子段和,即 `leftSum = MaxSubArraySum(arr, left, mid)` 和 `rightSum = MaxSubArraySum(arr, mid+1, right)`。 5. 跨越点的最大子段和,从点向左扫描左半部分的最大后缀和 `leftMax = arr[mid]`,从点向右扫描右半部分的最大前缀和 `rightMax = arr[mid+1]`,然后将它们相加得到跨越点的最大子段和 `crossMax = leftMax + rightMax`。 6. 返回 `leftSum`、`rightSum` 和 `crossMax` 三者的最大值。 Python 代码实现如下: ```python def MaxSubArraySum(arr, left, right): if left == right: return arr[left] mid = (left + right) // 2 # 计算左半部分的最大子段和 leftSum = MaxSubArraySum(arr, left, mid) # 计算右半部分的最大子段和 rightSum = MaxSubArraySum(arr, mid+1, right) # 计算跨越点的最大子段和 leftMax = arr[mid] tmp = 0 for i in range(mid, left-1, -1): tmp += arr[i] leftMax = max(leftMax, tmp) rightMax = arr[mid+1] tmp = 0 for i in range(mid+1, right+1): tmp += arr[i] rightMax = max(rightMax, tmp) crossMax = leftMax + rightMax # 返回三者的最大值 return max(leftSum, rightSum, crossMax) ``` 三、测试分析与总结 为了测试算法的正确性和效率,本文使用了两个测试用例,分别是: 1. `[-2, 1, -3, 4, -1, 2, 1, -5, 4]`,预期结果为 `6`。 2. `[1, -2, 3, 10, -4, 7, 2, -5]`,预期结果为 `18`。 对于这两个测试用例,算法都能够正确地出最大子段和,并且在较短的时间内完成计算。具体运行时间取决于数组的长度和计算机的性能,但是由于归并排序的时间复杂度为 $O(n\log n)$,因此该算法的时间复杂度也为 $O(n\log n)$。此外,该算法还需要使用 $O(\log n)$ 的空间来保存递归调用的栈帧。 总的来说,使用归并排序解决最大子段和问题是一种简单而高效的算法,它可以在较短的时间内计算出最大子段和,并且时间复杂度为 $O(n\log n)$,空间复杂度为 $O(\log n)$。然而,它的实现较为复杂,需要对递归、边界条件和数组下标等细节进行处理,因此在实际应用需要进行仔细的调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值