2021-08-29 剑指 Offer II 010. 和为 k 的子数组

在这里插入图片描述
一开始看到连续子数组就考虑用滑动窗口,滑动窗口模板如下

# 滑动窗口模板
left,right = 0, (0 or 1)
ret = total = 0
while right < len(nums):
   更新total值
   while 窗口内数据不满足要求
      1. 更新total值
      2. 收缩左边界
   更新ret最大值
返回 ret

代码

int subarraySum(int* nums, int numsSize, int k){
    int res = 0;
    int sum = 0;
    int left  = 0;
    int right = 0;
    /* 滑动窗口 */
    while (right < numsSize) {
        sum += nums[right];
        while (left < right && sum > k) {
            sum -= nums[left];
            left++;
        }

        if (sum == k) {
            res++;
        }
        right++;
    }
    return res;
}

但是注意到数组中有负数,滑动窗口的原则是right增加后和变大,left增加后和变小,所以该题不适合用滑动窗口。
采用前缀和的思想
朴素前缀和
定义 preSum[i] 为 [0…i] 里所有数的和
preSum[i] = preSum[i-1] + nums[i]
j…i子数组和为k转换为 preSum[i]-preSum[j-1] == k
preSum[j-1] == preSum[i]-k;(j<i) 满足这个条件的就应该是子数组和为k
preSum[]数组遍历一遍就可以得到
求以 序号 i 结尾的和为 k 的连续子数组个数
只要统计有多少个前缀和为 preSum[i]−k 的 preSum[m] 即可。m<i

int subarraySum(int* nums, int numsSize, int k){
    int res = 0;
    int *perHeadSum = (int*)malloc(sizeof(int) * (numsSize + 1));
    memset(perHeadSum, 0, sizeof(int) * (numsSize + 1));
    for (int i = 0; i < numsSize; ++i) {
        perHeadSum[i+1] = perHeadSum[i] + nums[i];
    }

    for (int i = 0; i < numsSize + 1; ++i) {
        for (int j = i + 1; j < numsSize + 1; ++j) {
            if (perHeadSum[j] - perHeadSum[i] == k) {
                res++;
            }
        }
    }
    return res;
}

朴素前缀和中还有双重循环,如果数据太多肯定超时

前缀和+Hash
用哈希标保存该前缀和是否出现过,即可优化二重循环


typedef struct {
    int sum;
    int count;
    UT_hash_handle hh;
} HashTable;

int subarraySum(int* nums, int numsSize, int k){
    int res = 0;
    int *perHeadSum = (int*)malloc(sizeof(int) * (numsSize + 1));
    memset(perHeadSum, 0, sizeof(int) * (numsSize + 1));
    HashTable *ht = NULL;
    HashTable *sumTemp = (HashTable*)malloc(sizeof(HashTable));
    sumTemp->sum = 0;
    sumTemp->count = 1;
    HASH_ADD_INT(ht, sum, sumTemp);
    for (int i = 0; i < numsSize; ++i) {
        perHeadSum[i+1] = perHeadSum[i] + nums[i];
        HashTable *temp = NULL;
        int findItem = perHeadSum[i+1] - k;
        HASH_FIND_INT(ht, &findItem, temp);
        if (temp != NULL) {
            res += temp->count;
        }
        HashTable *perHeadSumItem = NULL;
        int addKey = perHeadSum[i+1];
        HASH_FIND_INT(ht, &addKey, perHeadSumItem);
        if (perHeadSumItem == NULL) {
            HashTable *perNum = (HashTable*)malloc(sizeof(HashTable));
            perNum->sum = addKey;
            perNum->count = 1;
            HASH_ADD_INT(ht, sum, perNum);
        } else {
            perHeadSumItem->count++;
        }
    }

    return res;
}

C语言用HASH太难了。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值