1512. Number of Good Pairs
Problem Description
The problem gives us an array nums
of integers. We need to find the total number of 'good pairs' in this array. A 'good pair' is defined as a pair of indices (i, j)
such that i < j
and nums[i] == nums[j]
. In simple terms, we must count how many pairs of elements have the same value, where the first element comes before the second element in the array.
Intuition
To solve this problem, we use a hashmap (dictionary in Python) to keep track of the number of times each value appears in the array as we iterate through it. This approach helps us to find 'good pairs' efficiently.
Here's the thinking process for arriving at this solution:
- We initialize a counter
cnt
to keep the frequency of each element we've seen so far. - We initialize a variable
ans
to keep the running count of good pairs. - We iterate over each element
x
innums
. - For every element
x
, we addcnt[x]
toans
. Why? Because ifcnt[x]
is the number of times we've seenx
so far, then there arecnt[x]
ways to form a 'good pair' withx
being the second element. - After counting the 'good pairs' for
x
, we incrementcnt[x]
since we've just seen anotherx
. - After the loop ends,
ans
will hold the total number of 'good pairs'.
By counting incrementally with each new element, we avoid the need for nested loops, which reduces the time complexity significantly from O(n^2) to O(n).
Learn more about Math patterns.
Solution Approach
The solution for counting the number of good pairs uses a hashmap as an auxiliary data structure to store the frequency of each element in the array. In Python, we use the Counter
class from the collections
module for this purpose. The approach taken in the solution is both efficient and straightforward to implement.
Here are the details of the implementation:
-
We define a class
Solution
with a methodnumIdenticalPairs
that takes a list of integersnums
as input and returns an integer. -
Within the method, we initialize our answer variable
ans
to 0, which will eventually hold the total number of good pairs. -
We then initialize our counter
cnt
as an instance ofCounter
, which is a subclass of the dictionary in Python, specifically designed to count hashable objects. -
We begin a loop over each element
x
innums
:-
For each element
x
, we first increment our answerans
by the current count ofx
incnt
:ans += cnt[x]
This is based on the idea that if we have already encountered
x
'n' times, then there are 'n' pairs that can be formed with this current 'x' as the second element of the pair. -
We then increment the count of
x
in our counter:cnt[x] += 1
This is necessary to reflect that we have come across another instance of
x
.
-
-
After completing the loop, we have counted all good pairs, and the
ans
variable now contains the correct answer. -
Lastly, we return
ans
as the result.
The key algorithmic idea here is to efficiently keep track of past occurrences of elements to calculate the number of good pairs without needing to compare each pair of elements individually, which would otherwise result in a much slower algorithm.
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 array nums = [1, 2, 3, 1, 1, 3]
.
Now apply the steps described in the solution approach:
- Initialize
ans
to 0, as we have not yet counted any 'good pairs'. - Initialize
cnt
as an emptyCounter
object.
Now let's iterate over each element x
in nums
:
-
First element is
1
:ans += cnt[1]
which is 0 since1
has not appeared before.cnt[1] += 1
so nowcnt
is{1:1}
.
-
Second element is
2
:ans += cnt[2]
which is 0 since2
has not appeared before.cnt[2] += 1
, nowcnt
is{1:1, 2:1}
.
-
Third element is
3
:ans += cnt[3]
, which is 0 since3
has not appeared before.cnt[3] += 1
, nowcnt
is{1:1, 2:1, 3:1}
.
-
Fourth element is
1
again:ans += cnt[1]
which is 1, reflecting the first appearance of1
.cnt[1] += 1
, nowcnt
is{1:2, 2:1, 3:1}
andans
is 1.
-
Fifth element is
1
once more:ans += cnt[1]
which is 2, as we've previously encountered1
twice.cnt[1] += 1
, socnt
becomes{1:3, 2:1, 3:1}
andans
updates to 3.
-
Last element is
3
again:ans += cnt[3]
which is 1, reflecting the first appearance of3
.cnt[3] += 1
, nowcnt
is{1:3, 2:1, 3:2}
andans
‘s final value is 4.
At the end of the loop, ans
holds the total number of good pairs which is 4. These pairs are (0, 3)
, (0, 4)
, (1, 5)
, and (3, 4)
since they comply with the condition that i < j
and nums[i] == nums[j]
.
Finally, we return the value of ans
, which is 4
in this example.
This example adequately demonstrates how the algorithm works as intended, efficiently counting the number of good pairs in the array using the hashmap cnt
to keep track of the occurrences of each element.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def numIdenticalPairs(self, nums: List[int]) -> int:
5 # Initialize the count of good pairs to zero
6 good_pairs_count = 0
7
8 # Create a Counter object to track the occurrences of each number in the list
9 occurrences = Counter()
10
11 # Iterate over each number in the input list
12 for number in nums:
13 # For each number, add the current count of that number to good_pairs_count
14 # This utilizes the property that a pair is formed for each previous occurrence of the same number
15 good_pairs_count += occurrences[number]
16
17 # Increment the count for this number
18 occurrences[number] += 1
19
20 # Return the final count of good pairs
21 return good_pairs_count
22
1class Solution {
2 public int numIdenticalPairs(int[] nums) {
3 int goodPairs = 0; // This will hold the count of good pairs
4 int[] count = new int[101]; // Array to store the frequency of numbers (since the max number is 100)
5
6 for (int number : nums) {
7 goodPairs += count[number]; // Add the count of the current number to the good pairs count
8 count[number]++; // Increment the frequency of the current number
9 }
10
11 return goodPairs; // Return the total count of good pairs
12 }
13}
14
1#include <vector>
2
3class Solution {
4public:
5 int numIdenticalPairs(std::vector<int>& nums) {
6 int goodPairsCount = 0; // Initialize a count for good pairs
7 int counts[101] = {0}; // Initialize an array to store the frequency of each number, assuming numbers fall within 1 to 100
8
9 // Iterate over the input vector 'nums'
10 for (int num : nums) {
11 // For each number 'num', increment the good pairs count by the number of times 'num' has already appeared
12 goodPairsCount += counts[num];
13
14 // Increment the count for the current number in 'counts' array
15 counts[num]++;
16 }
17
18 // Return the total count of good pairs
19 return goodPairsCount;
20 }
21};
22
1// This function calculates the number of good pairs in an array.
2// A good pair is defined as pairs (i, j) where nums[i] == nums[j] and i < j.
3function numIdenticalPairs(nums: number[]): number {
4 // Initialize an array with 101 elements all set to zero
5 // as the problem constraints suggest numbers between 1 and 100.
6 const count = new Array(101).fill(0);
7
8 // This will hold the total number of good pairs.
9 let totalPairs = 0;
10
11 // Iterate over each number in the input array.
12 for (const number of nums) {
13 // A good pair is found for each prior occurrence of the same number,
14 // so we increase the totalPairs by the count of the current number seen so far.
15 totalPairs += count[number];
16
17 // Increment the count for the current number for tracking future pairs.
18 count[number]++;
19 }
20
21 // Return the total number of good pairs found.
22 return totalPairs;
23}
24
Time and Space Complexity
Time Complexity
The time complexity of the given code is O(n)
, where n
is the length of the input list nums
. This is because the code iterates through each element of nums
exactly once, and operations within the loop (accessing and updating the Counter
dictionary) are O(1)
on average due to the hashing.
Space Complexity
The space complexity of the code is O(m)
, where m
is the number of unique elements in nums
. In the worst case, if all elements are unique, m
would equal n
. The Counter
object - a dictionary in Python - holds count data for each unique element. Therefore, the storage required grows with the number of unique elements.
Learn more about how to find time and space complexity quickly using problem constraints.
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!