1,问题:
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
2,想法:
我们先看下丑数的定义:
所谓丑数,就是不能被2,3,5以外的其他素数整除的数。1,2,3,4,5,6,8,9,10,12,15是最前面的11个丑数。(1不是素数)
(1),就是写一个判断一个数是不是丑数的函数,然后从1到n一直调用这个函数,直到记录的丑数个数满足等于N时结束。
判断一个数是不是丑数的的代码为:
bool isugly(int n)
{
if (n <= 0)
{
return false;
}
while (n != 1)
{
while(n % 2 == 0)
{
n = n / 2;
}
while (n % 3 == 0)
{
n = n / 3;
}
while (n % 5 == 0)
{
n = n / 5;
}
return n == 1? true:false;
}
其它代码就比较简单了。但是这个思想有个问题是:不管一个数是不是丑数,我们都要判断,时间效率不高,有没有一种办法只判断丑数。
(2)由丑数的定义直到,一个丑数(除1外)是排在它前边的某个丑数乘以2或者乘以3或者乘以5得到的,那么我们只要在当前得到的丑数集合中找到比当前集合中最大丑数刚刚大的丑数即可,因为我们要保持有序,才好找到第N个丑数,现在难点是怎么在O(1)时间内就找到一个丑数,使得它乘以2或者乘以3或者乘以5刚刚好大于当前最大丑数呢?
我们设置三个指针,一个表示所指丑数乘以2是下一个可能加入到丑数集合中的丑数,一个表示所指丑数乘以3是下一个可能加入到丑数集合中的丑数,一个表示所指丑数乘以5是下一个可能加入到丑数集合中的丑数,当我们找到这三个指针,然后分别对应乘以2,3,5后,再比较,拿出最小的那个就是要插入当前丑数集合的丑数。而这三个指针的更新标准是,若当前的指针乘以对应数,大于刚加入的丑数,则不要变,否则就加1.
3,具体代码:
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;
int *puglymulty2 = puglynumber;
int *puglymulty3 = puglynumber;
int *puglymulty5 = puglynumber;//三个指标
int uglynumberindex = 1;//记录丑数的个数
while (uglynumberindex < index)
{
int m = min(*puglymulty2 * 2, *puglymulty3 * 3, *puglymulty5 * 5);
puglynumber[uglynumberindex] = m;
//下边就是更新2,3,5的指标
if (*puglymulty2 * 2 <= m)//小于等于当前最大的丑数,则要更新
{ //因为在这个指标之前的数乘以2都比当前
puglymulty2++; //的最大丑数小,
}
if (*puglymulty3 * 3 <= m)
{
puglymulty3++;
}
if (*puglymulty5 * 5 <= m)
{
puglymulty5++;
}
uglynumberindex++;
}
int ugly = puglynumber[uglynumberindex - 1];
return ugly;
}
};
这个算法的时间效率提高了,但是有空间消耗,因为需要保存N个丑数,即是用空间代价来换取时间效率。
启发:
给定一个新的数学概念。在充分理解这个概念下,找到相应的规律,化简代码,提高时间复杂度。