面试题 17.09. 第 k 个数[小顶堆][动态规划]

思路

暴力搜索

因为要求的数只含3\5\7这三个素因子,因此只需判断将每个数除3/5/7除尽后是否为1,从而判断是不是所要求序列中的一个数。

超时

代码如下:

class Solution {
    public int getKthMagicNumber(int k) {
        int cur = 0, count = 0;
        int temp;
        while(count < k){
            cur++;
            temp = cur;
            while(temp % 3 == 0){
                temp /= 3;
            }
            while(temp % 5 == 0){
                temp /= 5;
            }
            while(temp % 7 == 0){
                temp /= 7;
            }

            if(temp == 1){
                //说明只能被3,5,7整除
                count++;
            }
        }
        return cur;
    }
}

小顶堆

通过维护一个小顶堆,堆内元素为对应满足要求的序列,第k个弹出的数就是序列中第k个数,堆初始化时添加元素1
当搜索到(弹出)某个数x时,那么x*3x*5x*7肯定是后续序列中一个满足要求的数,将这三个数加入堆中。

需要注意的是,虽然xint,但是乘以3/5/7后可能会爆,导致负数的出现,使得入堆元素为负数,后续被优先弹出,答案错误。

class Solution {
    public int getKthMagicNumber(int k) {
        int[] factors = new int[]{3,5,7};
        Set<Long> set = new HashSet<>();  //存储已经存在的值
        PriorityQueue<Long> heap = new PriorityQueue<>();

        int count = 0;
        long ans = 1;
        heap.add(1L);
        set.add(1L);
        
        while(count < k){
            ans = heap.poll();
            count++;
            
            for(int factor : factors){
                long newNum = factor * ans;
                if(!set.contains(newNum)){
                    set.add(newNum);
                    heap.add(newNum);
                }
            }
        }
        return (int)ans;
    }
}

动态规划/多指针

参考:

符合题目中条件的数称为丑数。

不难发现,后续的丑数都是由之前的丑数通过乘以因子3/5/7得到的。
如果吧丑数序列称为uglyugly[i]为第i+1个丑数(下标从0开始)。考虑以下三个数列:

  1. ugly[0]*3, ugly[1]*3, ugly[2]*3, ugly[3]*3, ......
  2. ugly[0]*5, ugly[1]*5, ugly[2]*5, ugly[3]*5, ......
  3. ugly[0]*7, ugly[1]*7, ugly[2]*7, ugly[3]*7, ......

代入答案中的数列ugly = [1,3,5,7,9, ....]

  1. 1*3, 3*3, 5*3, 7*3, 9*3, ......
  2. 1*5, 3*5, 5*5, 7*5, 9*5, ......
  3. 1*7, 3*7, 5*7, 7*7, 9*7. ......

通过将上述三个数列合并为一个数列就可以得到我们所需要的答案,这其实就是一个合并有序线性表的问题。定义三个指针分别指向上面的三个序列,下一个丑数一定是三个index代表的值中最小的那个,然后将相应的index++即可。

当两个指针对应于相同的值,两个指针同时递增。

代码如下

class Solution {

    //需要注意到的是,每个数都是由之前的数与因子3、5、7相乘得到的。
    //利用3指针指向对应因子的序列
    public int getKthMagicNumber(int k) {
        int[] ans = new int[k+1];
        // 初始化第一个数为1
        ans[1] = 1;

        int p3 = 1, p5 = 1, p7 = 1;  //都指向第一个数
        for (int i = 2; i <= k; i++) {
            int cur = Math.min(ans[p3]*3, Math.min(ans[p5]*5, ans[p7]*7));
            ans[i] = cur;

            if (cur == ans[p3] * 3){
                p3++;
            }
            if (cur == ans[p5] * 5){
                p5++;
            }
            if (cur == ans[p7] * 7){
                p7++;
            }
        }

        return ans[k];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值