Problem Overview
LeetCode 1512 — Number of Good Pairs — gives you an array of integers nums and asks you to return the number of good pairs. A pair (i, j) is considered good if nums[i] == nums[j] and i < j. The indices must be distinct and i must be strictly less than j — so you are counting ordered pairs where the smaller index comes first.
For example, given nums = [1, 2, 3, 1, 1, 3], the answer is 4. The good pairs are: (0,3), (0,4), (3,4) — the three pairs among the three 1s — and (2,5) — the one pair between the two 3s. The single 2 forms no pairs.
This is an Easy-rated problem but it introduces a foundational pattern: instead of iterating all pairs with a nested loop, count value frequencies and apply combinatorics to derive the answer in linear time.
- 1 <= nums.length <= 100
- 1 <= nums[i] <= 100
- Goal: count pairs (i,j) where nums[i] == nums[j] and i < j
- Output: a single integer — the total number of good pairs
- Brute force O(n^2) works within constraints, but O(n) frequency approach is optimal
Brute Force Approach
The straightforward solution uses two nested loops: for each index i, scan all indices j > i and check if nums[i] == nums[j]. Every matching pair increments a counter. This is O(n^2) time and O(1) space. For the given constraint of n <= 100, this runs in at most 4,950 comparisons — well within limits.
The brute force is correct and simple to implement, but it misses a key mathematical insight that makes the problem much more elegant. When you find three 1s in the array, you are not just lucky — you know immediately that they form exactly 3 pairs without checking each one individually.
The brute force teaches you what you are counting. The optimized approach teaches you how to count it smarter.
Key Insight
Instead of checking pairs one by one, count frequencies. If a value appears k times in the array, it forms exactly k*(k-1)/2 good pairs — the combination formula nC2. Sum this across all values for the total count in O(n) time.
Frequency Counting Approach
The optimal solution builds a frequency map of each value in nums. For each value v with frequency k, compute k*(k-1)/2 and add it to the running total. This is because choosing any 2 indices from k equal elements gives one valid good pair — and the number of ways to choose 2 items from k is the combination C(k,2) = k*(k-1)/2.
The implementation is clean: one pass to build the frequency map using a HashMap or Counter, then one pass over the map entries to apply the formula. Total time is O(n) for counting plus O(u) for summing where u is the number of unique values — overall O(n). Space is O(u) for the map.
This approach scales to arbitrarily large arrays where the brute force would time out. For nums.length = 10^5 and all identical elements, brute force does ~5 billion operations; the frequency approach does exactly 1 formula evaluation.
- 1Initialize an empty frequency map (HashMap or Counter)
- 2Iterate nums: for each value, increment its count in the map
- 3Initialize result = 0
- 4Iterate the map entries: for each (value, k), add k*(k-1)/2 to result
- 5Return result
Why k*(k-1)/2 Works
The formula k*(k-1)/2 is the combination C(k, 2) — the number of ways to choose 2 items from k items without regard to order. Since every pair of indices (i, j) with i < j and nums[i] == nums[j] is a good pair, and since we want unordered pairs (only i < j, not both orderings), we need exactly C(k, 2) per value group.
Formally: C(k, 2) = k! / (2! * (k-2)!) = k*(k-1)/2. For frequency 3: 3*2/2 = 3 pairs. For frequency 4: 4*3/2 = 6 pairs. For frequency 5: 5*4/2 = 10 pairs. You can verify: four 1s at positions 0,1,2,3 form pairs (0,1),(0,2),(0,3),(1,2),(1,3),(2,3) — exactly 6.
The formula increments quadratically with frequency, which is why detecting high-frequency values and applying the formula is so much more efficient than enumerating pairs individually.
Combination Formula Family
The nC2 combination formula appears in many counting problems: number of handshakes in a room of k people, number of edges in a complete graph of k nodes, number of matches in a round-robin tournament with k teams. Recognizing the "choose 2 from k" pattern instantly unlocks the O(n) solution.
Online Counting Alternative
There is an elegant alternative that avoids the combination formula entirely: the online counting approach. Iterate through the array; for each element, look up how many times that value has already appeared (its current count in the map), and add that count directly to the result. Then increment the count. This works because each new occurrence pairs with every previous occurrence of the same value.
For example, processing [1, 1, 1]: first 1 — count[1]=0, add 0, count[1] becomes 1. Second 1 — count[1]=1, add 1, count[1] becomes 2. Third 1 — count[1]=2, add 2, count[1] becomes 3. Total: 0+1+2 = 3 = C(3,2). The result is identical to the formula approach but derived step by step.
The online approach naturally enforces the i < j constraint: when processing element at index j, count[v] holds only the number of occurrences before j, so every counted pairing has a smaller index on the left. No formula required, no two-pass algorithm needed.
- 1Initialize frequency map and result = 0
- 2For each element v in nums:
- 3 Add count[v] to result (pairs formed with all previous occurrences of v)
- 4 Increment count[v] by 1
- 5Return result
Code Walkthrough — Python and Java
Python one-liner using Counter and the formula: from collections import Counter; return sum(k*(k-1)//2 for k in Counter(nums).values()). This is concise and idiomatic. The online version with defaultdict: count = defaultdict(int); result = 0; for v in nums: result += count[v]; count[v] += 1; return result. Both are O(n) time and O(n) space.
Java formula approach: Map<Integer,Integer> freq = new HashMap<>(); for (int v : nums) freq.merge(v, 1, Integer::sum); int res = 0; for (int k : freq.values()) res += k*(k-1)/2; return res. Java online approach: Map<Integer,Integer> cnt = new HashMap<>(); int res = 0; for (int v : nums) { res += cnt.getOrDefault(v,0); cnt.merge(v,1,Integer::sum); } return res.
Both languages achieve O(n) time and O(u) space where u is the number of unique values. In the worst case u = n (all distinct, zero good pairs). In the best case u = 1 (all identical, n*(n-1)/2 good pairs). The space overhead from the HashMap is always bounded by the input size.
Interview Tip
The online approach is often cleaner in interviews: it avoids explaining the combination formula and naturally handles the i < j constraint without a second pass. Many interviewers prefer seeing the incremental logic over the batch formula — it shows you understand the problem structurally, not just mathematically.