Facebook Pixel

1995. Count Special Quadruplets

EasyArrayHash TableEnumeration
Leetcode Link

Problem Description

You are given an integer array nums that is 0-indexed (meaning the first element is at index 0). Your task is to count how many distinct quadruplets of indices (a, b, c, d) exist in the array that satisfy both of the following conditions:

  1. The sum of three elements equals the fourth element: nums[a] + nums[b] + nums[c] == nums[d]
  2. The indices are in strictly increasing order: a < b < c < d

A quadruplet consists of four different positions in the array. You need to find all valid combinations where the values at the first three positions add up to equal the value at the fourth position, while maintaining the strict ordering of indices.

For example, if nums = [1, 2, 3, 6], then the quadruplet (0, 1, 2, 3) would be valid because:

  • nums[0] + nums[1] + nums[2] = 1 + 2 + 3 = 6 = nums[3]
  • The indices satisfy 0 < 1 < 2 < 3

The solution uses a brute force approach with four nested loops to check every possible combination of four indices. For each valid combination where the indices are in increasing order, it checks if the sum condition is met. If both conditions are satisfied, the counter is incremented. The time complexity is O(n^4) where n is the length of the array.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we need to find all valid quadruplets with specific conditions, the most straightforward approach is to examine every possible combination of four indices. Since we're looking for groups of four elements where three sum to equal the fourth, and the indices must be in strict ascending order, we can think of this as a selection problem.

The key insight is that with the constraint a < b < c < d, we're essentially choosing 4 positions from the array in order. This naturally leads to using nested loops where each inner loop starts from the position after the previous loop's current index. This ensures we never revisit the same combination and automatically maintains the ordering requirement.

For each combination of four indices, we simply check if nums[a] + nums[b] + nums[c] == nums[d]. The equation has a fixed structure - we always sum the first three values and compare to the fourth. This makes the checking condition straightforward.

While we could optimize this problem using techniques like hash maps to store sums or two-pointer approaches, the brute force method with four nested loops is the most intuitive starting point. It directly translates the problem requirements into code: pick four indices in order, check if they satisfy the sum condition, and count the valid ones.

The range limits in each loop (n-3 for the first, n-2 for the second, etc.) ensure we always have enough elements remaining to form a complete quadruplet. This prevents index out-of-bounds errors and unnecessary iterations.

Solution Approach

The implementation uses a brute force approach with four nested loops to enumerate all possible quadruplets. Let's walk through the algorithm step by step:

  1. Initialize variables: We set ans = 0 to count valid quadruplets and store the array length in n for convenience.

  2. First loop (index a): The outermost loop iterates from 0 to n-3. We stop at n-3 because we need at least 3 more elements after position a to form a valid quadruplet.

  3. Second loop (index b): For each fixed a, we iterate b from a+1 to n-2. Starting from a+1 ensures a < b, and stopping at n-2 leaves room for indices c and d.

  4. Third loop (index c): For each fixed pair (a, b), we iterate c from b+1 to n-1. This maintains b < c and leaves at least one position for d.

  5. Fourth loop (index d): For each triple (a, b, c), we iterate d from c+1 to n. This ensures c < d and completes our quadruplet.

  6. Check the sum condition: Inside the innermost loop, we check if nums[a] + nums[b] + nums[c] == nums[d]. If this condition is satisfied, we've found a valid quadruplet and increment our answer counter.

  7. Return the result: After examining all possible quadruplets, we return the total count.

The algorithm systematically explores all combinations where a < b < c < d by design of the loop structure. Each inner loop starts one position after its parent loop's current index, which automatically enforces the ordering constraint without additional checks.

The time complexity is O(n^4) due to the four nested loops, and the space complexity is O(1) as we only use a constant amount of extra space for the counter and loop variables.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with nums = [1, 5, 3, 2, 6].

We need to find all quadruplets (a, b, c, d) where a < b < c < d and nums[a] + nums[b] + nums[c] == nums[d].

Step-by-step iteration:

Starting with a = 0 (nums[0] = 1):

  • b = 1 (nums[1] = 5):
    • c = 2 (nums[2] = 3):
      • d = 3: Check if 1 + 5 + 3 = 9 equals nums[3] = 2? No.
      • d = 4: Check if 1 + 5 + 3 = 9 equals nums[4] = 6? No.
    • c = 3 (nums[3] = 2):
      • d = 4: Check if 1 + 5 + 2 = 8 equals nums[4] = 6? No.
  • b = 2 (nums[2] = 3):
    • c = 3 (nums[3] = 2):
      • d = 4: Check if 1 + 3 + 2 = 6 equals nums[4] = 6? Yes! Count = 1

Moving to a = 1 (nums[1] = 5):

  • b = 2 (nums[2] = 3):
    • c = 3 (nums[3] = 2):
      • d = 4: Check if 5 + 3 + 2 = 10 equals nums[4] = 6? No.

The algorithm continues checking all remaining combinations, but no other valid quadruplets are found.

Final result: 1 valid quadruplet at indices (0, 2, 3, 4) where 1 + 3 + 2 = 6.

The nested loop structure ensures we check all possible combinations exactly once while maintaining the required index ordering. Each loop's starting point (a+1, b+1, c+1) guarantees the strict inequality constraint is satisfied without additional conditional checks.

Solution Implementation

1class Solution:
2    def countQuadruplets(self, nums: List[int]) -> int:
3        """
4        Count the number of quadruplets (a, b, c, d) where:
5        - 0 <= a < b < c < d < n
6        - nums[a] + nums[b] + nums[c] == nums[d]
7      
8        Args:
9            nums: List of integers
10          
11        Returns:
12            Number of valid quadruplets
13        """
14        # Initialize counter and get array length
15        count = 0
16        n = len(nums)
17      
18        # Iterate through all possible positions for first element
19        # Leave space for at least 3 more elements after it
20        for i in range(n - 3):
21            # Iterate through all possible positions for second element
22            # Start after first element, leave space for 2 more elements
23            for j in range(i + 1, n - 2):
24                # Iterate through all possible positions for third element
25                # Start after second element, leave space for 1 more element
26                for k in range(j + 1, n - 1):
27                    # Iterate through all possible positions for fourth element
28                    # Start after third element, go until end of array
29                    for l in range(k + 1, n):
30                        # Check if sum of first three elements equals fourth element
31                        if nums[i] + nums[j] + nums[k] == nums[l]:
32                            count += 1
33      
34        return count
35
1class Solution {
2    /**
3     * Count quadruplets where nums[a] + nums[b] + nums[c] = nums[d]
4     * with indices satisfying a < b < c < d
5     * 
6     * @param nums input array of integers
7     * @return count of valid quadruplets
8     */
9    public int countQuadruplets(int[] nums) {
10        int count = 0;
11        int n = nums.length;
12      
13        // Iterate through all possible index positions for 'a'
14        // Stop at n-3 to ensure space for b, c, and d
15        for (int indexA = 0; indexA < n - 3; indexA++) {
16          
17            // Iterate through all possible index positions for 'b'
18            // Start after 'a' and stop at n-2 to ensure space for c and d
19            for (int indexB = indexA + 1; indexB < n - 2; indexB++) {
20              
21                // Iterate through all possible index positions for 'c'
22                // Start after 'b' and stop at n-1 to ensure space for d
23                for (int indexC = indexB + 1; indexC < n - 1; indexC++) {
24                  
25                    // Iterate through all possible index positions for 'd'
26                    // Start after 'c' and go until the end of array
27                    for (int indexD = indexC + 1; indexD < n; indexD++) {
28                      
29                        // Check if the sum of first three elements equals the fourth
30                        if (nums[indexA] + nums[indexB] + nums[indexC] == nums[indexD]) {
31                            count++;
32                        }
33                    }
34                }
35            }
36        }
37      
38        return count;
39    }
40}
41
1class Solution {
2public:
3    int countQuadruplets(vector<int>& nums) {
4        int count = 0;
5        int n = nums.size();
6      
7        // Iterate through all possible combinations of four indices (a, b, c, d)
8        // where a < b < c < d
9        for (int i = 0; i < n - 3; ++i) {           // First index
10            for (int j = i + 1; j < n - 2; ++j) {   // Second index
11                for (int k = j + 1; k < n - 1; ++k) { // Third index
12                    for (int l = k + 1; l < n; ++l) { // Fourth index
13                        // Check if the sum of first three elements equals the fourth element
14                        if (nums[i] + nums[j] + nums[k] == nums[l]) {
15                            ++count;
16                        }
17                    }
18                }
19            }
20        }
21      
22        return count;
23    }
24};
25
1function countQuadruplets(nums: number[]): number {
2    let count: number = 0;
3    const n: number = nums.length;
4  
5    // Iterate through all possible combinations of four indices (a, b, c, d)
6    // where a < b < c < d
7    for (let i = 0; i < n - 3; i++) {           // First index (a)
8        for (let j = i + 1; j < n - 2; j++) {   // Second index (b)
9            for (let k = j + 1; k < n - 1; k++) { // Third index (c)
10                for (let l = k + 1; l < n; l++) { // Fourth index (d)
11                    // Check if the sum of first three elements equals the fourth element
12                    // nums[a] + nums[b] + nums[c] = nums[d]
13                    if (nums[i] + nums[j] + nums[k] === nums[l]) {
14                        count++;
15                    }
16                }
17            }
18        }
19    }
20  
21    return count;
22}
23

Time and Space Complexity

Time Complexity: O(n^4)

The code uses four nested loops to iterate through all possible quadruplets (a, b, c, d) where a < b < c < d.

  • The outer loop runs n-3 times
  • The second loop runs approximately n-2 times for each iteration of the first loop
  • The third loop runs approximately n-1 times for each iteration of the second loop
  • The innermost loop runs approximately n times for each iteration of the third loop

This results in approximately (n-3) * (n-2) * (n-1) * n / 4! iterations in the worst case, which simplifies to O(n^4).

Space Complexity: O(1)

The algorithm only uses a constant amount of extra space:

  • Variable ans to store the count
  • Variable n to store the length of the array
  • Loop variables a, b, c, d

No additional data structures are created that depend on the input size, so the space complexity is constant.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Loop Boundaries

One of the most common mistakes is setting incorrect loop boundaries, which can lead to either missing valid quadruplets or causing index out of bounds errors.

Incorrect Implementation:

# Wrong: This misses valid quadruplets or causes errors
for i in range(n):           # Should be n-3
    for j in range(i+1, n):   # Should be n-2
        for k in range(j+1, n): # Should be n-1
            for l in range(k+1, n):
                if nums[i] + nums[j] + nums[k] == nums[l]:
                    count += 1

Why it's wrong: The outer loops don't leave enough room for the remaining indices. For example, if i = n-1, there's no room for j, k, and l.

Correct Implementation:

for i in range(n - 3):        # Leaves room for j, k, l
    for j in range(i + 1, n - 2):  # Leaves room for k, l
        for k in range(j + 1, n - 1):  # Leaves room for l
            for l in range(k + 1, n):
                if nums[i] + nums[j] + nums[k] == nums[l]:
                    count += 1

2. Using Same Index Multiple Times

Another pitfall is accidentally allowing the same index to be used multiple times in a quadruplet.

Incorrect Implementation:

# Wrong: Allows same index to be used multiple times
for i in range(n - 3):
    for j in range(i, n - 2):    # Should start at i+1, not i
        for k in range(j, n - 1):  # Should start at j+1, not j
            for l in range(k, n):    # Should start at k+1, not k
                if nums[i] + nums[j] + nums[k] == nums[l]:
                    count += 1

Why it's wrong: Starting inner loops at the same index as the outer loop variable allows duplicate indices like (0, 0, 1, 2), which violates the requirement that all four indices must be distinct.

3. Optimization Opportunity: Early Termination

While not a bug, the brute force solution can be optimized with early termination when the sum becomes impossible.

Enhanced Implementation with Optimization:

def countQuadruplets(self, nums: List[int]) -> int:
    count = 0
    n = len(nums)
  
    # Sort to enable early termination (optional optimization)
    # Note: This changes indices, so only use if you don't need original positions
  
    for i in range(n - 3):
        for j in range(i + 1, n - 2):
            for k in range(j + 1, n - 1):
                target_sum = nums[i] + nums[j] + nums[k]
                for l in range(k + 1, n):
                    if nums[l] == target_sum:
                        count += 1
  
    return count

4. Memory-Efficient Alternative Using HashMap

For larger arrays, a more efficient approach uses a hashmap to reduce time complexity from O(n⁴) to O(n²).

Optimized Solution:

def countQuadruplets(self, nums: List[int]) -> int:
    count = 0
    n = len(nums)
  
    # Use hashmap to store sums of pairs
    for c in range(2, n - 1):
        sum_count = {}
        # Count all possible a+b where a < b < c
        for b in range(1, c):
            for a in range(b):
                sum_ab = nums[a] + nums[b]
                sum_count[sum_ab] = sum_count.get(sum_ab, 0) + 1
      
        # For each d > c, check if nums[d] - nums[c] exists in our map
        for d in range(c + 1, n):
            target = nums[d] - nums[c]
            if target in sum_count:
                count += sum_count[target]
  
    return count

This approach transforms the equation nums[a] + nums[b] + nums[c] = nums[d] into nums[a] + nums[b] = nums[d] - nums[c] and uses a hashmap to count matching pairs efficiently.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More