LintCode 4: Ugly Number II (重要)

本文探讨了如何高效地找到第N个丑数的问题。通过维护三个动态增长的列表,并利用最小堆或模拟链表合并的方式,实现了有效的算法。特别关注了去重问题,提供了三种不同的实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题其实并不容易。
观察一下其实每个丑数都是由3个list里面的最小的那个决定的。当最小的那个被选中,其对应的系数(Id)要加1。另外,有可能出现最小值同时出现在两个list(甚至3个list)里面的情况 (e.g., 5x2 and 2x5),这种情况下每个list的Id都要加1。

(1) 1x1, 1x2, 2x2, 2x2, 3x2, 3x2, 4x2, 5x2, 5x2, 5x2 …
(2) 1x1, 1x3, 1x3, 2x3, 2x3, 2x3, 3x3, 3x3, 4x3, 4x3
(3) 1x1, 1x5, 1x5, 1x5, 1x5, 2x5, 2x5, 2x5, 2x5, 3x5…

ugly sequences [1x1, 1x2, 1x3, 2x2, 1x5, 3x2, 4x2, 3x3, 5x2, 4x3…], index starts from 0.

下面这个视频讲得不错。
https://www.youtube.com/watch?v=uLctkg4Yf8M
下面这个链接讲的也不错
https://mp.weixin.qq.com/s/XXsWwDml_zHiTEFPZtbe3g
解法1:
注意这3个list就相当于是3个虚拟的链表一样。所以思路就跟链表合并的思路差不多。关键难度在于去重。注意下面的if不能是else if。

class Solution {
public:
    /**
     * @param n: An integer
     * @return: the nth prime number as description.
     */
    int nthUglyNumber(int n) {
        //the ugly number is 2^i * 3^j * 5^k,
        int id2 = 0;
        int id3 = 0;
        int id5 = 0;
        int index = 1;
        
        vector<int> uglys;
        uglys.push_back(1);
        
        while (index < n) {
            int candidate = min(min(uglys[id2] * 2, uglys[id3] * 3), uglys[id5] * 5);
            
            if (candidate == uglys[id2] * 2) { //不能是else if
                id2++;
            }
            
            if (candidate == uglys[id3] * 3) { //不能是else if
                id3++;
            }
            
            if (candidate == uglys[id5] * 5) { //不能是else if
                id5++;
            }
            
            uglys.push_back(candidate);
            index++;
        }
        
        return uglys[n - 1];
    }
};

解法2:加上去重。注意去重后下面的else if也可以用if。但else if 会快点。

class Solution {
public:
    /**
     * @param n: An integer
     * @return: return a  integer as description.
     */
    int nthUglyNumber(int n) {
        vector<int> ugly(n + 1, 0);
        int index = 1;
        ugly[1] = 1;
        int index_2 = 1, index_3 = 1, index_5 = 1;
        while (index <= n) {
            int candidate_2 = 2 * ugly[index_2];
            int candidate_3 = 3 * ugly[index_3];
            int candidate_5 = 5 * ugly[index_5];
            int candidate = min(candidate_2, min(candidate_3, candidate_5));
            if (candidate != ugly[index]) {
                if (index < n) ugly[++index] = candidate;
                else break;
            }
            
            if (candidate == candidate_2) index_2++;
            else if (candidate == candidate_3) index_3++;  //也可以是if, 因为上面已经去重
            else if (candidate == candidate_5) index_5++;  //也可以是if, 因为上面已经去重 
        }
        return ugly[n];
    }
};

解法3:图方便用的minHeap。但时间复杂度应该是O(nlogn)。

class Solution {
public:
    /**
     * @param n: An integer
     * @return: return a  integer as description.
     */
    int nthUglyNumber(int n) {
        priority_queue<long long, vector<long long>, greater<long long>> q;
        set<long long> s;
        q.push(1);
        s.insert(1);
        int index = 0;
        vector<int> uglys = {2, 3, 5};
        while (!q.empty()) {
           long long top = q.top();
           q.pop();
           index++;
           if (index == n) return top;
           for (int i = 0; i < uglys.size(); i++) {
               long long tmp = top * uglys[i];
               if (s.find(tmp) == s.end()) {
                   q.push(tmp);
                   s.insert(tmp);
               }
           }
        }
        return -1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值