From 16317c86459b7fb12695d43fc2e6b81e115811c2 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Thu, 1 Oct 2026 14:21:18 -0400 Subject: [PATCH] docs(content): enhance linear-structures docs with detailed explanations, new problem page, updated images, and corrected array badge count --- .../hash-table/index.mdx | 43 ++++++++++++++++ .../974-subarray-sums-divisible-by-k.mdx | 49 +++++++++++++++++++ .../prefix-sum/index.mdx | 37 ++++++++++++++ .../sliding-window/index.mdx | 15 ++++-- .../appendix/(1-data-structures)/array.mdx | 2 +- 5 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 apps/docs/content/(01-linear-structures)/prefix-sum/974-subarray-sums-divisible-by-k.mdx diff --git a/apps/docs/content/(01-linear-structures)/hash-table/index.mdx b/apps/docs/content/(01-linear-structures)/hash-table/index.mdx index 00e00ca..0f6f791 100644 --- a/apps/docs/content/(01-linear-structures)/hash-table/index.mdx +++ b/apps/docs/content/(01-linear-structures)/hash-table/index.mdx @@ -3,3 +3,46 @@ title: Hash Table description: 'A key maps to a value in constant time, which removes the need for a full scan.' hidden: true --- + +## Hash Tables + +Sometimes when programming, we need a data structure that maps an arbitrary data type to another arbitrary data type, like an array with non-integer indices. For example, a phone book that allows lookup by a person's name requires such data type. + +A **hash table** (or a hash map) is one way of representing this data structure. The idea of hash table is this: + +Data is stored in a fix-sized array, where each data value has an unique index. When we add a new `(key, value)` pair to the array, we use a hash function to generate a hash value within the range of the array with the `key`. We then store the data `value` at the index indicated by the generated hash value. Later, when we retrieve the data with the `key` from the hash table, we hash the `key` again and look up the data in the array with the corresponding index. Since the hash function always returns the same hash values with the same input, we will always find the correct item in that index. + + + + Hash Table Operations + + + +## Efficiency + +Assuming the hash function uses constant time, the average time complexity is `O(n / k)` for each insertion/access operation on the table, where `n` is the number of entries in the hash table and `k` is the array size of the table. This assumes the hash function distributes keys uniformly across the `k` buckets, so each bucket holds about `n / k` entries on average. + +**Frequency Counting** + +```python +def get_counter(arr: list[int]) -> dict[int, int]: + # initialize a hash map to store each number and its count + counter: dict[int, int] = {} + for num in arr: + # check if num is a key in the hash map + if num in counter: + # update the count of num to increase by 1 + counter[num] += 1 + else: + # set the count of num to 1 + counter[num] = 1 + return counter +``` + +::: tip[Python provides a built-in `collections.Counter` class] +```python +from collections import Counter + +fq = Counter(arr) +``` +::: diff --git a/apps/docs/content/(01-linear-structures)/prefix-sum/974-subarray-sums-divisible-by-k.mdx b/apps/docs/content/(01-linear-structures)/prefix-sum/974-subarray-sums-divisible-by-k.mdx new file mode 100644 index 0000000..23a4bcd --- /dev/null +++ b/apps/docs/content/(01-linear-structures)/prefix-sum/974-subarray-sums-divisible-by-k.mdx @@ -0,0 +1,49 @@ +--- +title: '974. Subarray Sums Divisible by K' +description: Given an integer array nums and an integer k, return the number of non-empty subarrays that have a sum divisible by k +icon: dot +topics: + - { name: "Array", slug: "array" } + - { name: "Hash Table", slug: "hash-table" } + - { name: "Prefix Sum", slug: "prefix-sum" } +issue: "https://github.com/prdlk/leetcode/issues/40" +sidebar: + label: 'Subarray Sums Divisible by K' + badge: 'Medium' +--- + +### Example 1: +- Input: `nums = [4,5,0,-2,-3,1], k = 5` +- Output: `7` +- Explanation: There are `7` subarrays with a sum divisible by `k = 5`: [4, `5`, `0`, `-2`, `-3`, 1], `[5]`, [5, 0], [5, `0`, `-2`, -3], `[0]`, [0, `-2`, -3], [-2, -3] + +### Example 2: +- Input: `nums = [5], k = 9` +- Output: `0` + +### Constraints: + +- `1 <= nums.length <= 3 * 10^4` +- `-10^4 <= nums[i] <= 10^4` +- `2 <= k <= 10^4` + +## Solution + +```py +class Solution: + def subarraysDivByK(self, nums: list[int], k: int) -> int: + remainder_counts = {0: 1} + cur_sum = 0 + count = 0 + + for n in nums: + cur_sum += n + remainder = cur_sum % k + + if remainder in remainder_counts: + count += remainder_counts[remainder] + + remainder_counts[remainder] = remainder_counts.get(remainder, 0) + 1 + + return count +``` diff --git a/apps/docs/content/(01-linear-structures)/prefix-sum/index.mdx b/apps/docs/content/(01-linear-structures)/prefix-sum/index.mdx index 08a4c0b..f1ceda3 100644 --- a/apps/docs/content/(01-linear-structures)/prefix-sum/index.mdx +++ b/apps/docs/content/(01-linear-structures)/prefix-sum/index.mdx @@ -3,3 +3,40 @@ title: Prefix Sum description: 'A running total is stored at each index, so the sum of any range is one subtraction.' hidden: true --- + +Suppose you are handed an array and asked for the sum of the elements between index `i` and index `j`. One query is easy: loop from `i` to `j` and add. But now imagine thousands of such queries over the same array, or a search that inspects the sum of every possible subarray. Recomputing each range from scratch costs `O(n)` per query, and the work piles up fast. + +Prefix sum removes that repetition. You spend `O(n)` once to precompute running totals, and afterward every range sum collapses into a single subtraction that runs in `O(1)`, no matter how wide the range. + + + +## Transformation + +A prefix sum turns an array into a new array of running totals. For an input array `arr` of length `n`, build a `prefix` array of length `n + 1`: + +`prefix[0] = 0` + +`prefix[k] = arr[0] + arr[1] + ... + arr[k-1]` + +Each entry is the previous total plus one more element, so a single pass builds the whole thing: + +`prefix[k] = prefix[k-1] + arr[k-1]` + +The leading `prefix[0] = 0` is the sum of zero elements. That extra slot is what makes the range math below land exactly on the boundaries, which is why `prefix` has one more cell than `arr`. + + + + + + +## Any range sum is one subtraction + +The sum of the subarray from index `i` up to (but not including) `j` is `prefix[j] - prefix[i]`. `prefix[j]` is the total of everything before `j`, and `prefix[i]` is the total of everything before `i`, so subtracting throws away the front you did not ask for and leaves the middle. + +## Watch the boundaries + +The one place prefix sums trip people up is the off-by-one at the edges. Keep three facts straight: `prefix` has length `n + 1`, `prefix[0]` is `0`, and `sum(arr[i..j-1]) = prefix[j] - prefix[i]` uses an exclusive right index `j`. If a problem hands you an inclusive right index `r`, translate it to `prefix[r + 1] - prefix[i]`. + +## When to reach for it + +Prefix sums fit whenever you need repeated range sums over a fixed array, or when an algorithm searches across subarray boundaries. For a single one-off sum, a plain loop is simpler; the prefix array earns its `O(n)` setup only once the same array is queried many times. diff --git a/apps/docs/content/(01-linear-structures)/sliding-window/index.mdx b/apps/docs/content/(01-linear-structures)/sliding-window/index.mdx index 1d20a29..2fea5d4 100644 --- a/apps/docs/content/(01-linear-structures)/sliding-window/index.mdx +++ b/apps/docs/content/(01-linear-structures)/sliding-window/index.mdx @@ -55,7 +55,10 @@ Without an invariant, shrinking the window becomes arbitrary. You no longer know The window size never changes. The main task is updating the window state efficiently when one element enters and one leaves. ::: -![Sliding Window Fixed Size](../../../public/sliding-window-fixed-size.svg) + + + Sliding Window Fixed Size + ### 2. Flexible-size windows with a validity rule @@ -63,8 +66,9 @@ The window size never changes. The main task is updating the window state effici Here the window expands until invalid, then shrinks until valid again. ::: -![Flexible-size windows](../../../public/sliding-window-flexible.svg) - + + Sliding Window Flexible + ### 3. Frequency-tracking windows @@ -72,7 +76,10 @@ Here the window expands until invalid, then shrinks until valid again. The difficulty comes from keeping the bookkeeping correct as characters enter and leave. ::: -![Frequency-tracking windows](../../../public/sliding-window-frequency.svg) + + + Frequency-tracking windows + ## How Sliding Window Differs From Other Two-Pointer Families diff --git a/apps/docs/content/appendix/(1-data-structures)/array.mdx b/apps/docs/content/appendix/(1-data-structures)/array.mdx index c881c6b..4a67677 100644 --- a/apps/docs/content/appendix/(1-data-structures)/array.mdx +++ b/apps/docs/content/appendix/(1-data-structures)/array.mdx @@ -3,7 +3,7 @@ title: "Array" list: "https://leetcode.com/problem-list/array/" icon: list-ordered sidebar: - badge: '41' + badge: '42' --- :::note