⭐算法入门⭐《哈希表》中等02 —— LeetCode 560. 和为K的子数组

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了⭐算法入门⭐《哈希表》中等02 —— LeetCode 560. 和为K的子数组相关的知识,希望对你有一定的参考价值。

🙉饭不食,水不饮,题必须刷🙉

C语言免费动漫教程,和我一起打卡!
🌞《光天化日学C语言》🌞

LeetCode 太难?先看简单题!
🧡《C语言入门100例》🧡

数据结构难?不存在的!
🌳《画解数据结构》🌳

LeetCode 太简单?算法学起来!
🌌《夜深人静写算法》🌌

一、题目

1、题目描述

  给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
  样例输入: s = nums = [1,1,1], k = 2
  样例输出: 2

2、基础框架

  • C语言版本 给出的基础框架代码如下:
int subarraySum(int* nums, int numsSize, int k){
}

3、原题链接

( 1 ) (1) (1) LeetCode 560. 和为K的子数组
( 2 ) (2) (2) 剑指 Offer II 010. 和为 k 的子数组

二、解题报告

1、思路分析

  1)考虑一段连续子数组,左区间为 i i i,右区间为 j j j,子数组区间和 s ( i , j ) = ∑ t = i j n u m s [ t ] s(i,j) = \\sum_{t=i}^{j} nums[t] s(i,j)=t=ijnums[t]
  2)令前缀和 s u m [ i ] = ∑ j = 0 i n u m s [ j ] sum[i] = \\sum_{j=0}^{i}nums[j] sum[i]=j=0inums[j],利用部分和公式,得到 s ( i , j ) = s u m [ j ] − s u m [ i − 1 ] s(i,j) = sum[j] - sum[i-1] s(i,j)=sum[j]sum[i1]    3)根据题意,需要满足 k = s u m [ j ] − s u m [ i − 1 ] k = sum[j] - sum[i-1] k=sum[j]sum[i1],移项后得到 s u m [ j ] = s u m [ i − 1 ] + k sum[j] = sum[i-1] + k sum[j]=sum[i1]+k   4)于是,我们可以通过枚举 s u m [ j ] sum[j] sum[j],然后查找有多少满足条件的 s u m [ i ] + k   ( − 1 ≤ i < j ) sum[i]+k \\ (-1 \\le i \\lt j) sum[i]+k (1i<j),统计求和。再把 s u m [ j ] + k sum[j] + k sum[j]+k 进行插入供下次查找用。这里的插入查找,只需要维护一个哈希表即可。

2、时间复杂度

  • 哈希表的插入查找均摊复杂度为 O ( 1 ) O(1) O(1),总共 n n n 个数字的枚举,时间复杂度为 O ( n ) O(n) O(n)

3、代码详解

/******************** 哈希表 开放定址法 ********************/
#define maxn (1<<17)
#define mask (maxn-1)
#define DataType int
#define Boolean int
#define NULLKEY (1<<30)    /* 空槽标记不能用-1,会导致正常值也为-1的情况*/

typedef struct {
    DataType data[maxn];
}HashTable;

void HashInit(HashTable *ht) {
    int i;
    for(i = 0; i < maxn; ++i) {
        ht->data[i] = NULLKEY;
    }
}

int HashGetAddr(DataType key) {
    return key & mask;        // 除留余数法
}

int HashInsert(HashTable *ht, DataType key) {
    int addr = HashGetAddr(key);
    int retaddr;
    if ( HashSearchKey(ht, key, &retaddr ) ) {
        return retaddr;
    } 
    while(ht->data[addr] != NULLKEY)
        addr = HashGetAddr(addr + 1);
    ht->data[addr] = key;
    return addr;
}

Boolean HashSearchKey(HashTable *ht, DataType key, int *addr) {
    int startaddr = HashGetAddr(key);
    *addr = startaddr;
    while(ht->data[*addr] != key) {
        *addr = HashGetAddr(*addr + 1);
        if(ht->data[*addr] == NULLKEY) {
            return 0;                    // 遇到了空槽,说明没找到,返回 0
        }
        if(*addr == startaddr) {
            return 0;                    // 找了一圈都没找到,循环了
        }
    }
    return 1;
}

/******************** 哈希表 开放定址法 ********************/
#define maxs 20020
int sum[maxs];
int cnt[maxn];
HashTable ht;

int subarraySum(int* nums, int numsSize, int k){
    int i, pos, ans;
    for(i = 0; i < numsSize; ++i) {
        sum[i] = nums[i];
        if(i)
            sum[i] += sum[i-1];                     // (1) 
    }
    ans = 0;
    memset(cnt, 0, sizeof(cnt));
    HashInit( &ht );
    pos = HashInsert( &ht, k );                     // (2) 
    ++cnt[ pos ];                  

    for(i = 0; i < numsSize; ++i) {
        if( HashSearchKey( &ht, sum[i], &pos) ) {   
            ans += cnt[ pos ];                      // (3) 
        }
        ++cnt[ HashInsert( &ht, sum[i] + k ) ];     // (4) 
    }
    return ans;
}
  • ( 1 ) (1) (1) 计算前缀和;
  • ( 2 ) (2) (2) 首先将sum[-1] + k插入到哈希表;
  • ( 3 ) (3) (3) 如果哈希表中存在sum[i]这个关键字,则它的统计个数;
  • ( 4 ) (4) (4)sum[i]+k插入哈希表,供下次查找使用;

三、本题小知识

哈希表 执行插入前 要先判断是否存在这个关键字。


以上是关于⭐算法入门⭐《哈希表》中等02 —— LeetCode 560. 和为K的子数组的主要内容,如果未能解决你的问题,请参考以下文章

⭐算法入门⭐《哈希表》中等04 —— LeetCode 347. 前 K 个高频元素

解题报告力扣 第 280 场周赛

⭐算法入门⭐《哈希表》中等03 —— LeetCode 380. O 时间插入删除和获取随机元素

⭐算法入门⭐《哈希表》中等05 —— LeetCode 215. 数组中的第K个最大元素

⭐算法入门⭐《链表》中等02 —— LeetCode 143. 重排链表

⭐算法入门⭐《哈希表》简单02 —— LeetCode 41. 缺失的第一个正数