Subarray Sum Equals K

子数组和等于K的个数
本文介绍了一种高效算法来找出整数数组中所有连续子数组的个数,这些子数组的和等于给定的目标值K。通过使用哈希表记录前缀和出现的次数,可以在O(n)的时间复杂度内解决问题。

问题描述:

Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k.

Example 1:

Input:nums = [1,1,1], k = 2
Output: 2
思路:记录从开始到位置i的累加和,再统计位置i之前有多少个累加和等于sum[i]-k.
代码:class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int n=nums.size();
        if (n==0)
            return 0;
        //计算到位置i的累加和
        vector<int> sum(n+1,0);
        for (int i=1; i<=n; i++)
            sum[i]=nums[i-1]+sum[i-1];
        //查看在位置i之前,是否有sum[j]=sum[i]-k
        unordered_map<int,int> hash;
        hash[0]=1;
        int res=0;
        for (int i=1; i<=n; i++)
        {
            if (hash[sum[i]-k])
                res+=hash[sum[i]-k];
            hash[sum[i]]++;
        }
        return res;
    }
};

你给出的函数签名: ```cpp int subarraySumEqualsK(vector<int> &nums, int K) ``` 是 **C++** 的语法(使用 `vector`),但你要求用 **C 语言** 实现 LintCode 838:求和为 `k` 的连续子数组个数。 --- ## ✅ 目标 我们使用 **纯 C 语言** 实现: - 输入:整型数组 `nums`,长度 `n`,目标和 `k` - 输出:连续子数组中和等于 `k` 的个数 例如: ```c nums = [2, 1, -1, 1, 2], k = 3 → 子数组:[2,1], [2,-1,1,2], [2,1,-1,1], [1,2] → 共 4 个 ``` --- ## ✅ 解法一:前缀和 + 哈希表(推荐,O(n) 时间) ### 🔍 思路 定义前缀和 `prefix[i] = nums[0] + nums[1] + ... + nums[i-1]` 若 `prefix[j] - prefix[i] == k`,则 `nums[i..j-1]` 的和为 `k` 即:我们要找有多少对 `(i, j)` 满足 `prefix[j] - prefix[i] == k` 等价于:遍历过程中,对当前前缀和 `sum`,查找之前是否有前缀和为 `sum - k` 的情况。 我们可以用哈希表记录每个前缀和出现的次数。 但由于 C 没有内置 `hashmap`,我们需要自己实现一个简单的 **哈希表(链地址法)**。 --- ## ✅ C 语言完整实现(前缀和 + 哈希表) ```c #include <stdio.h> #include <stdlib.h> // 哈希表节点(用于存储前缀和及其频次) typedef struct HashNode { int key; // 前缀和的值 int count; // 出现次数 struct HashNode* next; // 链地址法解决冲突 } HashNode; // 哈希表结构 typedef struct { int size; HashNode** buckets; } HashMap; // 创建哈希表 HashMap* createHashMap(int size) { HashMap* map = (HashMap*)malloc(sizeof(HashMap)); map->size = size; map->buckets = (HashNode**)calloc(size, sizeof(HashNode*)); return map; } // 哈希函数 int hash(int key, int size) { return (key < 0 ? -key : key) % size; // 处理负数 } // 插入或更新:key 出现次数 +1 void hashMapPut(HashMap* map, int key) { int index = hash(key, map->size); HashNode* head = map->buckets[index]; // 查看是否已存在该 key while (head != NULL) { if (head->key == key) { head->count++; return; } head = head->next; } // 不存在,插入新节点 HashNode* newNode = (HashNode*)malloc(sizeof(HashNode)); newNode->key = key; newNode->count = 1; newNode->next = map->buckets[index]; map->buckets[index] = newNode; } // 获取 key 的出现次数 int hashMapGet(HashMap* map, int key) { int index = hash(key, map->size); HashNode* head = map->buckets[index]; while (head != NULL) { if (head->key == key) { return head->count; } head = head->next; } return 0; } // 释放哈希表内存 void freeHashMap(HashMap* map) { for (int i = 0; i < map->size; i++) { HashNode* head = map->buckets[i]; while (head != NULL) { HashNode* temp = head; head = head->next; free(temp); } } free(map->buckets); free(map); } /** * 求连续子数组和为 k 的个数 * @param nums: 整数数组 * @param n: 数组长度 * @param k: 目标和 * @return: 满足条件的子数组个数 */ int subarraySumEqualsK(int* nums, int n, int k) { if (!nums || n == 0) return 0; // 创建哈希表,初始大小设为 100(可调整) HashMap* map = createHashMap(100); // 初始化:前缀和为 0 的情况出现 1 次 hashMapPut(map, 0); int sum = 0; int count = 0; for (int i = 0; i < n; i++) { sum += nums[i]; // 当前前缀和 // 查询是否存在前缀和为 (sum - k) int target = sum - k; count += hashMapGet(map, target); // 将当前前缀和存入哈希表 hashMapPut(map, sum); } freeHashMap(map); return count; } // ============ 测试代码 ============ int main() { int nums[] = {2, 1, -1, 1, 2}; int n = sizeof(nums) / sizeof(nums[0]); int k = 3; int result = subarraySumEqualsK(nums, n, k); printf("Input: ["); for (int i = 0; i < n; i++) { if (i > 0) printf(", "); printf("%d", nums[i]); } printf("], k = %d\n", k); printf("Output: %d\n", result); // 应输出 4 return 0; } ``` --- ## ✅ 输出结果 ``` Input: [2, 1, -1, 1, 2], k = 3 Output: 4 ``` ✅ 正确! --- ## ✅ 算法原理详解 | 步骤 | 说明 | |------|------| | 1. 前缀和 | `sum` 表示从 `0` 到 `i` 的累加和 | | 2. 哈希表记录 | 记录每个前缀和出现的次数 | | 3. 关键逻辑 | 若 `sum - k` 在之前出现过,则说明存在某个 `j < i` 使得 `sum[i] - sum[j] == k` → 区间 `[j+1, i]` 和为 `k` | | 4. 初始状态 | `hashMapPut(map, 0)`:表示空前缀和出现一次,处理从下标 0 开始的子数组 | --- ## ✅ 示例分析(nums = [2,1,-1,1,2], k=3) | i | num | sum(前缀和) | sum-k | hashMap 中是否有? | count += | |---|-----|----------------|--------|----------------------|----------| | 0 | 2 | 2 | -1 | 否 | 0 | | 1 | 1 | 3 | 0 | 是(初始插入) | +1 → 1 | | 2 | -1 | 2 | -1 | 否 | 0 | | 3 | 1 | 3 | 0 | 是(出现过) | +1 → 2 | | 4 | 2 | 5 | 2 | 是(sum=2 出现过两次)| +2 → 4 | 最终 `count = 4` --- ## ✅ 时间与空间复杂度 - **时间复杂度**:O(n),单次遍历,哈希操作平均 O(1) - **空间复杂度**:O(n),哈希表最多存 n 个不同前缀和 --- ## ✅ 对比暴力解法(O(n²)) 也可以用两层循环枚举所有子数组: ```c int subarraySumEqualsK_bruteforce(int* nums, int n, int k) { int count = 0; for (int i = 0; i < n; i++) { int sum = 0; for (int j = i; j < n; j++) { sum += nums[j]; if (sum == k) { count++; } } } return count; } ``` 虽然简单,但时间复杂度 O(n²),大数据会超时。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值