3483. Unique 3-Digit Even Numbers
Problem Description
You are given an array of digits (0-9) and need to find how many distinct three-digit even numbers can be formed using these digits.
The key constraints are:
- The number must be a valid three-digit number (100-999), so it cannot have leading zeros
- The number must be even (last digit must be 0, 2, 4, 6, or 8)
- Each digit from the array can be used at most once per number (if a digit appears multiple times in the array, each occurrence is treated as a separate digit that can be used)
- You need to count only distinct numbers (no duplicates)
For example, if you have digits [1, 2, 3, 4]
, you could form numbers like:
- 132 (even, ends with 2)
- 124 (even, ends with 4)
- 312 (even, ends with 2)
- And so on...
The solution uses three nested loops to enumerate all possible combinations:
- The outer loop picks the last digit (must be even for the number to be even)
- The middle loop picks the middle digit (cannot be the same position as the last digit)
- The inner loop picks the first digit (cannot be 0 for a valid three-digit number, and cannot use positions already taken)
The number is constructed as c * 100 + b * 10 + a
where c
is the hundreds digit, b
is the tens digit, and a
is the units digit. A set is used to automatically handle duplicates, ensuring only distinct numbers are counted.
Intuition
To form a valid three-digit even number, we need to think about the constraints for each position:
- Units place (rightmost digit): Must be even (0, 2, 4, 6, or 8) for the entire number to be even
- Hundreds place (leftmost digit): Cannot be 0 (otherwise it wouldn't be a three-digit number)
- Tens place (middle digit): Can be any digit from 0-9
Since we need to count distinct numbers and each digit position in the array can only be used once, we face a selection problem. We're essentially picking 3 positions from the array to form our number, with specific constraints on what values those positions can have.
The natural approach is to enumerate all valid combinations. We start by fixing the units digit (checking if it's even), then try all possible tens digits (avoiding the position we already used), and finally try all possible hundreds digits (avoiding both previously used positions and the value 0).
Why use a hash set? As we generate numbers, the same three-digit number might be formed from different combinations of array positions. For instance, if the array is [1, 2, 1, 2]
, we could form 212 using positions (0, 1, 3) or positions (2, 1, 3). The set automatically handles this deduplication for us.
The key insight is that we're not just looking at the values in the array, but at the positions of those values. This is why the solution uses indices i
, j
, k
to ensure we don't reuse the same position, even if the same digit value appears multiple times in the array.
Learn more about Recursion patterns.
Solution Approach
The solution uses a hash set with enumeration approach to find all distinct three-digit even numbers.
Data Structure Used:
- A hash set
s
to store distinct three-digit numbers and automatically handle duplicates
Algorithm Steps:
-
Initialize an empty set
s
to store distinct numbers. -
First loop - Fix the units digit: Iterate through each digit at position
i
in the array:- Check if the digit
a
is even using bitwise AND:if a & 1
(if the result is 1, the number is odd, so we skip it) - If odd, continue to the next iteration
- If even, this digit can be our units place
- Check if the digit
-
Second loop - Fix the tens digit: For each valid units digit, iterate through all positions
j
:- Skip if
i == j
(can't reuse the same position) - The digit
b
at positionj
becomes our tens digit
- Skip if
-
Third loop - Fix the hundreds digit: For each valid units and tens combination, iterate through all positions
k
:- Check two conditions:
c == 0
: Skip if the digit is 0 (no leading zeros allowed)k in (i, j)
: Skip if positionk
is already used
- If valid, the digit
c
at positionk
becomes our hundreds digit
- Check two conditions:
-
Form the number and add to set:
- Calculate the three-digit number as
c * 100 + b * 10 + a
- Add it to the set (duplicates are automatically ignored)
- Calculate the three-digit number as
-
Return the count: Return
len(s)
, which gives us the total number of distinct three-digit even numbers.
Time Complexity: O(nΒ³)
where n
is the length of the digits array, as we have three nested loops.
Space Complexity: O(m)
where m
is the number of distinct three-digit even numbers that can be formed (at most 450 for even numbers from 100-998).
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the array digits = [2, 1, 3, 0]
.
Step 1: Initialize empty set
s = {}
Step 2: Iterate through positions for units digit (must be even)
When i = 0 (digit = 2, even β):
- j = 1 (digit = 1):
- k = 2 (digit = 3): Form 312 β Add to set
- k = 3 (digit = 0): Skip (0 cannot be hundreds digit)
- j = 2 (digit = 3):
- k = 1 (digit = 1): Form 132 β Add to set
- k = 3 (digit = 0): Skip (0 cannot be hundreds digit)
- j = 3 (digit = 0):
- k = 1 (digit = 1): Form 102 β Add to set
- k = 2 (digit = 3): Form 302 β Add to set
When i = 1 (digit = 1, odd):
- Skip (not even)
When i = 2 (digit = 3, odd):
- Skip (not even)
When i = 3 (digit = 0, even β):
- j = 0 (digit = 2):
- k = 1 (digit = 1): Form 120 β Add to set
- k = 2 (digit = 3): Form 320 β Add to set
- j = 1 (digit = 1):
- k = 0 (digit = 2): Form 210 β Add to set
- k = 2 (digit = 3): Form 310 β Add to set
- j = 2 (digit = 3):
- k = 0 (digit = 2): Form 230 β Add to set
- k = 1 (digit = 1): Form 130 β Add to set
Step 3: Count distinct numbers
- Final set:
{312, 132, 102, 302, 120, 320, 210, 310, 230, 130}
- Return
len(s) = 10
The algorithm successfully finds all 10 distinct three-digit even numbers that can be formed from the digits [2, 1, 3, 0].
Solution Implementation
1class Solution:
2 def totalNumbers(self, digits: List[int]) -> int:
3 # Set to store unique 3-digit numbers
4 unique_numbers = set()
5
6 # Iterate through all digits to find candidates for ones place (must be even)
7 for ones_index, ones_digit in enumerate(digits):
8 # Skip if ones digit is odd (number must be even)
9 if ones_digit & 1:
10 continue
11
12 # Iterate through all digits for tens place
13 for tens_index, tens_digit in enumerate(digits):
14 # Skip if using the same digit position as ones place
15 if ones_index == tens_index:
16 continue
17
18 # Iterate through all digits for hundreds place
19 for hundreds_index, hundreds_digit in enumerate(digits):
20 # Skip if hundreds digit is 0 (not valid for 3-digit number)
21 # or if using same digit position as ones or tens place
22 if hundreds_digit == 0 or hundreds_index in (ones_index, tens_index):
23 continue
24
25 # Construct the 3-digit number and add to set
26 number = hundreds_digit * 100 + tens_digit * 10 + ones_digit
27 unique_numbers.add(number)
28
29 # Return count of unique 3-digit numbers
30 return len(unique_numbers)
31
1class Solution {
2 public int totalNumbers(int[] digits) {
3 // Use a HashSet to store unique 3-digit numbers
4 Set<Integer> uniqueNumbers = new HashSet<>();
5 int arrayLength = digits.length;
6
7 // Iterate through all possible positions for the ones digit (must be even)
8 for (int onesIndex = 0; onesIndex < arrayLength; ++onesIndex) {
9 // Skip if the digit at this position is odd
10 if (digits[onesIndex] % 2 == 1) {
11 continue;
12 }
13
14 // Iterate through all possible positions for the tens digit
15 for (int tensIndex = 0; tensIndex < arrayLength; ++tensIndex) {
16 // Skip if using the same index as the ones digit
17 if (onesIndex == tensIndex) {
18 continue;
19 }
20
21 // Iterate through all possible positions for the hundreds digit
22 for (int hundredsIndex = 0; hundredsIndex < arrayLength; ++hundredsIndex) {
23 // Skip if:
24 // 1. The digit is 0 (cannot be in hundreds place)
25 // 2. The index is already used for ones or tens digit
26 if (digits[hundredsIndex] == 0 ||
27 hundredsIndex == onesIndex ||
28 hundredsIndex == tensIndex) {
29 continue;
30 }
31
32 // Form the 3-digit number and add to set
33 int threeDigitNumber = digits[hundredsIndex] * 100 +
34 digits[tensIndex] * 10 +
35 digits[onesIndex];
36 uniqueNumbers.add(threeDigitNumber);
37 }
38 }
39 }
40
41 // Return the count of unique 3-digit numbers formed
42 return uniqueNumbers.size();
43 }
44}
45
1class Solution {
2public:
3 int totalNumbers(vector<int>& digits) {
4 // Use unordered_set to store unique 3-digit numbers
5 unordered_set<int> uniqueNumbers;
6 int n = digits.size();
7
8 // Iterate through all possible positions for the ones place (last digit)
9 for (int i = 0; i < n; ++i) {
10 // Skip if the digit at position i is odd (ones place must be even)
11 if (digits[i] % 2 == 1) {
12 continue;
13 }
14
15 // Iterate through all possible positions for the tens place (middle digit)
16 for (int j = 0; j < n; ++j) {
17 // Skip if trying to use the same digit position as ones place
18 if (i == j) {
19 continue;
20 }
21
22 // Iterate through all possible positions for the hundreds place (first digit)
23 for (int k = 0; k < n; ++k) {
24 // Skip if:
25 // 1. The digit is 0 (cannot be the first digit of a 3-digit number)
26 // 2. Position k is already used for ones or tens place
27 if (digits[k] == 0 || k == i || k == j) {
28 continue;
29 }
30
31 // Construct the 3-digit number and add to set
32 // hundreds place * 100 + tens place * 10 + ones place
33 int number = digits[k] * 100 + digits[j] * 10 + digits[i];
34 uniqueNumbers.insert(number);
35 }
36 }
37 }
38
39 // Return the count of unique valid 3-digit even numbers
40 return uniqueNumbers.size();
41 }
42};
43
1/**
2 * Counts unique three-digit numbers where:
3 * - The last digit (ones place) is even
4 * - The first digit (hundreds place) is non-zero
5 * - All three digits come from different positions in the input array
6 *
7 * @param digits - Array of single digits (0-9)
8 * @returns The count of unique valid three-digit numbers
9 */
10function totalNumbers(digits: number[]): number {
11 // Set to store unique three-digit numbers
12 const uniqueNumbers = new Set<number>();
13 const arrayLength = digits.length;
14
15 // Iterate through all positions for the ones place (last digit)
16 for (let onesIndex = 0; onesIndex < arrayLength; ++onesIndex) {
17 // Skip if the ones digit is odd (must be even)
18 if (digits[onesIndex] % 2 === 1) {
19 continue;
20 }
21
22 // Iterate through all positions for the tens place (middle digit)
23 for (let tensIndex = 0; tensIndex < arrayLength; ++tensIndex) {
24 // Skip if using the same position as ones digit
25 if (onesIndex === tensIndex) {
26 continue;
27 }
28
29 // Iterate through all positions for the hundreds place (first digit)
30 for (let hundredsIndex = 0; hundredsIndex < arrayLength; ++hundredsIndex) {
31 // Skip if:
32 // - The hundreds digit is 0 (invalid for first digit)
33 // - Using the same position as ones or tens digit
34 if (digits[hundredsIndex] === 0 ||
35 hundredsIndex === onesIndex ||
36 hundredsIndex === tensIndex) {
37 continue;
38 }
39
40 // Construct the three-digit number and add to set
41 const threeDigitNumber = digits[hundredsIndex] * 100 +
42 digits[tensIndex] * 10 +
43 digits[onesIndex];
44 uniqueNumbers.add(threeDigitNumber);
45 }
46 }
47 }
48
49 // Return the count of unique numbers
50 return uniqueNumbers.size;
51}
52
Time and Space Complexity
The time complexity is O(nΒ³)
, where n
is the length of the array digits
.
This is because the code contains three nested loops:
- The outer loop iterates through all digits (n iterations)
- The middle loop iterates through all digits (n iterations)
- The inner loop iterates through all digits (n iterations)
Even though there are early continue
statements that skip some iterations (when a
is odd, when i == j
, or when c == 0
or k
is in (i, j)
), these conditions only affect constant factors. In the worst case, we still need to examine all possible combinations of three positions from the array, resulting in O(nΒ³)
time complexity.
The space complexity is O(nΒ³)
, where n
is the length of the array digits
.
The set s
stores unique three-digit numbers formed by the digits. In the worst case, if all digits are distinct and meet the conditions (even last digit, non-zero first digit), we could potentially store up to O(nΒ³)
unique three-digit numbers in the set. Each unique combination of three positions with valid digits creates a distinct number that gets added to the set.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Confusing digit value with array index
A frequent mistake is treating the digit positions and digit values interchangeably. The code correctly uses indices to track which positions in the array have been used, but developers often mistakenly compare digit values instead of indices.
Incorrect approach:
# Wrong: comparing digit values instead of indices if hundreds_digit == ones_digit or hundreds_digit == tens_digit: continue
Correct approach:
# Right: comparing indices to ensure each position is used only once if hundreds_index in (ones_index, tens_index): continue
2. Incorrectly checking for even numbers
Some developers check evenness of the wrong digit or place the check in the wrong position.
Incorrect approach:
# Wrong: checking evenness after forming the number number = hundreds_digit * 100 + tens_digit * 10 + ones_digit if number % 2 == 0: unique_numbers.add(number)
Correct approach:
# Right: check ones digit for evenness before proceeding if ones_digit & 1: # Skip odd digits continue
3. Allowing duplicate digits when the array has duplicates
If the input array is [2, 2, 8, 8, 2]
, each occurrence is a separate digit that can be used. However, a common mistake is to deduplicate the input array first, which would incorrectly reduce the number of possible combinations.
Incorrect approach:
# Wrong: removing duplicates from input
digits = list(set(digits)) # This loses valid digit occurrences
Correct approach:
# Right: treat each array element as a separate digit
for ones_index, ones_digit in enumerate(digits):
# Each index represents a unique position, even if values are the same
4. Using the wrong data structure for storing results
Using a list instead of a set would require manual duplicate checking, making the code more complex and error-prone.
Incorrect approach:
# Wrong: using list and manual duplicate checking result = [] number = hundreds_digit * 100 + tens_digit * 10 + ones_digit if number not in result: # O(n) check each time result.append(number)
Correct approach:
# Right: using set for automatic duplicate handling
unique_numbers = set()
unique_numbers.add(number) # O(1) operation with automatic deduplication
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
Recommended Readings
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
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
Want a Structured Path to Master System Design Too? Donβt Miss This!