We need to find the nth number in a sequence where every number has only 2, 3, and 5 as prime factors. The sequence starts with 1 (by convention, 1 is ugly since it has no prime factors at all), then 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, and so on.
Every ugly number except 1 is generated by multiplying a smaller ugly number by 2, 3, or 5. For example, 4 = 2 2, 6 = 2 3, 8 = 2 4, and 9 = 3 3. The work is generating these numbers in sorted order without gaps or duplicates, which means we need a way to always pick the next smallest ugly number.
1 <= n <= 1690 → The upper bound on n is small, so the time complexity is not the constraint here. The harder part is generating the sequence in order without producing the same number twice.5 * 2,123,366,400, which overflows 32 bits. That approach uses 64-bit integers for safety. The three-pointer approach never produces a value past the answer, so 32-bit integers are sufficient there.Instead of testing every integer for ugliness, we can generate ugly numbers directly. Start with 1, the first ugly number. For each ugly number we extract, its multiples by 2, 3, and 5 are also ugly, so we push those three products into a min-heap. The heap always returns the smallest candidate next, which gives us the sequence in sorted order.
Duplicates are the complication. The number 6 can be produced as 2 3 and as 3 2, so the same value reaches the heap from two directions. A set of seen values prevents counting it twice.
The heap approach carries two costs: the log-factor overhead of the heap and a hash set for deduplication. The next approach removes both by tracking three pointers into the ugly number sequence, so each step knows exactly which ugly number to multiply by each factor.
Every ugly number is one of: 2 (some earlier ugly number), 3 (some earlier ugly number), or 5 * (some earlier ugly number). So we maintain a sorted array of ugly numbers and three pointers: i2, i3, and i5. Each pointer tracks which ugly number in our array should next be multiplied by its respective factor.
At each step, we compute three candidates: ugly[i2] * 2, ugly[i3] * 3, and ugly[i5] * 5. The next ugly number is the minimum of these three. Then we advance whichever pointer or pointers produced that minimum. When two candidates tie (for example 2 3 = 6 and 3 2 = 6), we advance both pointers, which skips the duplicate without a set.
The pointer i2 maintains the invariant that ugly[0] through ugly[i2-1] have already been multiplied by 2 and placed into the sequence, so ugly[i2] * 2 is the smallest multiple of 2 not yet used. The same holds for i3 and i5. The minimum of the three candidates is therefore the smallest ugly number not yet in the array, so the array stays sorted and skips nothing. Advancing every pointer that matches the minimum (separate if statements, not else if) consumes a duplicate from all of its sources at once, so a value like 6 is written only one time.
ugly of size n. Set ugly[0] = 1.i2 = 0, i3 = 0, i5 = 0.next2 = ugly[i2] * 2, next3 = ugly[i3] * 3, next5 = ugly[i5] * 5.ugly[i] = min(next2, next3, next5).ugly[i] == next2, increment i2.ugly[i] == next3, increment i3.ugly[i] == next5, increment i5.ugly[n - 1].