python_18_数组累加和

1 题目

1.1 累加和

一个数组全是正数,给一个sum,问累加和正好等于sum的子数组,最长多长?sum=6, a[3,2,1,1,1,6,1,1,1,1,1,1],返回6,6个1
,子数组,子串都是连续的,序列才是不连续的。

窗口为左闭右开。


def getMaxLength(arr,K):
    if arr is None or len(arr) == 0 or K <= 0:
        return 0
    # [L,R]
    # [0,0]  0 到 0 范围,左闭右闭
    L = 0
    R = 0
    wsum = arr[0]   # 窗口累加和
    is_len = 0
    while R < len(arr):
       if wsum == K:   # 如果正好为累加和
           is_len = max(is_len,R - L + 1)
           wsum -= arr[L]   # 先让L动,L出窗口,先减掉值,窗口退出
           L += 1
       elif wsum < K:   # 小于的时候
           # [R + 1] 位置的数加到窗口内 -> wsum
           R += 1
           if R == len(arr):  # 扩到越界,说明没有答案
               break
           wsum += arr[R]
       else:   # 大于的时候
           wsum -= arr[L]
           L += 1
    return is_len

a = [1,2,3,4,5,6,1,1,1,1,1]
k = 5
print(getMaxLength(a,k))


1.2 累加和2

一个数组有正数有负数有0,给一个sum,问累加和正好等于sum的子数组,最长多长?

解决子数组两种常见想法:
每个位置开头s答案是,答案在其中
每个位置以s结尾答案是…

思路:
map种记录最早累加和,初始化0 :-1

代码:


def getMaxLength(arr,K):
    if arr is None or len(arr) == 0 or K <= 0:
        return 0
    # [L,R]
    # [0,0]  0 到 0 范围,左闭右闭
    map = {}
    map[0] = -1            # 初始化
    is_len = 0
    sum = 0
    for i in range(len(arr)):
        sum += arr[i]           # 累加和 [0...i] 1000 200  1000-200 800
        if (sum - k) in map:   # 找sum-k累计和,如果有就有答案
            is_len = max(i - map.get(sum - k),is_len)
        if sum not in map:
            map[sum] = i        # 最早累加和加上
    return is_len



a = [1,2,3,4,1,-1,1,1,1,1,1]
k = 5
print(getMaxLength(a,k))




如果求子数组中1和2数量相同的数组,那么把1变为1,把2变为-1,其余数字都是0,加起来是0(1和2正好抵消),即可。

1.3 累加和3

一个数组有正数有负数有0,给一个k是累加和,小于等于k的子数组都达标,问哪一个是小于等于K最长的子数组?

思路:
辅助数组minsum
minsum[i]=5 的意思是,子数组以i开头,到5位置取得最小的累加和
minsum[i]=j
辅助数组minsumEnd
与上面搭配,5,j记录在minsumEnd里
在这里插入图片描述
往右扩

def maxLengthAwesome(arr,K):
    if arr is None or len(arr) == 0:
        return 0
    # [L,R]
    # [0,0]  0 到 0 范围,左闭右闭
    N = len(arr)
    # 长度和原数组一样
    minSums = [0] * N
    minSumEnds = [0] * N
    minSums[N - 1] = arr[N - 1]  # 右边界
    minSumEnds[N - 1] = arr[N - 1]
    for i in range(N-2,-1,-1):
        # 往右扩,有利可图
        if minSums[i + 1] <= 0:   # 小于0和小于等于0都一样
            # 右边扩的累加和,右边扩的累加和都加在一起
            minSums[i] = arr[i] + minSums[i + 1]
            # 我右边界和我扩的右边界保持一致
            minSumEnds[i] = minSumEnds[i + 1]
        else:    # 往右扩,无利可图,不影响结果
            minSums[i] = arr[i]  # 右边界是自己
            minSumEnds[i] = i

    # 下面是如何扩展,上面是如何生成数组
    # (i...)(...)(i...)(end...X   end代表当把end这一块算进来,就不达标了,之前是扩出来的
    end = 0
    sum = 0    # 上面(i...)(...)(i...)这三块累加和就是sum,已经扩出来整体累加和是多少
    res_len = 0    # 最大长度
    # i是窗口的最左的位置,end扩出来的最右有效块儿的最后一个位置的,
    # end也是下一块儿的开始位置
    # 窗口:[i~end)
    # 窗口以0开始扩,慢慢缩小,以1开始扩,再缩
    # 但并不是从头扩,而是从end位置看一看,能不能把这一块算进来。所以i递增
    for i in range(N):
        # while循环结束之后
        # 1)如果以i开头的情况下,累加和<=k的最长子数组是arr[i..end-1],看看这个子数组
        # 2)如果以i开头的情况下,累加和<=k的最长子数组比arr[i...end-1]短,更新还是不更新
        # end要扩 不能越界,end从0开始先看看能不能算进来 and 此时的累加和加上下一块的最小累加和,没有超,就一直扩
        # end越界 and 加上下一块的累加和超了,就跳出去
        while end < N and sum + minSums[end] <= K:
            # (end...?)(? + 1)
            sum += minSums[end]    # 算进sum里
            end = minSumEnds[end] + 1   # minSumEnds右边界+1,就是问号区域
        # [i....](end....X) 已经扩好了,来到end就会超过去
        # end越界跳出来,i.....越界位置,也可以
        # [2,2]任点击
        res_len = max(res_len,end - i)     # end - i 有多少个数扩进去了
        # sum -= arr[i] 意思是,[i....] -> [i+1...]缩一个,和减去缩的数
        # 窗口弄没了还不达标
        if end < i:   # 窗口内还有数[i~end) [4,4)
            sum -= arr[i]
        else:    # 窗口内已经没有救了,说明从i开头的所有子数组累加和都不可能<=K
            end = i + 1    # i和end一起动,i开头一个块也扩不动

    return res_len


a = [1,2,3,4,1,-1,1,1,1,1,1]
k = 5
print(maxLengthAwesome(a,k))


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值