4.丑数2

首先了解一下什么是丑数,丑数的定义是,只包含质因子 2, 3, 5 的正整数。比如 6, 8 就是丑数,但是 14 不是丑数因为他包含了质因子 7。而判断一个整数是不是丑数则可以借鉴[517.丑数](http://www.cnblogs.com/yunlambert/p/8010871.html)。
那么这道题其实是让我们找出第n个丑数(包括1)
开始的思路自然是遍历,万宗不变遍历嘛,就有了以下代码:

class Solution {
public:
    /*
     * @param n: An integer
     * @return: the nth prime number as description.
     */
    int nthUglyNumber(int n) {
        // write your code here
      int count=0;
      int max=100000000;
      for(int i=1;i<=max;i++)
       {
           if(isUgly(i)) count++;
           if(count==n) return i;
       }
    }
     
    bool isUgly(int num) {
        // write your code here
        if (num <= 0) 
           return false;
     while (num%2 == 0)
           num = num / 2;
     while (num%3 == 0)
           num = num /3;
     while (num%5 == 0)
           num = num /5;
     if (num == 1)
         return true;
    return false ;
    }
};

但遗憾的是,在输入1665的时候提示超时了。。的确,遍历算法的缺点就在这里,所以需要对算法进行优化。这里推荐一篇博客面试题之丑数的C++实现求解,对于丑数的解法介绍的还是比较全面的。
我们知道丑数序列可以拆分为下面3个子列表:

(2) 1×2, 2×2, 3×2, 4×2, 5×2, …
(3) 1×3, 2×3, 3×3, 4×3, 5×3, …
(5) 1×5, 2×5, 3×5, 4×5, 5×5, …

一个新的丑数可以看做是一个旧的丑数乘以2,3,5得到的(第一个丑数是1,第二个为2,则第二个可以看做第一个丑数2得到了第二个丑数(12、13和15中的最小者)),本题解题难点在于维护丑数数列为从小到大排列的顺序。

根据上述思路可以做如下实现:

  1. 维护三个列表l2,l3,l5,分别是当前处理的丑数num的2,3,5倍,因此三个列表组内均是按照从小到大进行排列的;
  2. 每次获得三个列表的第一个结点(最小的结点)为当前丑数num,根据当前丑数值在三个列表末端插入新的较大的丑数值,等待后续处理;
  3. 若列表的第一个结点与当前丑数num相等,则删除列表第一个结点(表示该丑数已经被计数);
  4. 题目的初始条件为num = 1, count = 1;
class Solution {
public:
    int nthUglyNumber(int n) {
        int count = 1;
        int num = 1;
        list<int> l2;
        list<int> l3;
        list<int> l5;

        while(count != n)
        {
            l2.push_back(2*num);
            l3.push_back(3*num);
            l5.push_back(5*num);

            int l2min = l2.front();
            int l3min = l3.front();
            int l5min = l5.front();

            int minNum = min(l2min, min(l3min, l5min));
            if(l2min == minNum) l2.pop_front();
            if(l3min == minNum) l3.pop_front();
            if(l5min == minNum) l5.pop_front();
            num = minNum;
            ++count;
        }
        return num;
    }
};

对于乘以2而言,肯定存在某一个丑数t2,使得排在它之前的每一个丑数乘以2得到的结果都会小于已有最大丑数,在它之后的每个丑数乘以2得到的结果都会大于当前最大丑数。我们只需要记下这个丑数的位置,同时每次生成新的丑数时去更新这个t2。对于3和5,同样存在t3和t5。

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> uglyNumSeq(1, 1);
        int i2 = 0, i3 = 0, i5 = 0;
        while (uglyNumSeq.size() < n) {
            int n2 = uglyNumSeq[i2] * 2;
            int n3 = uglyNumSeq[i3] * 3;
            int n5 = uglyNumSeq[i5] * 5;
            int next = min(n2, min(n3, n5));
            if (next == n2) ++i2;
            if (next == n3) ++i3; //注意不用else if,因为不同序列可能有相同值
            if (next == n5) ++i5;
            uglyNumSeq.push_back(next);
        }
        return uglyNumSeq.back();
    }
};
posted @ 2017-12-09 11:58  MrYun  阅读(145)  评论(0)    收藏  举报