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.
Hint:
- The naive approach is to call
isUgly
for every number until you reach the nth one. Most numbers are not ugly. Try to focus your effort on generating only the ugly ones. - An ugly number must be multiplied by either 2, 3, or 5 from a smaller ugly number.
- The key is how to maintain the order of the ugly numbers. Try a similar approach of merging from three sorted lists: L1, L2, and L3.
- Assume you have Uk, the kth ugly number. Then Uk+1 must be Min(L1 * 2, L2 * 3, L3 * 5).
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
Subscribe to see which companies asked this question
解法1:从2到n循环,对每个数进行isUgly判断,会超时Time Limit Exceeded
class Solution { public: int nthUglyNumber(int n) { int num = 2; while(n > 1) { if(isUgly(num)) --n; ++num; } return --num; } private: bool isUgly(int num) { while(num > 0 && num % 2 == 0) num /= 2; while(num > 0 && num % 3 == 0) num /= 3; while(num > 0 && num % 5 == 0) num /= 5; return num == 1; } };
解法2:根据提供的Hint,我们知道丑陋数序列可以拆分为下面3个子列表:
(2) 1×2, 2×2, 3×2, 4×2, 5×2, …
(3) 1×3, 2×3, 3×3, 4×3, 5×3, …
(5) 1×5, 2×5, 3×5, 4×5, 5×5, …
一个新的丑数可以看做是一个旧的丑数乘以2,3,5得到的(第一个丑数是1,第二个为2,则第二个可以看做第一个丑数*2得到了第二个丑数(1*2、1*3和1*5中的最小者)),本题解题难点在于维护丑数数列为从小到大排列的顺序。
根据上述思路可以做如下实现:
- 维护三个列表l2,l3,l5,分别是当前处理的丑数num的2,3,5倍,因此三个列表组内均是遵从从小到大排列的规则;
- 每次获得三个列表的第一个结点(最小的结点)最为当前丑数num,根据当前丑数值想三个列表末端插入新的较大的丑数值,等待后续处理;
- 若列表的第一个结点与当前丑数num相等,则删除列表第一个结点(表示该丑数已经被计数);
- 题目的初始条件为num = 1, count = 1;
class Solution { public: int nthUglyNumber(int n) { int count = 1; int num = 1; list<int> l2; list<int> l3; list<int> l5; while(count != n) { l2.push_back(2*num); l3.push_back(3*num); l5.push_back(5*num); int l2min = l2.front(); int l3min = l3.front(); int l5min = l5.front(); int minNum = min(l2min, min(l3min, l5min)); if(l2min == minNum) l2.pop_front(); if(l3min == minNum) l3.pop_front(); if(l5min == minNum) l5.pop_front(); num = minNum; ++count; } return num; } };
改进:对于乘以2而言,肯定存在某一个丑数t2,使得排在它之前的每一个丑数乘以2得到的结果都会小于已有最大丑数,在它之后的每个丑数乘以2得到的结果都会大于当前最大丑数。我们只需要记下这个丑数的位置,同时每次生成新的丑数时去更新这个t2。对于3和5,同样存在t3和t5。
class Solution { public: int nthUglyNumber(int n) { vector<int> uglyNumSeq(1, 1); int i2 = 0, i3 = 0, i5 = 0; while (uglyNumSeq.size() < n) { int n2 = uglyNumSeq[i2] * 2; int n3 = uglyNumSeq[i3] * 3; int n5 = uglyNumSeq[i5] * 5; int next = min(n2, min(n3, n5)); if (next == n2) ++i2; if (next == n3) ++i3; //注意不用else if,因为不同序列可能有相同值 if (next == n5) ++i5; uglyNumSeq.push_back(next); } return uglyNumSeq.back(); } };