每日一题——和为 K 的子数组

建议参考下列视频:
【560. 和为 K 的子数组 Subarray Sum Equals K【LeetCode 力扣官方题解】】 https://www.bilibili.com/video/BV13t4y1y7ya/?share_source=copy_web&vd_source=b1f5728f3a9c87006fa48f39e09acbab

问题描述

给定一个整数数组 nums 和一个整数 k,请统计并返回该数组中和为 k 的子数组的个数。子数组是数组中元素的连续非空序列。

示例

示例 1

输入:nums = [1, 1, 1], k = 2
输出:2
解释:子数组 [1, 1][1, 1] 的和为 2。

示例 2

输入:nums = [1, 2, 3], k = 3
输出:2
解释:子数组 [1, 2][3] 的和为 3。

约束条件

  • 1 <= nums.length <= 2 * 10^4
  • -1000 <= nums[i] <= 1000
  • -10^7 <= k <= 10^7

算法思路

本题的核心是利用前缀和哈希表来高效统计和为 k 的子数组个数。

前缀和

前缀和是指从数组的起始位置到当前位置的所有元素之和。通过计算前缀和,可以快速判断任意子数组的和是否等于目标值 k

哈希表

哈希表用于存储每个前缀和出现的次数。通过查找 pre - k 是否存在于哈希表中,可以快速判断是否存在一个子数组的和为 k

算法步骤

  1. 初始化哈希表,插入键为 0 的条目,值为 1。
  2. 遍历数组,计算前缀和 pre
  3. 查找 pre - k 是否存在于哈希表中,如果存在,累加其值到结果中。
  4. 更新哈希表,插入当前前缀和 pre
  5. 返回结果。

代码实现

以下是使用 C 语言实现的代码,包含详细注释。

使用 uthash.h 的实现

#include "uthash.h"
#include <stdio.h>
#include <stdlib.h>

// 哈希表结构
typedef struct {
    int key; // 前缀和
    int value; // 前缀和出现的次数
    UT_hash_handle hh; // uthash 需要的句柄
} hashTable;

int subarraySum(int* nums, int numsSize, int k) {
    hashTable* hashMap = NULL; // 创建一个空哈希表
    hashTable* hashNode;
    int result = 0; // 结果计数器
    int pre = 0; // 前缀和

    // 初始化哈希表,插入键为 0 的条目
    hashNode = (hashTable*)malloc(sizeof(hashTable));
    hashNode->key = 0;
    hashNode->value = 1;
    HASH_ADD_INT(hashMap, key, hashNode);

    for (int i = 0; i < numsSize; i++) {
        pre += nums[i]; // 更新前缀和
        int target = pre - k; // 计算目标值
        HASH_FIND_INT(hashMap, &target, hashNode); // 查找 pre - k 是否存在
        if (hashNode != NULL) {
            result += hashNode->value; // 如果存在,累加其值
        }

        // 更新哈希表,插入当前前缀和
        HASH_FIND_INT(hashMap, &pre, hashNode);
        if (hashNode == NULL) {
            hashNode = (hashTable*)malloc(sizeof(hashTable));
            hashNode->key = pre;
            hashNode->value = 1;
            HASH_ADD_INT(hashMap, key, hashNode);
        } else {
            hashNode->value++; // 如果已存在,值加 1
        }
    }

    // 释放哈希表
    hashTable* current;
    hashTable* tmp;
    HASH_ITER(hh, hashMap, current, tmp) {
        HASH_DEL(hashMap, current);
        free(current);
    }

    return result;
}

int main() {
    int nums1[] = {1, 1, 1};
    int k1 = 2;
    printf("Result 1: %d\n", subarraySum(nums1, 3, k1)); // 输出 2

    int nums2[] = {1, 2, 3};
    int k2 = 3;
    printf("Result 2: %d\n", subarraySum(nums2, 3, k2)); // 输出 2

    return 0;
}

自定义哈希表的实现

#include <stdio.h>
#include <stdlib.h>

// 哈希表节点
typedef struct HashNode {
    int key; // 前缀和
    int value; // 前缀和出现的次数
    struct HashNode* next; // 指向下一个节点
} HashNode;

// 哈希表
typedef struct HashTable {
    HashNode** table; // 哈希表数组
    int size; // 哈希表大小
} HashTable;

// 初始化哈希表
HashTable* initHashTable(int size) {
    HashTable* table = (HashTable*)malloc(sizeof(HashTable));
    table->size = size;
    table->table = (HashNode**)calloc(size, sizeof(HashNode*));
    return table;
}

// 插入或更新哈希表
void insertOrUpdate(HashTable* table, int key, int value) {
    int index = (key % table->size + table->size) % table->size; // 确保索引为非负值
    HashNode* node = table->table[index];
    while (node != NULL) {
        if (node->key == key) {
            node->value += value; // 如果键已存在,更新值
            return;
        }
        node = node->next;
    }
    // 如果键不存在,创建新节点
    node = (HashNode*)malloc(sizeof(HashNode));
    node->key = key;
    node->value = value;
    node->next = table->table[index];
    table->table[index] = node;
}

// 查找哈希表
int find(HashTable* table, int key) {
    int index = (key % table->size + table->size) % table->size; // 确保索引为非负值
    HashNode* node = table->table[index];
    while (node != NULL) {
        if (node->key == key) {
            return node->value; // 如果找到键,返回其值
        }
        node = node->next;
    }
    return 0; // 如果未找到键,返回 0
}

// 释放哈希表
void freeHashTable(HashTable* table) {
    for (int i = 0; i < table->size; i++) {
        HashNode* node = table->table[i];
        while (node != NULL) {
            HashNode* next = node->next;
            free(node); // 释放当前节点
            node = next;
        }
    }
    free(table->table); // 释放哈希表数组
    free(table); // 释放哈希表
}

// 计算和为 K 的子数组个数
int subarraySum(int* nums, int numsSize, int k) {
    HashTable* table = initHashTable(20001); // 初始化哈希表,大小为 20001
    insertOrUpdate(table, 0, 1); // 插入键为 0 的条目
    int count = 0; // 结果计数器
    int pre = 0; // 前缀和

    for (int i = 0; i < numsSize; i++) {
        pre += nums[i]; // 更新前缀和
        count += find(table, pre - k); // 查找 pre - k 是否存在
        insertOrUpdate(table, pre, 1); // 插入当前前缀和
    }

    freeHashTable(table); // 释放哈希表
    return count;
}

int main() {
    int nums1[] = {1, 1, 1};
    int k1 = 2;
    printf("Result 1: %d\n", subarraySum(nums1, 3, k1)); // 输出 2

    int nums2[] = {1, 2, 3};
    int k2 = 3;
    printf("Result 2: %d\n", subarraySum(nums2, 3, k2)); // 输出 2

    return 0;
}

代码说明

使用 uthash.h

  • 使用 uthash 提供的哈希表功能,简化了哈希表的实现。
  • 通过 HASH_ADD_INTHASH_FIND_INT 操作哈希表。

自定义哈希表

  • 使用链表解决哈希冲突。
  • 通过 (key % size + size) % size 确保索引为非负值。
  • 手动管理哈希表的内存分配和释放。

总结

本题通过前缀和和哈希表的结合,实现了高效的子数组和统计。使用 uthash 可以简化代码实现,而自定义哈希表则提供了更多的灵活性。两种实现方式均能达到 O(n) 的时间复杂度,适用于大规模数据的处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tt555555555555

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值