丑数 II(Leetcode-264)-优先队列 &三指针法

题目

知识点

  • 优先队列
  • 三指针法

思路

优先队列法

  • 关键点在于某个丑数一定是在前面某个丑数*2|*3|*5上得到的,否则无法满足只包含质因数的条件
  • 第一次见到丑数就是在紫书上面看到的,当时讲解就是用的优先队列实现的,以例子来说:
    第一次:1(1被认为是丑数),后面的丑数必然是在1的基础上*2,*3,*5
    第二次:1|2,3,5,取2再*2,*3,*5
    第三次:1,2,3,5| 4,6,10 -> 排序后: 1,2,3,4,5,6,10,取3再*2,*3,*5
    第四次: 1,2,3,4,5,6,10 | 6(重复),9,25

  • 因此,我们维护一个从小到大的优先队列,每次取当前优先队列最小的元素 *2,*3,*5,并出队,将答案记录在一个set中(因为结果可能重复),在寻找完毕后,输出第n个即可。

三指针法

  • 以最开始的一部分过程来说明该算法,| 左侧一列是当前所有的丑数,| 右侧是设置的三个指针(指向某个丑数),一开始丑数为1,那么初始位置都在丑数1上:
    初始状态:
    1 | 2 3 5

    Step1:
    *2,*3,*5的指针都指向1,那么都分别*1,得到2,3,5,取最小2,放入 | 左侧丑数集合中,且*2的指针++,得到:
    1 | 3 5
    2 | 2

    Step2:.
    *3,*5的指针仍然指向1,那么都分别*1,得到3,5。
    *2指针指向2,那么得到2*2 = 4
    上面三个得到的最小乘积为3,放入 | 左侧丑数集合中,且*3的指针++,得到:
    1 | 5
    2 | 2 3
    3 |

    Step2:.
    *5的指针仍然指向1,那么得到15 = 5。
    *2和*3指针指向2,那么得到2
    2 = 4和2*3=6
    上面三个得到的最小乘积为4,放入 | 左侧丑数集合中,且*5的指针++,得到:
    1 |
    2 | 3 5
    3 | 2
    4 |

  • 在下一步中,需要注意的是元素的重复,*3指针指在丑数2上,*2指针乘在3上,乘积都为6,而*5的指针在2上,乘积为10。两个乘积都是6且最小,那我们更新元素指针时候两个指针都++即可,即:
    1 |
    2 | 5
    3 | 3
    4 | 2

  • 按照上述步骤我们就能得到第n个丑数,其实也可以说是一个DP问题

代码

优先队列法

class Solution {
public:
    int mult[3] = {2, 3, 5};
    priority_queue<double, vector<double>, greater<>> q;
    unordered_set <double> s;

    int nthUglyNumber(int n) {
        q.push(1);
        int cnt = 0;
        while (true) {
            int base = q.top();
            q.pop();
            cnt++;
            if (cnt == n) return base;
            for (double i:mult){
                double ans = base * i;
                if (!s.count(ans)){
                    s.insert(ans);
                    q.push(ans);
                }
            }
        }
    }
};

三指针法

  • 似乎是vector操作的问题,效率并没有想象中那么快
class Solution {
    public:
    int nthUglyNumber(int n) {
        int* dp = new int[n + 1];
        dp[1] = 1;
        int p2 = 1, p3 = 1, p5 = 1;
        for (int i = 2; i <= n; i++) {
            int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;
            dp[i] = min(min(num2, num3), num5);
            if (dp[i] == num2) {
                p2++;
            }
            if (dp[i] == num3) {
                p3++;
            }
            if (dp[i] == num5) {
                p5++;
            }
        }
        return dp[n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值