https://leetcode-cn.com/problems/maximum-subarray/solution/zui-da-zi-xu-he-by-leetcode-solution/
今日从零开始刷到求数组中的最大连续子序和,我的思路是动态规划,求带最后一位的最大连续子序和,最终对比找到最大值。
官方题解提到了另一种分治法,引申出线段树的概念
大致思想是 分段递归,求四个关键的参数进行对比求最大。
首先是数组的区间[l,r],其次取分治的中间点m,分成两段[l,m] 和 [m+1,r]
对于一个区间 [l,r],我们可以维护四个量:
lSum 表示 [l,r] 内以 l 为左端点的最大子段和
rSum 表示 [l,r] 内以 r 为右端点的最大子段和
mSum 表示 [l,r] 内的最大子段和
iSum 表示 [l,r] 的区间和
所以问题就变为,我要求[l,r]的mSum,那么这个数据只有两种情况,要么是最长子序段不跨m也就是
max { [l,m]的mSum , [m+1,r]的mSum };要么是跨m也就是 [l,m]的rSum + [m+1,r]的lSum。所以最后的实现也就变为递归,到最底的[l,l]长度为1,也就是四个关键参数都相等时。往上return最后得出[l,r]的四个关键参数,最后比较大小即可。
那么线段树又是什么?这里官方最后给了段题外话,如下:
题外话
「方法二」相较于「方法一」来说,时间复杂度相同,但是因为使用了递归,并且维护了四个信息的结构体,运行的时间略长,空间复杂度也不如方法一优秀,而且难以理解。那么这种方法存在的意义是什么呢?
对于这道题而言,确实是如此的。但是仔细观察「方法二」,它不仅可以解决区间[0,n−1],还可以用于解决任意的子区间[l,r] 的问题。如果我们把[0,n−1] 分治下去出现的所有子区间的信息都用堆式存储的方式记忆化下来,即建成一颗真正的树之后,我们就可以在 O(logn) 的时间内求到任意区间内的答案,我们甚至可以修改序列中的值,做一些简单的维护,之后仍然可以在 O(logn) 的时间内求到任意区间内的答案,对于大规模查询的情况下,这种方法的优势便体现了出来。这棵树就是上文提及的一种神奇的数据结构——线段树。
我的理解就是,这样维护的树结构,所有的查询都可以用接近一半的时间取获得答案,会快于O(n)的动态规划。但是这颗线段树的节点新增和删除都是需要重新计算关键参数的,维护起来比较麻烦,只能适用变更少,数值较为固定的场景、或可利用缓存结构提前计算,可接受一定程度延时性的场景