第N个丑数

题目:把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。


思路:新的丑数必然是由已经有的丑数乘以2、3或5中某一个因子而得到的。

    假设我们已经有了前K个丑数,现在来计算第K+1个丑数。首先想到的是遍历这K个丑数,分别求出它们乘以2、3或5后得到的值,共3K个值,最后找出里面最小的那个就是第K+1个丑数。这个算法的结果是正确的,但它做了很多没有意义的计算。

    第一,假设第L个丑数乘以2后比第K个丑数还小,那么L之前的“丑数乘以2”就没必要计算,因为结果也比第K个丑数小,必然是前K个丑数中的一个。(3、5的情况类似)

    第二,假设第L个丑数乘以2后比第K个丑数还大,那么L之后的“丑数乘以2”也没必要计算,因为结果必然更大,而我们要找的是大于第K个丑数的丑数中最小的那个。(3、5的情况类似)

而我们为了求第K+1个丑数,就必须把前K个丑数都依次求出来。这个时候上面两条就很致命。

一个很好的解决办法是引入三个索引数 d2,d3,d5(即di, i代表因子2、 3或5)。 di的含义是:它前面的丑数乘以i后必然比第K个丑数小,它或它后面的丑数乘以i后必然比第K个丑数大。

于是,我们求下一个丑数时,只需要考虑这三个索引对应的丑数即可。假设已有丑数序列为arr[0~K-1],则分别计算arr[d2]*2, arr[d3]*3, arr[d5]*5,取它们中的最小值即可。注意:第K+1个丑数必然在这三个数当中!因为它们三个数都arr[K-1]大,且它们之前的数都不比arr[K-1]大。最后,假设我们找到arr[d3]*3是第K个丑数,则将d3加一并更新。

依次类推,可以求出第K+2, K+3, K+4... 个丑数。

C++代码:

int FindUgly(int n) 
{
	int* ugly = new int[n];
	ugly[0] = 1;
	int index2 = 0,index3 = 0,index5 = 0,index = 1;
	while (index < n)
	{
		int val = Min(ugly[index2] * 2, ugly[index3] * 3, ugly[index5] * 5);       
		if (val == ugly[index2] * 2)    
			++index2;
		if (val == ugly[index3] * 3)  
			++index3;
		if (val == ugly[index5] * 5)
			++index5;
		ugly[index++] = val;
	}
	int result = ugly[n - 1];
	delete[] ugly;
	return result;
}

一言蔽之,就是把我们每次查找到的痕迹保存起来,下一次查找时直接考虑痕迹处,并且更新痕迹。

猜你喜欢

转载自blog.csdn.net/sinat_38972110/article/details/80043014