Facebook Pixel

1394. Find Lucky Integer in an Array

EasyArrayHash TableCounting
Leetcode Link

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.
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Count how often each number appears - this screams for a hash table or counter structure
  2. Check each number to see if number == frequency[number]
  3. 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 integers
  • max() finds the largest among these lucky integers
  • default=-1 ensures that if no lucky integers exist (empty generator), the function returns -1

Time and Space Complexity:

  • Time Complexity: O(n) where n is the length of the array. We iterate through the array once to count frequencies, and then iterate through the unique numbers (at most n) to find lucky integers.
  • Space Complexity: O(k) where k is the number of unique elements in the array. In the worst case where all elements are unique, this would be O(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 Evaluator

Example 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 all n elements of the array once, which takes O(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 contains n unique elements, taking O(n) time
  • The max() function with the generator consumes the generator in O(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) requires O(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 us O(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.

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

What's the relationship between a tree and a graph?


Recommended Readings

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

Load More