Imagine you want to generate a sequence of numbers whose only prime factors are 2, 3, or 5, like a special recipe that only allows these ingredients. Finding the nth such number efficiently is the challenge.
An ugly number is a positive integer whose prime factors only include 2, 3, and 5. Given an integer n, return the nth ugly number.
Input: Integer n
Output: Integer representing the nth ugly number
1 ≤ n ≤ 10^5The answer is guaranteed to fit in a 32-bit signed integer
Edge cases: n = 1 → output 1 (smallest ugly number)n = 2 → output 2 (first multiple of 2)n = 15 → output 24 (checks correctness beyond first 10)
def nthUglyNumber(n: int) -> int:
# Write your solution here
pass
class Solution {
public int nthUglyNumber(int n) {
// Write your solution here
return 0;
}
}
#include <vector>
using namespace std;
int nthUglyNumber(int n) {
// Write your solution here
return 0;
}
function nthUglyNumber(n) {
// Write your solution here
}
Coming soon
0/9
Common Bugs to Avoid
Wrong: Incorrect smaller number (e.g., 10 instead of 12 for n=10)Not incrementing all pointers that produce the current ugly number, causing duplicates to be missed.✅ Increment every pointer i where ugly[i]*factor == current ugly number, not just one pointer.
Wrong: Wrong output for n=1 (e.g., 0 or error)Missing base case handling for n=1, returning uninitialized or default value.✅ Add explicit check: if n == 1, return 1 immediately.
Wrong: Timeout or no output for large n (e.g., n=100000)Using brute force factorization or inefficient approach with O(n log n) or worse complexity.✅ Implement O(n) three-pointer merge dynamic programming approach.
Wrong: Off-by-one errors causing wrong nth ugly numberIncorrect loop bounds or indexing in the generation loop.✅ Ensure loop runs n-1 times after initializing first ugly number, and array indices are correct.
Wrong: Confusing 0/1 knapsack style usage, missing repeated multiplesRestricting usage of multiples to once instead of unlimited times.✅ Allow pointers to move forward and reuse multiples unlimited times in the sequence generation.
✓
Test Cases
Focus on handling the smallest and largest inputs correctly.
Check your pointer updates and duplicate handling carefully.
Optimize your solution to run in linear time for large inputs.
t1_01basic
Input{"n":10}
Expected12
⏱ Performance - must finish in 2000ms
The first 10 ugly numbers are 1, 2, 3, 4, 5, 6, 8, 9, 10, 12. The 10th is 12.
💡 Think about generating ugly numbers by merging sequences multiplied by 2, 3, and 5.
💡 Use three pointers to track the next multiples of 2, 3, and 5 respectively.
💡 Maintain an array of ugly numbers and update pointers when their multiple matches the new ugly number.
Why it failed: Incorrect output means the merging logic is flawed; ensure you update pointers only when their multiple equals the current ugly number.
✓ Correctly generates the first 10 ugly numbers using the three-pointer merge approach.
t1_02basic
Input{"n":15}
Expected24
⏱ Performance - must finish in 2000ms
The 15th ugly number is 24, continuing the sequence beyond the first 10.
💡 Extend the three-pointer merge logic to larger n values.
💡 Check that you handle duplicates correctly when multiple pointers produce the same candidate.
💡 Increment all pointers whose multiples equal the current ugly number to avoid duplicates.
Why it failed: Output less than expected indicates missing duplicates; fix by incrementing all pointers that match the current ugly number.
✓ Correctly handles duplicates and produces the 15th ugly number.
t2_01edge
Input{"n":1}
Expected1
⏱ Performance - must finish in 2000ms
The smallest ugly number is 1, which is the 1st ugly number.
💡 Check the base case where n=1 returns 1 immediately.
💡 Ensure your code does not attempt to generate further numbers when n=1.
💡 Return 1 directly or initialize your array with 1 as the first ugly number.
Why it failed: Failing this test means base case handling is missing; add a condition to return 1 when n=1.
✓ Properly handles the smallest input returning 1.
t2_02edge
Input{"n":2}
Expected2
⏱ Performance - must finish in 2000ms
The 2nd ugly number is 2, the first multiple of 2.
💡 Verify that the first multiple of 2 is correctly identified after 1.
💡 Check that your pointers start correctly and update after the first ugly number.
💡 Ensure the next ugly number after 1 is min(2,3,5) = 2.
Why it failed: Incorrect output means pointer initialization or update is wrong; fix by initializing pointers at 0 and updating after adding 1.
✓ Correctly identifies the 2nd ugly number as 2.
t2_03edge
Input{"n":100000}
Expected"290142196707511001929482240000000000000"
⏱ Performance - must finish in 2000ms
The 100000th ugly number fits in 32-bit integer and tests efficiency and overflow handling.
💡 Use an O(n) approach with three pointers to handle large n efficiently.
💡 Avoid brute force factorization which is too slow for large n.
💡 Use 64-bit integer type internally if needed to avoid overflow during multiplication.
Why it failed: Failing this test indicates inefficiency or overflow; switch to three-pointer DP approach and use appropriate integer types.
✓ Efficiently computes large nth ugly number without overflow.
t3_01corner
Input{"n":7}
Expected8
⏱ Performance - must finish in 2000ms
The 7th ugly number is 8; tests handling of duplicates and pointer increments.
💡 Check if your code handles duplicates when multiple pointers generate the same number.
💡 Verify that all pointers that produce the current ugly number are incremented.
💡 For example, 6 can be generated by 2*3 and 3*2; both pointers must move forward.
Why it failed: Failing this test means duplicates are not handled; fix by incrementing all pointers whose multiples equal the current ugly number.
✓ Correctly handles duplicates and pointer increments.
t3_02corner
Input{"n":11}
Expected15
⏱ Performance - must finish in 2000ms
The 11th ugly number is 15; tests confusion between 0/1 knapsack style and unbounded merges.
💡 Remember this is a sequence generation, not a 0/1 knapsack problem.
💡 You can reuse multiples of 2, 3, and 5 unlimited times.
💡 Ensure pointers move forward correctly without skipping valid multiples.
Why it failed: Failing this test indicates confusion with 0/1 constraints; fix by allowing repeated use of multiples and proper pointer updates.
✓ Correctly generates sequence with unlimited multiples.
t3_03corner
Input{"n":9}
Expected10
⏱ Performance - must finish in 2000ms
The 9th ugly number is 10; tests off-by-one errors in indexing or pointer updates.
💡 Check your indexing carefully to avoid off-by-one errors.
💡 Verify that the array of ugly numbers is correctly sized and accessed.
💡 Ensure the loop runs exactly n-1 times after initializing the first ugly number.
Why it failed: Failing this test means off-by-one error; fix loop bounds and indexing to generate exactly n ugly numbers.
✓ Correctly handles indexing and loop bounds.
t4_01performance
Input{"n":100000}
Expectednull
⏱ Performance - must finish in 2000ms
Performance test with n=100000; O(n) three-pointer merge approach must complete within 2 seconds.
💡 Avoid brute force factorization; use dynamic programming with three pointers.
💡 Pre-allocate arrays and avoid unnecessary computations.
💡 Use efficient data types and avoid overhead in loops.
Why it failed: TLE indicates brute force or inefficient approach; fix by implementing O(n) three-pointer merge algorithm.
✓ Efficient O(n) solution confirmed by passing performance test.
Practice
(1/5)
1. You are given two sorted arrays and need to find the k pairs with the smallest sums formed by taking one element from each array. Which approach guarantees an optimal solution with efficient time complexity?
easy
A. Use dynamic programming to build a table of sums and select the smallest pairs from it.
B. Use a brute force approach by enumerating all pairs, then sorting them to find the smallest sums.
C. Use a min-heap to lazily generate pairs starting from the smallest possible sums, expanding only as needed.
D. Use a greedy approach by always pairing the smallest elements from each array without backtracking.
Solution
Step 1: Understand problem constraints
The problem requires finding the k smallest pairs by sum from two sorted arrays efficiently.
Step 2: Evaluate approaches
Brute force enumerates all pairs, which is inefficient. DP is not suitable since pairs are not independent subproblems. Greedy fails because pairing smallest elements once misses other smaller sums. The min-heap approach lazily expands pairs starting from the smallest sums, ensuring optimality and efficiency.
Final Answer:
Option C -> Option C
Quick Check:
Min-heap approach matches the known optimal solution [OK]
Hint: Min-heap lazily expands pairs for optimal efficiency [OK]
Common Mistakes:
Assuming greedy pairing always works
Thinking DP applies directly
Believing brute force is efficient enough
2. Given the following code snippet, what is the final output when called with nums1 = [1,7,11], nums2 = [2,4,6], and k = 3?
def countPairs(nums1, nums2, mid):
count = 0
j = len(nums2) - 1
for i in range(len(nums1)):
while j >= 0 and nums1[i] + nums2[j] > mid:
j -= 1
count += (j + 1)
return count
def kthSmallestSum(nums1, nums2, k):
low = nums1[0] + nums2[0]
high = nums1[-1] + nums2[-1]
while low < high:
mid = (low + high) // 2
if countPairs(nums1, nums2, mid) < k:
low = mid + 1
else:
high = mid
return low
print(kthSmallestSum(nums1, nums2, k))
For mid=7, countPairs returns 3 pairs ≤7, which matches k=3, so high=7.
Final Answer:
Option B -> Option B
Quick Check:
Output matches expected k-th smallest sum [OK]
Hint: Binary search narrows sum range to find k-th smallest sum [OK]
Common Mistakes:
Off-by-one in binary search bounds
Miscounting pairs in countPairs
3. Consider the following Python code implementing the optimal job assignment to K machines. Given K = 2 and jobs = [[0,5],[1,2],[5,3]], what is the final returned list indicating the machine assigned to each job (in original job order)?
easy
A. [0, 1, 0]
B. [0, 0, 1]
C. [1, 0, 1]
D. [1, 1, 0]
Solution
Step 1: Sort jobs by start time
Jobs sorted by start time: (0,[0,5]), (1,[1,2]), (2,[5,3]) with indices 0,1,2 respectively.
Step 2: Simulate assignment
Initially, available machines = [0,1].
- Job 0 starts at 0: assign machine 0, finish at 5.
- Job 1 starts at 1: machine 1 is free, assign machine 1, finish at 3.
- Job 2 starts at 5: running machines finish times are 3 and 5; both ≤ 5, so both machines become available. Assign machine 0 (smallest index).
Result array indexed by original job indices: [0,1,0].
Final Answer:
Option A -> Option A
Quick Check:
Trace matches output [0,1,0] [OK]
Hint: Sort jobs by start time and track machine availability with heaps [OK]
Common Mistakes:
Assigning machine 1 to job 1 instead of machine 0
Not freeing machines before assigning next job
Mixing up job indices after sorting
4. Consider the following Python code that merges k sorted arrays using divide and conquer. Given the input arrays [[1,4,5],[1,3,4],[2,6]], what is the value of merged_arrays after the first iteration of the while loop inside merge_k_sorted_arrays?
easy
A. [[1,1,3,4,4,5,2,6]]
B. [[1,1,3,4,4,5],[2,6]]
C. [[1,3,4,5],[1,2,4,6]]
D. [[1,1,3,4,4,5],[2,6]]
Solution
Step 1: Trace merging pairs in first iteration
Arrays are [[1,4,5],[1,3,4],[2,6]]. Merge first two: merge_two_arrays([1,4,5],[1,3,4]) -> [1,1,3,4,4,5]. The last array [2,6] remains as is because no pair.
Step 2: Form merged_arrays after first iteration
merged_arrays = [[1,1,3,4,4,5],[2,6]]
Final Answer:
Option D -> Option D
Quick Check:
First pair merged correctly, last array unchanged -> matches [[1,1,3,4,4,5],[2,6]] [OK]
Hint: Merge pairs left to right, last array unchanged if odd count [OK]
Common Mistakes:
Merging last array incorrectly
Off-by-one in loop
Appending arrays instead of merging
5. What is the time complexity of the divide and conquer approach to merge k sorted arrays with a total of N elements?
medium
A. O(N log N) because each merge sorts the combined arrays
B. O(N log k) because merging pairs reduces arrays by half each iteration
C. O(k N) because each of the k arrays is merged sequentially
D. O(N k) because each element is compared with k arrays
Solution
Step 1: Identify number of merge rounds
Divide and conquer merges pairs of arrays, halving the number of arrays each round, so log k rounds.
Step 2: Calculate work per round
Each round merges all N elements once, so O(N) per round.
Final Answer:
Option B -> Option B
Quick Check:
O(N) work per round x log k rounds -> O(N log k) [OK]
Hint: Divide and conquer halves arrays each round -> log k rounds [OK]
Common Mistakes:
Confusing total elements N with number of arrays k
Assuming sorting entire array each time
Multiplying N by k incorrectly
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.