1512. Number of Good Pairs - Explanation

Problem Link



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Hash Maps - Using dictionaries to count frequencies of elements in O(1) time
  • Combinatorics - Understanding how to count pairs using the formula n*(n-1)/2 for choosing 2 items from n

1. Brute Force

Intuition

A good pair is defined as a pair (i, j) where i < j and nums[i] == nums[j]. The simplest approach is to check every possible pair of indices and count those that satisfy both conditions.

Algorithm

  1. Initialize a counter res to zero.
  2. Use two nested loops: the outer loop picks index i, and the inner loop picks index j where j > i.
  3. For each pair, if nums[i] == nums[j], increment res.
  4. Return res.
class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        res = 0
        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                if nums[i] == nums[j]:
                    res += 1
        return res

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity: O(1)O(1)

2. Hash Map (Math)

Intuition

If a value appears c times, the number of good pairs using that value equals the number of ways to choose 2 indices from c positions, which is c * (c - 1) / 2. We can count frequencies first, then sum up the pairs for each value.

Algorithm

  1. Count the frequency of each number using a hash map.
  2. For each frequency c, add c * (c - 1) / 2 to the res.
  3. Return the total sum.
class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        count = Counter(nums)
        res = 0
        for num, c in count.items():
            res += c * (c - 1) // 2
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

3. Hash Map

Intuition

Instead of counting all frequencies first and then computing pairs, we can count pairs on the fly. As we traverse the array, each new occurrence of a value can form a good pair with every previous occurrence of that same value. We track the count of each value seen so far and add it to the res before updating the count.

Algorithm

  1. Initialize a hash map to store the count of each number seen so far.
  2. For each number in the array:
    • Add the current count of that number to the res (this is the number of new pairs formed).
    • Increment the count of that number in the map.
  3. Return the res.
class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        count = defaultdict(int)
        res = 0
        for num in nums:
            res += count[num]
            count[num] += 1
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

Common Pitfalls

Double Counting Pairs

A good pair requires i < j, meaning each pair should only be counted once. When using nested loops, ensure the inner loop starts from i + 1 rather than 0. Similarly, when using the mathematical formula c * (c - 1) / 2, this already accounts for unique pairs and should not be doubled.

Integer Overflow in Pair Calculation

When using the formula c * (c - 1) / 2 to calculate the number of pairs for a frequency c, the multiplication can overflow for large values if using 32-bit integers. In languages with fixed-size integers, ensure you use an appropriate data type or perform the division before the multiplication becomes too large.