[leetcode-264-Ugly Number II]
Write a program to find the n
-th ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5
. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12
is the sequence of the first 10
ugly numbers.
Note that 1
is typically treated as an ugly number, and n does not exceed 1690.
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
思路:
We have an array k of first n ugly number. We only know, at the beginning, the first one, which is 1. Then
k[1] = min( k[0]x2, k[0]x3, k[0]x5). The answer is k[0]x2. So we move 2's pointer to 1. Then we test:
k[2] = min( k[1]x2, k[0]x3, k[0]x5). And so on. Be careful about the cases such as 6, in which we need to forward both pointers of 2 and 3.
x here is multiplication.
Explanation:
The key is to realize each number can be and have to be generated by a former number multiplied by 2, 3 or 5
e.g.
1 2 3 4 5 6 8 9 10 12 15..
what is next?
it must be x * 2 or y * 3 or z * 5, where x, y, z is an existing number.
How do we determine x, y, z then?
apparently, you can just traverse the sequence generated by far from 1 ... 15, until you find such x, y, z that x * 2, y * 3, z * 5 is just bigger than 15. In this case x=8, y=6, z=4. Then you compare x * 2, y * 3, z * 5 so you know next number will be x * 2 = 8 * 2 = 16.
k, now you have 1,2,3,4,....,15, 16,
Then what is next?
You wanna do the same process again to find the new x, y, z, but you realize, wait, do I have to
traverse the sequence generated by far again?
NO! since you know last time, x=8, y=6, z=4 and x=8 was used to generate 16, so this time, you can immediately know the new_x = 9 (the next number after 8 is 9 in the generated sequence), y=6, z=4.
Then you need to compare new_x * 2, y * 3, z * 5. You know next number is 9 * 2 = 18;
And you also know, the next x will be 10 since new_x = 9 was used this time.
But what is next y? apparently, if y=6, 6*3 = 18, which is already generated in this round. So you also need to update next y from 6 to 8.
Based on the idea above, you can actually generated x,y,z from very beginning, and update x, y, z accordingly. It ends up with a O(n) solution.
int nthUglyNumber(int n) { vector<int>ret(n, 1); int index2 = 0, index3 = 0, index5 = 0; for (int i = 1; i < n; i++) { ret[i] = min(ret[index2]*2,min(ret[index3]*3,ret[index5]*5)); if (ret[i] == ret[index2] * 2)index2++; if (ret[i] == ret[index3] * 3)index3++; if (ret[i] == ret[index5] * 5)index5++; } return ret[n - 1]; }
参考:
https://discuss.leetcode.com/topic/24306/elegant-c-solution-o-n-space-time-with-detailed-explanation
https://discuss.leetcode.com/topic/21882/my-16ms-c-dp-solution-with-short-explanation