1995. Count Special Quadruplets
Problem Description
In this LeetCode problem, we are given an integer array nums
. Our task is to find the count of unique quadruplets (a, b, c, d)
that fulfill two conditions:
- The sum of three elements at indices
a
,b
, andc
is equal to the element at indexd
:nums[a] + nums[b] + nums[c] == nums[d]
. - The indices
a
,b
,c
, andd
must follow a strict increasing order, such thata < b < c < d
.
The objective is to determine how many such quadruplets exist in the provided array.
Intuition
To solve this problem, the intuition is to iterate over the array in a structured way so that we can check possible combinations that fulfill the given conditions without redundancy. Instead of checking every possible quadruplet, which could be inefficient, we can use a counting approach to aid in this process.
The solution employs a hashmap, which in Python is provided by the Counter
class from the collections
module, to keep track of the differences between nums[d]
and nums[c]
. This is valuable because if we fix b
and c
, and find a d > c
, we can update our counter for the difference nums[d] - nums[c]
. Then, for all a < b
, we can directly check if the sum nums[a] + nums[b]
is present as a key in our counter. This represents the fact that nums[d]
exists such that nums[a] + nums[b] + nums[c] == nums[d]
.
The approach involves reverse iterating over the potential b
indices starting from the third-to-last index down to the first index (since a
must be less than b
). After fixing b
, we iterate over c
and d
to the end of the array and update the counter for each such pair. Then, in another loop moving from 0
to b
, we use the counter to check how many times nums[a] + nums[b]
appears as a sum nums[d] - nums[c]
, as that indicates valid quadruplets. The count of these occurrences is added to the total answer (ans
).
By using this method, we avoid the need to individually check each quadruplet and improve the efficiency of the algorithm significantly, leading to a solution that can handle arrays with larger numbers of elements.
Solution Approach
The implemented solution follows a three-pointer approach that uses the Counter
data structure to optimize the searching process for the sum condition specified in the problem statement.
Here is how the algorithm unfolds:
-
Initialize a variable
ans
to keep track of the total count of valid quadruplets and obtain the lengthn
of the givennums
array. -
Prepare a
Counter
object namedcounter
that is going to keep track of the frequency of the differencesnums[d] - nums[c]
observed thus far in the remaining part of the array to the right ofb
. -
Perform a reverse iteration over the potential
b
positions, starting fromn-3
and decrementing down to1
. This step ensures that there is enough space forc
andd
to the right, as the conditiona < b < c < d
must be respected. -
For each fixed
b
, iterate forwards through the indicesc
andd
, wherec
starts immediately afterb
andd
moves pastc
towards the end of the array. For eachc
andd
pair, update thecounter
to record the frequency ofnums[d] - nums[c]
. This setup aids in the future checking of the sum condition without extra iteration through the array. -
After populating the
counter
for a specificb
, start another loop from0
up tob - 1
to find valida
indices. Witha
andb
fixed, and the counter holding information for allc
andd
pairs beyondb
, we can find out how many timesnums[a] + nums[b]
appears as a sumnums[d] - nums[c]
. For eacha
, increment the total countans
by the value ofcounter[nums[a] + nums[b]]
, which represents the number of valid quadruplets with the currenta
andb
. -
Return the total count
ans
as the final result.
The Counter
is crucial as it serves as a hash map (dictionary in Python) to efficiently record and access frequency counts of specific sum differences. This allows for constant-time lookups which are much faster than iterating over the array to find matching sums for each potential quadruplet.
By optimizing the lookup process and managing how we iterate through the array, this solution avoids the naive approach that would otherwise have a much higher time complexity. The use of a Counter
combines with strategic pointer movement to solve this problem in a more efficient manner.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider an example to illustrate the solution approach. Suppose we have the following integer array nums
:
nums = [1, 1, 2, 2, 3, 3, 4]
Let's walk through the algorithm:
-
Initialize
ans
to0
as the counter and obtain the length ofnums
, which isn = 7
in this case. -
Prepare an empty
Counter
object namedcounter
to keep track of the frequency of the differences observed. -
Begin reverse iteration for
b
starting fromn-3
, which is index4
. This leaves room forc
andd
.For
b = 4
(nums[b]
is 3):- We initialize
counter
to an empty state again since we shiftb
leftward. - We start by setting
c = 5
andd = 6
. Sincenums[d] - nums[c] = 4 - 3 = 1
, updatecounter
to havecounter[1] = 1
. - There's no more room to increment
c
andd
, so we proceed to the next step.
- We initialize
-
Now, for this
b
(index4
), we loop over alla
less thanb
. We can only considera
at indices0
,1
,2
, and3
.Checking each
a
:- For
a = 0
: The sumnums[a] + nums[b] = 1 + 3 = 4
. The counter has1
for key1
, which does not match, so no valid quadruplet is found here. - For
a = 1
: The sumnums[a] + nums[b] = 1 + 3 = 4
. Again, no valid quadruplet. - For
a = 2
: The sumnums[a] + nums[b] = 2 + 3 = 5
. Again, no valid quadruplet. - For
a = 3
: The sumnums[a] + nums[b] = 2 + 3 = 5
. Again, no valid quadruplet.
- For
-
Move
b
to the next index,3
(nums[b]
is 2), and repeat steps 3 and 4.For
b = 3
:- Reset
counter
. - Start
c
at4
andd > c
. Supposec = 4
andd = 5
,nums[d] - nums[c] = 3 - 2 = 1
, we update thecounter
tocounter[1] = 1
. - Move
d
to6
,nums[d] - nums[c] = 4 - 2 = 2
, the counter is updated tocounter[2] = 1
. - There's no more room for
d
, movec
to5
, andd
to6
,nums[d] - nums[c] = 4 - 3 = 1
, the counter is nowcounter[1] = 2
since we've observed another1
.
Loop over
a
indices less than3
:- For
a = 0
: The sumnums[a] + nums[b] = 1 + 2 = 3
. This does not match any key in the counter. - For
a = 1
: The sumnums[a] + nums[b] = 1 + 2 = 3
. Again, no match and no valid quadruplet. - For
a = 2
: The sumnums[a] + nums[b] = 2 + 2 = 4
. The counter has2
for key2
, which matches. We found one valid quadruplet,(nums[a], nums[b], nums[c], nums[d]) = (2, 2, 2, 4)
. So,ans = ans + counter[4 - nums[b]] = 0 + 1 = 1
.
- Reset
-
We would continue to reverse iterate
b
down to the index1
and repeat the above steps. However, for the sake of brevity, let's stop the walkthrough here.
The total ans
at the end of the full iteration (not shown for the entire array to keep this brief) would give us the count of all unique quadruplets where the sum of elements at a
, b
, and c
equals the element at d
, while maintaining the order a < b < c < d
.
This walkthrough demonstrates how the combination of reverse iteration and the use of a Counter
can efficiently solve the problem by strategically checking for the sums that align with our conditions.
Solution Implementation
1from collections import Counter # Importing Counter from collections module
2
3class Solution:
4 def countQuadruplets(self, nums: List[int]) -> int:
5 count = 0 # Initialize the count of quadruplets to 0
6 length = len(nums) # Store the length of the input list
7
8 # Counter to track the frequency of (nums[d] - nums[c])
9 frequency_counter = Counter()
10
11 # Iterate from the third last element down to the second element
12 for b_index in range(length - 3, 0, -1):
13 # Start c_index from the element right after b_index
14 c_index = b_index + 1
15
16 # Update the frequency counter for each pair (c, d)
17 for d_index in range(c_index + 1, length):
18 frequency_counter[nums[d_index] - nums[c_index]] += 1
19
20 # Count quadruplets for each pair (a, b_index)
21 for a_index in range(b_index):
22 count += frequency_counter[nums[a_index] + nums[b_index]]
23
24 return count # Return the final count of quadruplets
25
1class Solution {
2 public int countQuadruplets(int[] nums) {
3 int count = 0; // Holds the number of valid quadruplets found
4 int length = nums.length; // The length of the input array
5
6 // Counter array to hold the frequency of differences between nums[d] and nums[c]
7 // Initialized to 310 based on the constraints that nums[i] <= 100
8 int[] differenceCounter = new int[310];
9
10 // We are iterating from the third last element down to the second element
11 // because we need at least two more elements for 'c' and 'd'
12 for (int b = length - 3; b > 0; b--) {
13 int c = b + 1;
14
15 // We are calculating the frequency of the difference between nums[d] and nums[c]
16 // and storing it in our counter. This will help us to check for quadruplets quickly later.
17 for (int d = c + 1; d < length; d++) {
18 int difference = nums[d] - nums[c];
19 if (difference >= 0) { // Ensure we don't have a negative index for the counter array
20 ++differenceCounter[difference];
21 }
22 }
23
24 // Now we check for all 'a' values that come before 'b'
25 // For each 'a', if the sum of nums[a] and nums[b] exists as an index in the counter array,
26 // it means there exists a 'c' and 'd' such that nums[a] + nums[b] = nums[c] + nums[d].
27 // Hence, we add the frequency (number of occurrences) of that sum (difference) to the count of quadruplets.
28 for (int a = 0; a < b; ++a) {
29 int sum = nums[a] + nums[b];
30 count += differenceCounter[sum];
31 }
32 }
33
34 return count; // Return the total count of quadruplets found
35 }
36}
37
1class Solution {
2public:
3 // Function to count the number of special quadruplets [a, b, c, d] in the given array.
4 // A quadruplet is considered special if a + b + c = d.
5 int countQuadruplets(vector<int>& nums) {
6 int count = 0; // This will hold the final count of special quadruplets.
7 int size = nums.size(); // Get the size of the input array.
8 vector<int> frequency(310, 0); // Array to store the frequencies of values for the differences of c and d.
9
10 // Start from the second-to-last element and go backwards, as this is the 'b' in the quadruplet.
11 for (int b = size - 3; b > 0; --b) {
12 // 'c' is always to the right of 'b', so start from 'b + 1'.
13 for (int c = b + 1; c < size - 1; ++c) {
14 // 'd' is always to the right of 'c', so start from 'c + 1'.
15 for (int d = c + 1; d < size; ++d) {
16 if (nums[d] - nums[c] >= 0) {
17 // Increment the frequency of this particular difference.
18 frequency[nums[d] - nums[c]]++;
19 }
20 }
21 }
22 // Now looking for 'a' which is to the left of 'b'.
23 for (int a = 0; a < b; ++a) {
24 // If a sum of nums[a] and nums[b] happened to be the difference
25 // previously recorded, it contributes to the total count.
26 count += frequency[nums[a] + nums[b]];
27 }
28 // Reset the frequency array for the next iteration.
29 // This ensures that we only count the differences relevant to the current 'b'.
30 fill(frequency.begin(), frequency.end(), 0);
31 }
32 return count; // Return the final count of special quadruplets.
33 }
34};
35
1// Define a global array to store input numbers.
2let nums: number[] = [];
3
4// Global array to store the frequencies of values for the differences of c and d.
5let frequency: number[] = new Array(310).fill(0);
6
7// Function to count the number of special quadruplets [a, b, c, d] in 'nums' array.
8// A quadruplet is considered special if a + b + c = d.
9function countQuadruplets(): number {
10 let count = 0; // This will hold the final count of special quadruplets.
11 let size = nums.length; // Get the size of the input array.
12
13 // Start from the second-to-last element and go backwards, as this is the 'b' in the quadruplet.
14 for (let b = size - 3; b > 0; --b) {
15 // 'c' is always to the right of 'b', so start from 'b + 1'.
16 for (let c = b + 1; c < size - 1; ++c) {
17 // 'd' is always to the right of 'c', so start from 'c + 1'.
18 for (let d = c + 1; d < size; ++d) {
19 if (nums[d] - nums[c] >= 0) {
20 // Increment the frequency of this particular difference.
21 frequency[nums[d] - nums[c]]++;
22 }
23 }
24 }
25 // Now looking for 'a' which is to the left of 'b'.
26 for (let a = 0; a < b; ++a) {
27 // If a sum of nums[a] and nums[b] happened to be the difference
28 // previously recorded, it contributes to the total count.
29 count += frequency[nums[a] + nums[b]];
30 }
31 // Reset the frequency array for the next iteration.
32 // This ensures that we only count the differences relevant to the current 'b'.
33 frequency.fill(0);
34 }
35 return count; // Return the final count of special quadruplets.
36}
37
38// Example usage:
39// nums = [1, 2, 3, 4];
40// let result = countQuadruplets();
41// console.log(result); // Should log the count of special quadruplets
42
Time and Space Complexity
The given Python code defines a method countQuadruplets
which counts the number of quadruples (a, b, c, d) in an array nums
where a < b < c < d
and nums[a] + nums[b] + nums[c] == nums[d]
.
Time Complexity:
The outermost loop runs from the third-to-last element to the beginning (n - 3
to 1
), which gives us at most n
iterations. Inside this loop, we have two nested loops:
- The first nested loop iterates over the range from
c + 1
ton - 1
, wherec
starts fromb + 1
. In the worst case, this loop runs forn - b - 2
iterations. - The second nested loop runs from
0
tob - 1
. In the worst-case scenario, whereb
is close ton / 2
, this loop also contributes a factor ofn/2
iterations.
The nested loops are not entirely independent: as b
decreases, the number of iterations in the first nested loop increases, but the iterations in the second nested loop decrease proportionally.
The time complexity of these nested loops is hence proportional to the sum of the two sequences, which forms an arithmetic progression from 1
to approximately n/2
. The sum of the first n/2
positive integers is given by (n/2) * ((n/2) + 1) / 2
, simplifying to O(n^2)
.
Therefore, the overall time complexity of the function is O(n^2)
.
Space Complexity:
The space complexity is governed by the Counter
object, which stores a mapping of the differences between nums[d]
and nums[c]
. In the worst case, the Counter
could store up to n
different values if all the differences are unique. This gives us a space complexity of O(n)
for the counter
variable.
Thus, the space complexity of the function is O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What's the relationship between a tree and a graph?
Recommended Readings
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!