题目描述
- 题目:我们把只包含因子 2、3 和 5 的数称为丑数(Ugly Number)。求按从小到大的顺序的第 1500 个丑数。例如 6、8 都是丑数,但是 14 不是,因为它包含因子 7。习惯上我们把 1 当做第一个丑数
解题思路
- 根据丑数定义我们可以知道:丑数可以被 2、3、5整除,即如果一个数可以被 2 整除,那我们对一个数依次循环做 mod(2)运算,若得到最终结果为 1 ,则说明这个数是丑数,同理可被 3、5 整除,我们也是对其循环做 mod(3)运算,mod(5)运算,根据最终结果是否为 1 来判断它是不是丑数
- 若我们已经可以判断一个数是否是丑数,则只需要从 1 开始依次对每个数进行判断,直到找到第 1500 个丑数为止,此时这个丑数一定是按从小到大顺序的第1500个丑数
- 这个思路非常直观,也非常简单,代码也很简洁,但是很显然这是一个效率比较低的算法,我们对每个数都要做除运算和模运算,即使这个数不是丑数。这个代码对于小一点的数效率无多大的影响,但是若这个数特别大呢,如果是 10000,100000,或者是更大,显然这个算法就不能满足效率的要求了
- 所以我们的想法是不要再非丑数的数上花时间。根据丑数的定义,我们可以想到的是:一个丑数可以由另一个丑数乘以 2、3、5 获得。因此我们可以创建一个数组,里面的数是排好序的丑数。但是我们如何我们获取到的丑数是有序的呢 ?
- 假设数组中已经有若干个排好序的丑数,并且最大的那个记为 M ,下一个丑数肯定是前面已经生成的丑数乘以 2、3、5得到的,这样我们首先考虑给前面每个数乘以 2,若小于 M,则这个丑数肯定已经存在于数组中了,大于 M 的第一个数我们将其记为 M2,同时类似得到 M3,M5,这样 M2,M3,M5中最小的一个即是下一个丑数
- 事实上我们不需要给已经排好序的所有丑数乘以 2,因为在乘以 2 的过程中会有一个丑数T2,在它之前的每一个丑数乘以 2 都小于 M,我们只需要更新这个 T2 即可,同样的也有 T3,T5.
- 显然,这个算法虽然有空间的耗费,但是时间效率却有所提高,是一种以空间换时间的算法
- 思路理清楚后,我们就可以分别来实现这两个算法了
代码实现
bool IsUglyNumber(int number)
{
while(number % 2 == 0)
num /= 2;
while(number % 3 == 0)
num /= 3;
while(number % 5 == 0)
num /= 5;
return number == 1 ? true : false;
}
int GetUglyNumber(int index)
{
if(index <= 0)
return 0;
int number = 0;
int uglynumber = 0;
while(uglynumber < index)
{
++number;
if(IsUglyNUmber(number))
++uglynumber;
}
return number;
}
int Min(int num1, int num2, int num3)
{
int min = num1 < num2 ? num1 : num2;
min = min < num3 ? min : num3;
return min;
}
int GetUglyNumber_Solution(int index)
{
if (index <= 0)
return 0;
int* pUglyNumbers = new int[index];
pUglyNumbers[0] = 1;
int nextUglyIndex = 1;
int *p2 = pUglyNumbers;
int *p3 = pUglyNumbers;
int *p5 = pUglyNumbers;
while (nextUglyIndex < index)
{
int min = Min(*p2 * 2, *p3 * 3, *p5 * 5);
pUglyNumbers[nextUglyIndex] = min;
//T2
while (*p2 * 2 <= pUglyNumbers[nextUglyIndex])
++p2;
//T3
while (*p3 * 3 <= pUglyNumbers[nextUglyIndex])
++p3;
//T5
while (*p5 * 5 <= pUglyNumbers[nextUglyIndex])
++p5;
++nextUglyIndex;
}
int ugly = pUglyNumbers[nextUglyIndex - 1];
delete[] pUglyNumbers;
return ugly;
}