00. Prefix Sum & Line Swipe
Prefix Sum
Theory
Description
The prefix sum technique allows for efficient range sum queries by precomputing cumulative sums in an array.
Steps
- Build the Prefix Sum Array:
Create a new arrayprefix
where each element at indexi
stores the sum of elements from the start of the array up to indexi
:
And so on…
Example:
Forarr = [3, 1, 4, 1, 5, 9]
, theprefix_sum
array is[3, 4, 8, 9, 14, 23]
. - Answer Range Sum Queries:
To find the sum of elements between indicesi
andj
, use:
sum(i, j) = prefix[j] - prefix[i-1]
Example:
For arr = [3, 1, 4, 1, 5, 9]
, the sum from index 2
to 4
:
sum(2, 4) = prefix[4] - prefix[1] = 14 - 4 = 10
.
Note
If i = 0
, simply return prefix[j]
.
Time & Space Complexity
- Time:
O(n)
for building the prefix sum array,O(1)
per query. - Space:
O(n)
for storing the prefix sum array.
Problems
1. Range Sum Query - Immutable (Leetcode:303)
Problem Statement
Given an integer array nums
, handle multiple queries of the following type:
Calculate the sum of the elements of nums between indices left
and right
inclusive where left
<= right
.
Implement the NumArray
class:
NumArray(int[] nums)
Initializes the object with the integer arraynums
.int sumRange(int left, int right)
Returns the sum of the elements of nums between indicesleft
andright
inclusive (i.e.nums[left] + nums[left + 1] + ... + nums[right])
.
Example 1:
Input
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
Output
[null, 1, -1, -3]
Explanation
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return (-2) + 0 + 3 = 1
numArray.sumRange(2, 5); // return 3 + (-5) + 2 + (-1) = -1
numArray.sumRange(0, 5); // return (-2) + 0 + 3 + (-5) + 2 + (-1) = -3
Constraints:
1 <= nums.length <= 104
-105 <= nums[i] <= 105
0 <= left <= right < nums.length
At most 104 calls will be made to sumRange.
Code & Explaination
-
Constructor (
__init__
):- Initializes a list
prefix_sum
whereprefix_sum[i]
stores the sum of the elements from index0
toi-1
. - Builds this
prefix_sum
array by iterating over the input listnums
.
- Initializes a list
-
Method
sumRange(left, right)
:- Returns the sum of the subarray between indices left and right by calculating the difference:
prefix_sum[right+1] - prefix_sum[left].
- This allows constant time
O(1)
querying of subarray sums after the initial setup.
- Returns the sum of the subarray between indices left and right by calculating the difference:
2. Left and Right Sum Differences (Leetcode:2574)
Problem Statement
Given a 0-indexed integer array nums
, find a 0-indexed integer array answer
where:
answer.length == nums.length
.answer[i] = |leftSum[i] - rightSum[i]|
.
Where:
leftSum[i]
is the sum of elements to the left of the indexi
in the arraynums
. If there is no such element,leftSum[i] = 0
.rightSum[i]
is the sum of elements to the right of the indexi
in the arraynums
. If there is no such element,rightSum[i] = 0
.
Return the arrayanswer
.
Example 1:
Input: nums = [10,4,8,3]
Output: [15,1,11,22]
Explanation: The array leftSum is [0,10,14,22] and the array rightSum is [15,11,3,0].
The array answer is [|0 - 15|,|10 - 11|,|14 - 3|,|22 - 0|] = [15,1,11,22].
Example 2:
Input: nums = [1]
Output: [0]
Explanation: The array leftSum is [0] and the array rightSum is [0].
The array answer is [|0 - 0|] = [0].
Constraints:
1 <= nums.length <= 1000
1 <= nums[i] <= 105
code & Explanation
-
Initialization :
left_sum = 0
: Tracks the sum of elements before the current index.right_sum = sum(nums)
: Initially holds the total sum of all elements.answer = []
: Stores the result.
-
Loop through nums :
- For each element
num
: - Update
left_sum
: Addnum
toleft_sum
. - Calculate and append absolute difference:
|left_sum - right_sum|
is appended toanswer
. - Update
right_sum
: Subtractnum
fromright_sum
.
- For each element
-
Return answer :
- After the loop, return the computed differences in
answer
.
- After the loop, return the computed differences in
3. XOR Queries of a Subarray (Leetcode:1310)
Problem Statement
You are given an array arr
of positive integers. You are also given the array queries
where queries[i] = [lefti, righti]
.
For each query i
compute the XOR of elements from lefti
to righti
(that is, arr[lefti] XOR arr[lefti + 1] XOR ... XOR arr[righti]
).
Return an array answer
where answer[i]
is the answer to the ith
query.
Example 1:
Input:
arr = [1,3,4,8]
,queries = [[0,1],[1,2],[0,3],[3,3]]
Output:[2,7,14,8]
Explanation: The binary representation of the elements in the array are:
1 = 0001
3 = 0011
4 = 0100
8 = 1000
The XOR values for queries are:
[0,1] = 1 xor 3 = 2
[1,2] = 3 xor 4 = 7
[0,3] = 1 xor 3 xor 4 xor 8 = 14
[3,3] = 8```
Example 2:
Input: arr = [4,8,2,10], queries = [[2,3],[1,3],[0,0],[0,3]]
Output: [8,0,4,4]
Constraints:
1 <= arr.length, queries.length <= 3 * 104
1 <= arr[i] <= 109
queries[i].length == 2
0 <= lefti <= righti < arr.length
Code & Explaination
-
Initialization:
prefix = [0] * (len(arr) + 1)
: Create a listprefix
to store the cumulative XOR of elements in the array. The extra element accounts for the starting point (index 0).answer = []
: This will store the result for each query.
-
Build Prefix XOR Array:
- For each element
arr[i]
in the array, compute the cumulative XOR up to that index: prefix[i + 1] = prefix[i] ^ arr[i]
: This updates theprefix
list, where each entry ati + 1
holds the XOR of all elements from the start of the array up to indexi
.
- For each element
-
Process Queries:
- For each query
[left, right]
, calculate the XOR of elements from indexleft
toright
: answer.append(prefix[right + 1] ^ prefix[left])
: This uses the precomputedprefix
array. The XOR of the subarray fromleft
toright
is found by subtracting the prefix XORs atleft
andright + 1
.
- For each query
-
Return the Answer:
- The final
answer
list contains the result for each query, which is the XOR of the elements in the subarray defined byleft
andright
.
- The final
4. Subarray Sum Equals K (Leetcode:560)
Problem Statement
Given an array of integers nums
and an integer k
, return the total number of subarrays whose sum equals to k
.
A subarray is a contiguous non-empty
sequence of elements within an array.
Example 1:
Input: nums = [1,1,1], k = 2
Output: 2
Example 2:
Input: nums = [1,2,3], k = 3
Output: 2
Constraints:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
Code & Explaination
-
Initialization:
count = 0
: To store the number of valid subarrays.prefix_sum = 0
: To keep track of the cumulative sum as we iterate through the array.dictionary = {0: 1}
: This keeps track of how many times a certain prefix sum has occurred.- We start with
{0: 1}
because a sum of0
is possible before we start processing the array.
-
Iterate through the array:
- For each element
num
in the array, update theprefix_sum
by addingnum
to it. - Compute the difference
diff = prefix_sum - k
. Ifdiff
exists in the dictionary, it means there are subarrays whose sum equalsk
, and theircount
is stored in thedictionary
. - Increment
count
by the number of timesdiff
has occurred. - Update the dictionary by increasing the
count
ofprefix_sum
.
- For each element
-
Return the
count
:- This gives the total number of subarrays whose sum equals
k
.
- This gives the total number of subarrays whose sum equals
5. Product of Array Except Self (Leetcode:238)
Problem Statement
6. Increment Submatrices by One (Leetcode:2536)
Problem Statement
7. Range Sum Query 2D - Immutable (Leetcode:304)
Problem Statement
8. Power of Heroes (Leetcode:2681)
Problem Statement
9. Minimum Cost to Make Array Equal (Leetcode:2448)
Problem Statement
Line Sweep
Theory
Description
The Line Sweep Algorithm is a powerful technique used in computational geometry. It's an efficient way to solve problems like detecting intersections, finding the closest pair of points, or calculating the convex hull. Let’s break down the core concepts of this algorithm to understand how it works.
What is the Sweep Line?
The sweep line is an imaginary vertical line that moves across a plane from left to right. As it moves, it sweeps through the objects in its path, processing important events at each step. Imagine it like a curtain slowly moving across a stage, revealing different parts of a scene. In the context of algorithms, this "curtain" moves across the plane, checking and reacting to specific points where events occur.
The sweep line doesn’t just move randomly—it advances through key points, called events, which are defined based on the specific problem being solved. By focusing on these events, the algorithm can efficiently process only the relevant parts of the data.
What are Events?
An event is a point on the plane where something significant happens as the sweep line moves. These events are the heart of the algorithm because they trigger actions like adding or removing objects, or checking for interactions. Depending on the problem you're solving, events could include:
- Start events: The beginning of an object, such as the left endpoint of a line segment or the opening of a rectangle.
- End events: The end of an object, like the right endpoint of a line segment or the closing of a rectangle.
- Intersection events: When two objects, like two line segments, intersect or overlap.
The sweep line processes these events in order, one by one, and each event may lead to an update in the algorithm’s state—whether it’s adding a new object, removing one, or checking for an intersection.
How Does it Work?
Here’s how the line sweep algorithm works in practice:
-
Sort the events: The first step is to sort all the events in order of their position along the x-axis (left to right). For example, if you are working with line segments, this would mean sorting by the x-coordinate of the start and end points of each segment.
-
Process events in order: The sweep line starts at the leftmost event and moves to the right. For each event, you do something specific:
- When the sweep line reaches a start event (like the left endpoint of a line segment), the segment is added to a list of active objects.
- When the sweep line reaches an end event (like the right endpoint), that segment is removed from the list of active objects.
- At each step, the algorithm may check for intersections or other interactions between active objects (e.g., checking if newly added segments intersect with others already in the list).
By sorting and processing events in this way, the algorithm can handle complex geometric problems without having to check every possible pair of objects, which would be much slower.
Problems
1. Points That Intersect With Cars (Leetcode:2848)
Problem Statement
Code & Explaination
2. Check if All the Integers in a Range Are Covered (Leetcode:1893)
Problem Statement
Code & Explaination
3. Minimum Number of Arrows to Burst Balloons (Leetcode:452)
Problem Statement
Code & Explaination
4.Car Pooling (Leetcode:1094)
Problem Statement
Code & Explaination
5. My Calendar II (Leetcode:731)
Problem Statement
6. Number of Flowers in Full Bloom (Leetcode:2251)
Problem Statement
7. My Calendar III (Leetcode:732)
Problem Statement
8. Minimum Number of Taps to Open to Water a Garden (Leetcode:1326)
Problem Statement