1394. Find Lucky Integer in an Array
Problem Description
You are given an array of integers called arr
. A lucky integer is defined as an integer whose value equals the number of times it appears in the array.
For example, if the number 3 appears exactly 3 times in the array, then 3 is a lucky integer. Similarly, if the number 7 appears exactly 7 times, then 7 is also a lucky integer.
Your task is to find and return the largest lucky integer in the array. If no lucky integers exist in the array, return -1
.
Let's break down what this means:
- First, count how many times each number appears in the array
- Check if any number's value matches its frequency (count)
- Among all numbers that satisfy this condition, return the largest one
- If no number satisfies this condition, return
-1
For instance:
- If
arr = [2, 2, 3, 3, 3]
, then 2 appears 2 times and 3 appears 3 times. Both are lucky integers, but we return 3 since it's the larger one. - If
arr = [1, 2, 2, 3, 3, 3]
, then 1 appears 1 time, 2 appears 2 times, and 3 appears 3 times. All three are lucky integers, and we return 3 as it's the largest. - If
arr = [2, 2, 2, 3, 3]
, then 2 appears 3 times and 3 appears 2 times. Neither number's value matches its frequency, so we return-1
.
Intuition
The key insight is that we need to know the frequency of each number in the array to determine if it's a lucky integer. This immediately suggests using a frequency counting approach.
Think about what we need to check: for each unique number in the array, does its value equal its count? This is a straightforward comparison once we have the frequency information.
The natural approach is to:
- Count how often each number appears - this screams for a hash table or counter structure
- Check each number to see if
number == frequency[number]
- Keep track of the maximum lucky integer we find
Why use a Counter or hash table? Because we need to efficiently look up how many times each number appears. A Counter
in Python gives us exactly this - it creates a dictionary where keys are the numbers and values are their frequencies.
Once we have the frequency map, we can iterate through it and check the condition x == count[x]
for each number x
. Since we want the largest lucky integer, we need to find the maximum among all numbers that satisfy this condition.
The solution elegantly uses a generator expression (x for x, v in cnt.items() if x == v)
to filter only the lucky integers, then applies max()
with a default=-1
parameter to handle the case when no lucky integers exist. This is concise and handles both the filtering and finding the maximum in a single expression.
Solution Approach
The solution uses a counting approach with a hash table to efficiently find the largest lucky integer.
Step 1: Count the frequencies
We use Python's Counter
from the collections module to count the occurrences of each number in the array:
cnt = Counter(arr)
This creates a dictionary-like object where each key is a unique number from arr
and its corresponding value is the frequency of that number.
For example, if arr = [2, 2, 3, 3, 3, 4]
, then cnt
would be {2: 2, 3: 3, 4: 1}
.
Step 2: Find lucky integers and get the maximum
The solution then uses a generator expression combined with the max()
function:
return max((x for x, v in cnt.items() if x == v), default=-1)
Let's break this down:
cnt.items()
returns pairs of(number, frequency)
if x == v
filters only those pairs where the number equals its frequency (i.e., lucky integers)(x for x, v in cnt.items() if x == v)
generates all lucky integersmax()
finds the largest among these lucky integersdefault=-1
ensures that if no lucky integers exist (empty generator), the function returns-1
Time and Space Complexity:
- Time Complexity:
O(n)
wheren
is the length of the array. We iterate through the array once to count frequencies, and then iterate through the unique numbers (at mostn
) to find lucky integers. - Space Complexity:
O(k)
wherek
is the number of unique elements in the array. In the worst case where all elements are unique, this would beO(n)
.
The beauty of this solution lies in its simplicity - it directly translates the problem requirements into code using appropriate data structures and Python's built-in functions.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with arr = [1, 2, 2, 3, 3, 3, 4, 4, 4]
.
Step 1: Count frequencies using Counter
When we create cnt = Counter(arr)
, we get:
cnt = {1: 1, 2: 2, 3: 3, 4: 3}
This tells us:
- 1 appears 1 time
- 2 appears 2 times
- 3 appears 3 times
- 4 appears 3 times
Step 2: Find lucky integers
Now we check each number to see if its value equals its frequency:
- Number 1: value is 1, frequency is 1 → 1 == 1 ✓ (lucky!)
- Number 2: value is 2, frequency is 2 → 2 == 2 ✓ (lucky!)
- Number 3: value is 3, frequency is 3 → 3 == 3 ✓ (lucky!)
- Number 4: value is 4, frequency is 3 → 4 ≠ 3 ✗ (not lucky)
The generator expression (x for x, v in cnt.items() if x == v)
produces: 1, 2, 3
Step 3: Find the maximum
Among the lucky integers [1, 2, 3], the maximum is 3.
Therefore, the function returns 3.
If instead we had arr = [5, 5, 5, 5, 5, 7, 7]
where 5 appears 5 times (lucky) and 7 appears 2 times (not lucky since 7 ≠ 2), we would return 5.
If no lucky integers existed, like in arr = [2, 2, 2, 3, 3]
where 2 appears 3 times and 3 appears 2 times, the generator would be empty and max()
would return the default value of -1.
Solution Implementation
1class Solution:
2 def findLucky(self, arr: List[int]) -> int:
3 # Count the frequency of each number in the array
4 frequency_map = Counter(arr)
5
6 # Find all lucky numbers (numbers where value equals its frequency)
7 # and return the maximum, or -1 if no lucky number exists
8 return max((num for num, freq in frequency_map.items() if num == freq), default=-1)
9
1class Solution {
2 /**
3 * Finds the largest lucky integer in the array.
4 * A lucky integer is an integer that has a frequency in the array equal to its value.
5 *
6 * @param arr the input array of integers
7 * @return the largest lucky integer, or -1 if no lucky integer exists
8 */
9 public int findLucky(int[] arr) {
10 // Create frequency array to count occurrences of each number
11 // Size 501 to handle numbers from 1 to 500 (as per problem constraints)
12 int[] frequencyCount = new int[501];
13
14 // Count the frequency of each number in the input array
15 for (int number : arr) {
16 frequencyCount[number]++;
17 }
18
19 // Iterate from largest possible value to smallest to find the largest lucky integer
20 for (int i = frequencyCount.length - 1; i > 0; i--) {
21 // Check if current number's value equals its frequency (lucky integer condition)
22 if (i == frequencyCount[i]) {
23 return i;
24 }
25 }
26
27 // No lucky integer found
28 return -1;
29 }
30}
31
1class Solution {
2public:
3 int findLucky(vector<int>& arr) {
4 // Initialize frequency array to count occurrences of each number
5 // Size 501 to handle numbers from 1 to 500 (index 0 unused)
6 int frequency[501] = {0};
7
8 // Count the frequency of each number in the array
9 for (int num : arr) {
10 ++frequency[num];
11 }
12
13 // Iterate from largest to smallest to find the maximum lucky integer
14 // A lucky integer is one where the value equals its frequency
15 for (int i = 500; i > 0; --i) {
16 if (i == frequency[i]) {
17 return i; // Found the largest lucky integer
18 }
19 }
20
21 // No lucky integer found
22 return -1;
23 }
24};
25
1/**
2 * Finds the largest lucky integer in the array.
3 * A lucky integer is an integer that has a frequency in the array equal to its value.
4 * @param arr - The input array of positive integers
5 * @returns The largest lucky integer, or -1 if no lucky integer exists
6 */
7function findLucky(arr: number[]): number {
8 // Create a frequency counter array with size 501 (covers range 1-500)
9 // Index represents the number, value represents its frequency
10 const frequencyCounter: number[] = Array(501).fill(0);
11
12 // Count the frequency of each number in the input array
13 for (const num of arr) {
14 ++frequencyCounter[num];
15 }
16
17 // Iterate from the largest possible value to find the largest lucky integer
18 // Start from length - 1 and go down to 1 (skip 0 as it's not in the valid range)
19 for (let value = frequencyCounter.length - 1; value > 0; --value) {
20 // Check if the current value equals its frequency (lucky integer condition)
21 if (value === frequencyCounter[value]) {
22 return value;
23 }
24 }
25
26 // No lucky integer found
27 return -1;
28}
29
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array arr
. This is because:
- Creating the
Counter
object requires iterating through alln
elements of the array once, which takesO(n)
time - The generator expression
(x for x, v in cnt.items() if x == v)
iterates through the counter items, which in the worst case containsn
unique elements, takingO(n)
time - The
max()
function with the generator consumes the generator inO(n)
time in the worst case - Overall, these operations are sequential, resulting in
O(n) + O(n) = O(n)
time complexity
The space complexity is O(n)
, where n
is the length of the array arr
. This is because:
- The
Counter
object stores frequency counts for each unique element in the array, which in the worst case (all elements are unique) requiresO(n)
space - The generator expression itself uses
O(1)
space as it yields values one at a time - Therefore, the dominant space usage is from the
Counter
, giving usO(n)
space complexity
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Forgetting to handle the empty result case
The Problem:
A common mistake is to use max()
without the default
parameter when finding the maximum lucky integer:
# Incorrect - will raise ValueError if no lucky integers exist
return max(x for x, v in frequency_map.items() if x == v)
This code will raise a ValueError: max() arg is an empty sequence
when there are no lucky integers in the array.
The Solution:
Always use the default
parameter with max()
when the sequence might be empty:
# Correct - returns -1 when no lucky integers exist
return max((x for x, v in frequency_map.items() if x == v), default=-1)
Pitfall 2: Confusing value constraints with frequency constraints
The Problem: Some might mistakenly filter based on whether the frequency is valid rather than checking if the value equals the frequency:
# Incorrect - checking if frequency is positive integer
return max((x for x, v in frequency_map.items() if v > 0 and v <= len(arr)), default=-1)
The Solution: Remember that a lucky integer is defined as a number whose VALUE equals its FREQUENCY:
# Correct - checking if value equals frequency
return max((x for x, v in frequency_map.items() if x == v), default=-1)
Pitfall 3: Not considering negative numbers or zero
The Problem: The problem states we're looking for integers where the value equals the frequency. Since frequencies are always positive (a number appears at least once if it's in the array), negative numbers can never be lucky integers. However, some might try to add unnecessary checks:
# Unnecessary complexity
return max((x for x, v in frequency_map.items() if x == v and x > 0), default=-1)
While this isn't wrong, the condition x == v
already ensures x > 0
since frequencies are positive.
The Solution:
The simple condition x == v
is sufficient:
# Clean and sufficient
return max((x for x, v in frequency_map.items() if x == v), default=-1)
Note: Zero can never be a lucky integer since it would need to appear 0 times, which means it wouldn't be in the array at all.
What's the relationship between a tree and a graph?
Recommended Readings
Coding Interview 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
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 assets algo monster recursion jpg You first call Ben and ask
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!