86 丑数II(Ugly Number II)

1 题目

题目:丑数II(Ugly Number II)
描述:如果一个数只有质数因子2,3,5 ,那么这个数是一个丑数。前10个丑数分别为 1, 2, 3, 4, 5, 6, 8, 9, 10, 12…设计一个算法,找出第N个丑数。我们可以认为 1 也是一个丑数。

lintcode题号——4,难度——medium

样例1:

输入:n = 9
输出:10
解释:[1,2,3,4,5,6,8,9,10,....],第9个丑数为10。

样例2:

输入:n = 1
输出:1

2 解决方案

2.1 思路

  要找第n个丑数,考虑从1开始向后一个个计算出丑数,每次取出最小的数,乘以2、3、5,再对集合中的数排序,再取最小的数乘以2、3、5,以此类推,直到取到第n个数即为结果。需要一个能够放入无序的数,并取出最小的数的数据结构,考虑使用优先队列来解。
  还有一种方式,使用3个指针分别表示被乘数2、3、5,初始都指向数组(初始只有数字1),将被指数1与指针表示的值2、3、5分别相乘,取最小的数加入数组,并将对应的指针向右移动一位,以此类推,每次通过被指数与指针表示的数相乘,再取得的最小值,即为下一个丑数的值,重复n次,即可找到第n个丑数。

2.3 时间复杂度

  使用优先队列的方式,向优先队列插入和删除元素的耗时为O(log n),从优先队列中获取顶部元素的耗时为O(1),遍历n次,总时间复杂度为O(n * log n)。
  使用三指针方式,进行一轮循环即可找到第n个数,时间复杂度为O(n)。

2.4 空间复杂度

  使用优先队列的方式,用到了优先队列,空间复杂度为O(n)。
  使用三指针的方式,空间复杂度为O(1))。

3 源码

3.1 优先队列方式

细节:

  1. 当前优先序列从1开始,将当前数乘以2、3、5,并加入最小堆优先序列中。
  2. 每次弹出顶部元素(当前序列中最小值),用该值乘以2、3、5得到结果放入优先序列中,循环n-1次,此时的优先序列顶部元素即为第n个丑数。

优先队列定义 template<T, Container, Compare> priority_queue;
示例:
priority_queue<int> myQueue; // 最大堆优先序列(省去默认参数)
priority_queue<int, vector<int>, less<int>> myQueue; // 最大堆优先序列(完整)
priority_queue<int, vector<int>, greater<int>> myQueue; // 最小堆优先序列

C++版本:

/**
* @param n: An integer
* @return: return a  integer as description.
*/
int nthUglyNumber(int n) {
    // write your code here
    int result = 1;
    if (n == 1)
    {
        return result;
    }

    priority_queue<long, vector<long>, greater<long>> numQueue; // 定义最小堆优先队列
    numQueue.push(1);
    for (int i = 1; i < n; i++)
    {
        long cur = numQueue.top();
        numQueue.push(cur * 2); // 加入最小数与2的乘积
        numQueue.push(cur * 3); // 加入最小数与3的乘积
        numQueue.push(cur * 5); // 加入最小数与5的乘积
        numQueue.pop(); // 弹出前n-1个元素

        while (numQueue.top() == cur)
        {
            numQueue.pop(); // 弹出重复的数
        }
    }

    return numQueue.top(); // 返回优先队列顶部元素
}

3.2 三指针方式

细节:

  1. 用一个序列nums记录前n个丑数。
  2. 三个指针index2,index3和index5指向nums中的第一个元素1。
  3. 最小的丑数只可能出现在index2指向的数的2倍、index3指向的数的3倍和index5指向的数的5倍三者中的一个,找到最小的数后加入序列,并右移一位对应的指针,就能保证生成的丑数是从小到大有序的,循环n轮即可找到第n个丑数。

C++版本:

/**
* @param n: An integer
* @return: return a  integer as description.
*/
int nthUglyNumber(int n) {
    // write your code here
    vector<int> nums;
    nums.push_back(1);
    int index2 = 0;
    int index3 = 0;
    int index5 = 0;
    while (nums.size() != n)
    {
        int minValue = min(nums.at(index2) * 2, min(nums.at(index3) * 3, nums.at(index5) * 5));
        nums.push_back(minValue);

        if (minValue == nums.at(index2) * 2)
        {
            index2++;
            //continue; // 此处不用continue,为了跳过重复的数
        }
        if (minValue == nums.at(index3) * 3)
        {
            index3++;
            //continue; // 此处不用continue,为了跳过重复的数
        }
        if (minValue == nums.at(index5) * 5)
        {
            index5++;
            //continue; // 此处不用continue,为了跳过重复的数
        }
    }

    return nums.back();
}

猜你喜欢

转载自blog.csdn.net/SeeDoubleU/article/details/124657738