问题描述
差分数组
差分数组,看完名字就懂了。最大的作用还是节省时间复杂度。原来可能要 O ( n 2 ) O(n^2) O(n2)的程序,用完差分数组,只需要 O ( n ) O(n) O(n)。
并查集
并查集的思想是:最开始是一群不连通的区域,通过某些操作之后,a和b连通了,b和c又连通了,访问a等于访问一大块区域。并查集就是来记录这些连通区域的。
有一个比较好理解的实现可以看:
并查集视频图解
我们来看例题:
给你两个下标从 0 开始的整数数组 nums 和 removeQueries ,两者长度都为 n 。对于第 i 个查询,nums 中位于下标 removeQueries[i] 处的元素被删除,将 nums 分割成更小的子段。
一个 子段 是 nums 中连续 正 整数形成的序列。子段和 是子段中所有元素的和。
请你返回一个长度为 n 的整数数组 answer ,其中 answer[i]是第 i 次删除操作以后的 最大 子段和。
示例 1:
输入:nums = [1,2,5,6,1], removeQueries = [0,3,2,4,1]
输出:[14,7,2,2,0]
解释:用 0 表示被删除的元素,答案如下所示:
查询 1 :删除第 0 个元素,nums 变成 [0,2,5,6,1] ,最大子段和为子段 [2,5,6,1] 的和 14 。
查询 2 :删除第 3 个元素,nums 变成 [0,2,5,0,1] ,最大子段和为子段 [2,5] 的和 7 。
查询 3 :删除第 2 个元素,nums 变成 [0,2,0,0,1] ,最大子段和为子段 [2] 的和 2 。
查询 4 :删除第 4 个元素,nums 变成 [0,2,0,0,0] ,最大子段和为子段 [2] 的和 2 。
查询 5 :删除第 1 个元素,nums 变成 [0,0,0,0,0] ,最大子段和为 0 ,因为没有任何子段存在。
所以,我们返回 [14,7,2,2,0] 。
本题采用了倒序思想 + 并查集的思路。倒序其实我当初竟然也想出来了,惊了。如果倒序,可以看成添加元素的过程。就涉及到并查集的合并区间。
当添加新的下标x时,合并x和x + 1。其实很好理解!
假如下标0的元素被添加了,相当于连接了端点0到端点1。 0->1
下标2被添加,连接了端点2到端点3。2->3
下标1被添加,连接了端点1到端点2。1->2 这样会把012都合并起来。
定义**[链的起点,链的终点 - 1]**为实际子段。
对于最大子段和,取上一个最大子段和,合并后最大子段和的最大值。
class Solution:
def maximumSegmentSum(self, nums: List[int], removeQueries: List[int]) -> List[int]:
n = len(nums)
fa = list(range(n + 1)) #原定义 0:0 1:1 2:2 3:3 4:4...
sum = [0] * (n + 1)
def find(x: int) -> int:
if fa[x] != x:
fa[x] = find(fa[x]) #找到链尾
return fa[x]
ans = [0] * n
for i in range(n - 1, 0, -1):
x = removeQueries[i]
to = find(x + 1) #找到这个区间的最右侧
fa[x] = to # 合并 x 和 x+1
sum[to] += sum[x] + nums[x]
ans[i - 1] = max(ans[i], sum[to])
return ans
举个例子
插入下标0的元素5 0->1 to = 1 sum[1] = 5
插入下标2的元素15 2->3 to = 3 sum[3] = 15
插入下标1的元素10 1->2->3 to = 3 sum[3] = sum[3] + sum[1] + num[1]
sum[x]为 最后一个元素为x - 1的子段和。
如果合并了区间,链尾总和等于左区间(sum[x])加上右区间(sum[to])和以及填补的窟窿(也就是num[x])
记住当时存sum的位置总是子区间最右边那个框。
如果没有合并区间,那么新得到的区间和就是本身。sum[x]肯定 = 0,sum[to]也肯定没有被访问过, = 0.
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)