丑数 题解

写在前面

剑指offer 编程题:丑数。

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

基本思路(暴力解)

  • 从2开始遍历每一个数判断是不是丑数。如果是丑数计数器count++,如果不是数字加1,继续判断。算法复杂度极大。

进阶思路

  • 观察发现,每一个丑数本身必然是之前的某个丑数的值乘以2或3或5。我们只需要按照这个规律找到下一个丑数,同时确定下一个丑数是谁。简单的方式是从1开始维护一个数组,数组存放求解第N个丑数过程当中的中间结果。通过递推关系我们可以得出 :
  • next[n] = min(M2,M3,M5);
  • M2为遍历数组前n-1个元素乘以2得到的第一个大于next[n-1]的值同理可得M3,M5。
    缺陷就是每次都需要遍历前n-1个元素得到M2,M3,M5.整个算法的复杂度为O(N*N);

改进方案

其实每次在找M2,M3,M5的过程可以看成在前面的丑数当中找到某个丑数乘以2或3获刚好大于next[n-1]。
假设位置i的元素是我们需要找到求得M2的数组下标。每次从头开始到i-1遍历得到的值乘以2都会小于next[n-1],同时从i+1开始到n-1得到的乘积都大于next[n]。所以通过记录下标的方式可以不用做过多的遍历。以n2,n3,n5表示当前对于2,3,5合适的下标。初始化都为0。当求得的next[n]等于某个因子乘出来的积那么对应的下标加1。

代码

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index<=0) return 0;
        if(index==1) return 1;
        vector<int> v;
        v.push_back(1);
        int n2=0,n3=0,n5=0;
        for(int i=0;i<index-1;++i) {
            int m2 = v[n2]*2;
            int m3 = v[n3]*3;
            int m5 = v[n5]*5;
            int tempMin = min(m2,min(m3,m5)); //竞争产生下一个丑数 (最小)
            //将产生这个丑数的index*向后挪一位;  
            if(m2==tempMin) ++n2;
            if(m3==tempMin) ++n3;//这里不能用elseif,因为可能有两个最小值,这时都要挪动
            if(m5==tempMin) ++n5; 
            v.push_back(tempMin);
        }
        return v[index-1];
    }
};

猜你喜欢

转载自blog.csdn.net/zhc_24/article/details/81185247