一:题目描述
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
二:解题思路
题目的关键就是对丑数定义的理解
所谓一个数m是另一个数n的因子,是指n能被m整除,n%m==0.
根据丑数的定义,丑数只能被2,3,5整除。
一个数能被2整除,我们就把他连续除以2
如果能被3整除,就连续除以3
如果能被5整除,就连续除以5
如果最后我们得到的是1,那么这个数就是丑数,否则不是
接下来,我们只需要按照顺序判断每一个正数是不是一个丑数就可以了
class Solution {
public:
bool IsUgly(int number){
while(number%2==0)
number/=2;
while(number%3==0)
number/=3;
while(number%5==0)
number/=5;
return (number==1)?true:false;
}
int GetUglyNumber_Solution(int index) {
if(index<=0)
return 0;
int number=1;
int numOfUgly=1;
while(numOfUgly<index){
number++;
if(IsUgly(number))
numOfUgly++;
}
return number;
}
};
在牛客网上会超时
前面的算法之所以效率低,很大程度上是对非丑数也进行了操作
接下来我们介绍一种只要计算丑数的方法
根据丑数的定义,丑数应该是另一个丑数乘以2,3,5的结果(1除外)
因此我们只需要创建一个数组,里面的数字是排序好的丑数,每一个丑数都是前面丑数乘以2,3,5得到
这个思路的关键是在于怎么确保数组中丑数都是排序好的?
假设数组中已经存放若干个排好序的丑数,并且把最大的丑数记为M。,接下来如何生成下一个丑数?
该丑数一定是前面的丑数乘以2,3,5的结果。所以
我们考虑把已有的每一个丑数乘以2,再乘以2后,会得到若干个小于等于M的结果,由于是按照顺序生成的,小于等于M的肯定已经在数组中,我们不再考虑;还有若干个大于M的结果,但是我们至于要知道第一个大于M的结果,记作M2.
同理我们可以得到M3,M5.
那么下一个丑数就是M2,M3,M5当中最小的结果。
前面的分析,把已有的每一个丑数乘以2,3,5,是没有必要的。
因为已有的丑数是按照顺序存放在数组中的,对乘以2而言,肯定存在某个丑数T2,排在它前面的每一个丑数乘以2 的结果都会小于等于当前最大的丑数,所以我们只需要记录这个丑数的位置,同时每次生成新的丑数的时候,去更新T2。
同理更新T3,T5
三:代码实现
class Solution {
public:
int Min(int number1,int number2,int number3){
int min=(number1<number2)?number1:number2;
min=(min<number3)?min:number3;
return min;
}
int GetUglyNumber_Solution(int index) {
if(index<=0)
return 0;
int *pUglyNumber= new int[index];
pUglyNumber[0]=1;//第一个丑数是1
int nextUglyNumber=1;//代表下一个丑数在数组中的下标
//创建乘子2,3,5三个指针,初始化均指向丑数数组头,用来保存T2,T3,T5
int *pMultiply2=pUglyNumber;
int *pMultiply3=pUglyNumber;
int *pMultiply5=pUglyNumber;
while(nextUglyNumber<index){
int min=Min(*pMultiply2*2,*pMultiply3*3,*pMultiply5*5);
pUglyNumber[nextUglyNumber]=min;
//更新T2,T3,T5
while(*pMultiply2*2<=min)
pMultiply2++;
while(*pMultiply3*3<=min)
pMultiply3++;
while(*pMultiply5*5<=min)
pMultiply5++;
nextUglyNumber++;
}
int ugly=pUglyNumber[index-1];
delete[] pUglyNumber;
return ugly;
}
};