算法深度学习2

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

看到题目,第一反应就是暴力,但是我试了一下,直接爆内存。这里我参考了别人的代码,然后反向推导出这题的理解思路和解题过程。看了下文,你会和我一样惊叹这世上竟有如此神奇的代码和如此精妙的思路!

分析题意:题目很简单,就是将所有只包含素因子的数叫做丑数,比如1 2 3 4 5 6 8 9 10 12 15  16 18...那么,我们是不是可以这样理解,就是求所有的N=(2^i) * (3^j) * (5^k)  (其中i,j,k表示正整数),最后排序N,求指定下标的N.

经过上面的分析,我们成功的将问题转化了,现在问题来了,N的大小有规律可言吗?简单的测一下:

i   j   k      N

1   0   0      2

0   1   0      3

0   0   1      5

1   1   0      6

1   0   1      10

0   1    1     15

1    1    1    30

2    0    0    4

2    1    0     12

我们发现,好像没有什么规律而言,也确实没什么规律可言。所以,就不用再花时间来琢磨这不太容易发现的规律。依据题意,我们需要对N进行排序,如果是找到所有数据再排序,效率明显太低,所以我们要想办法找到一个数据就把它放在正确的位置。那要如何实现呢?

每次找到数据,都必须保证该数据是所有可能中最小的数据。那么每次比较的所有可能数据有哪些呢?

1 2 3 4 5 6 8 9 10 12 15 16 18...它们每个数都会和2 3 5相乘,然后决策出下一个数字是什么。但是事实上,并不是下一个数并不是和以上的每一个都相关.举个例子:18,与1 2 3 4无关,与8 10 12 15无关,因为它们2 3 5中任意一个相乘都不可能得到20。比如18下一个丑数将会是20,其实这个而是与1 2 3 6无关,与8 9 12 15 16 18也无关再比如20后面的数将是24,与1 2 3 4 5无关,与9 10 15 16 18 20也无关。我们可以对比一下18,20和24:

18 ==>6*3   9*2           20 ==>4*5  10*2             24 ==>8*3  12*2         似乎可以看到与2相乘的数在不停的增大,与3相乘的数也是如此,为了验证这种想法,我们多列举几个:

25==>5*5    27==>9*3    30==>10*3  15*2    32==>16*2

相信到了这里,我们就又可以大胆推测了,下一个数的出现,是从之前部分数据与2,3,5的乘积中取出最小的一个,而且这部分数据会逐渐向后移动,而且我们发现,与2相乘的数大于与3相乘的数,与3相乘的数大于与5相乘的数。

到了这一步,我们就会很自然的想到一个东西了--下标!下标不就是记录当前的位置的吗?因为是从与2 3 5的乘积中选出最小的数,所有我们设置三个下标来记录它们的移动路径,分别为t2 t3 t5(t2>=t3>=t5)。每次找到一个新的数,就将对应的下标+1,总的下标也+1.例如:

N=25,在此之前t5=4,取出N=25,t5=5,t5对应的数变成了6.

然后每次查找下一个数时,根据原策略需要分别比较每个数字与2 3 5的乘积的大小。但是事实上,每个位置上的数都会经历与2 3 5相乘,而且它们不应该同时比较。什么意思呢?简单点说根据最优策略,最大的数应该与2相乘,其次与3相乘,最末与5相乘。t2>=t3>=t5,t2位置上的数会分别与2 3 5相乘,而与3 5相乘时,t2已经在后面了,t3或t5变成了此时的t2。所以,我们比较的是t2*2  与  t3*3  与t5*5之间的大小,取出它们之间最小值,当作下一个丑数。这样就能保证每次取到的数都是最小的丑数。

附上源码一份:

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        int t2=0,t3=0,t5=0,i=1;
        if(index<7)
            return index;
        vector a(index);
        a[0]=1;
        for(;i

 

你可能感兴趣的:(算法)