Leetcode264答案与解析 - Ugly Number II

原题

Leetcode原题传送门

Write a program to find the n-th ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5

Example:
Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.
  1. 1 is typically treated as an ugly number.
  2. n does not exceed 1690.

解析

由于我没看懂原版网上提供的Hint,在此说下我的理解。先阐述一下问题的一些观察。

观察1 根据定义,较大的ugly number一定为某一较小的ugly数的x2, x3, x5倍。

基于观察1,我们希望不断地循环,根据前面较小的ugly数,逐渐扩大ugly数的列表。应该遵循什么样的法则呢?我们通过观察,继续发现了以下事实:

观察2 如果一个数的x2, x3, x5倍不在目前的序列里,那么他一定在我们待生成的列表上。

观察3 如果一个数的x2倍在目前的序列里,那么生成新数字一定基于后面的数字x2倍了,否则与已经生成的数字重复。x3, x5倍同理。

此外,我们每次生成新的数字,都希望能生成最小切满足条件的ugly数。这样才能保证序列不漏掉任何一个数。如何找到最小的数呢?我们基于贪心算法:

  1.  初始化数列,第一个元素为1(第一个ugly数)。初始化三个指针,指向第一个数。将他们的值分别记为num_x2, num_x3, num_x5
  2.  新的数=min(num_x2 * 2 + num_x3 * 3 + num_x5 * 5)。(要寻找最小满足条件的数)
  3. 若num_x2 * 2 = 新的数,则将x2倍的指针指向下一个ugly数。x3, x5的同理。(基于观察3)
  4. 不断循环,重复n次。返回第n元素。

为什么生成的数一定是符合条件最小的数?

完备性:首先基于观察1,新生成的数一定是当前ugly数中的x2, x3, x5倍。而我们依次比较每个ugly数的x2, x3, x5倍并加入列表,不可能漏掉任何一个数。可以从数学归纳法的视角看:1:假设某倍数的指针指向的第j个数字到第一个都是完备的,那么后一个生成的数字一定能保证是乘上该倍数最小的。2:开始时列表只有一个数字1,一定是完备的。

速度优势:这一算法的优势在于:我们生成第n个ugly数时,不用穷举每一个n-1比他小的数。记当前某倍数指向的为第j个数。因为根据步骤3:如果他的倍数(例如两倍)已经在数组里的话,没有必要再去考虑1~j-1个数。并且,既然他x2或x3或x5数不在数列中,就说明他的对应倍数一定等待生成,没必要找j+1~n-1的数,否则会漏掉该数。

答案(附带主程序)

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    int nthUglyNumber(int n) {
        /*
        Target: constructing a complete sequence of increasing ugly number.

        Fact1: The larger ugly number must be a double, trple or quintuple of smaller ugly number.
        Fact2: If the target multiple of a number is not included in current sequence, the
        target multiple of this number is in our to-do list.
        Fact3: If the target multiple of a number is included in current sequence, it is high
        time that we were supposed to move on to next number.

        Key idea:
        The very idea that to find all ugly numbers of which mulitple of it is not included and smallest. 
        
        1. We do this by setting three pointers to ugly number corresponding to 2x, 3x and 5x. They are all
        initialized into the first ugly number, 1. We find a larger ugly number by iterations.
        2. Find smallest one among *pointer_x2 * 2, *pointer_x3 * 3, *pointer_x5 * 5. Add it into the sequence.
        3.Update pointer to next ugly number iff target multiple of it is equal to the number in this iteration. 
        By then,  we guarantee it is small enough due to Fact1. Will double multiple of this number also included
        in sequence already(like x2 and x4)? It won't happen since it must be updated by previos iteration.
         */
        vector<int> l(n); // initialize the squence.
        int p_2, p_3, p_5; // initialize the pointer
        l[0] = 1;
        p_2 = 0;
        p_3 = 0;
        p_5 = 0;
        for (int i = 1; i < n; ++i) {
            l[i] = min(min(l[p_2] * 2, l[p_3] * 3), l[p_5] * 5);
            if (l[p_2] * 2 == l[i]) ++p_2;
            if (l[p_3] * 3 == l[i]) ++p_3;
            if (l[p_5] * 5 == l[i]) ++p_5;
        }
        return l[n - 1];
    }
};

int main() {
    int n;
    Solution* sol = new Solution();
    cout << "Please input n: ";
    cin >> n;
    cout <<  sol->nthUglyNumber(n) << endl;
    return 0;
}

Acknowledgement

参考了Discussion中的这个Post

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值