力扣 面试题 17.09. 第 k 个数

题目来源:https://leetcode.cn/problems/get-kth-magic-number-lcci/

大致题意:
返回素因子只能是 3 5 7 的数列的第 k 个数,这个数列前几个数为:1、3、5、7、9、15

思路

已知数列每个数的素因子只有 3 5 7,那么该数列的数在不为 1 时只能是 3 5 7 相乘组成的数

于是可以将当前已知的数列中的数放入 TreeSet 中,每次遍历取出当前最小的数,然后将当前数与 3 5 7 的乘积放入 set,重复这个操作直至取出第 k 个数

具体过程为:

  1. 使用一个TreeSet存下当前数列中的数,在遍历过程中不断更新;使用一个索引位 idx 表示当前取出的数是第几个
  2. 在 idx 小于 k 时,取出当前TreeSet中最小的数,然后将取出的数分别乘以 3、5、7,将乘积放入TreeSet中,这样保证了数列的下一位数一直在TreeSet中,同时集合也会自动去重

代码:

public int getKthMagicNumber(int k) {
        int[] nums = new int[]{3, 5, 7};
        TreeSet<Long> set = new TreeSet<>();
        set.add(1L);    // 初始时放入 1
        int idx = 0;    // 当前取出了几个数
        int res = 0;    // 存最新的取出的数
        while (idx < k) {
            long cur = set.first(); // 取出当前最小的数
            res = (int) cur;    // 更新结果
            for (int i = 0; i < 3; i++) {   // 放入当前数与 3 5 7 的乘积
                set.add(cur * nums[i]);
            }
            idx++;  // 更新标记
        }
        return res;
    }
三指针优化

上述方法的时间复杂度为 O(klogk),主要是TreeSet的排序时长

观察数列中的数可以发现,数列较大的数都是较小的数与 3 5 7 的乘积,可以知道下一个数是数列中已经出现数与 3 5 7 某个数的乘积

并且可以发现若数列出现过 nums[i] * 3 的数(这里假设 nums[i] 为数列第 i 个数),则接下来再出现的与 3 相乘的数会是 nums[i + 1] * 3,存在递增的关系,即可以通过指针记录当前与因子 3 相乘的数应该为数列的第几个数

于是可以通过一个数组记录数列中已经出现的数,并且用三个指针记录当前与 3 5 7 相乘的数的索引。每次求出对应索引与 3 5 7 的乘积,然后最小的乘积即为当前位置数列中的数,同时更新最小乘积对应指针即可。

具体解题思路为:

  1. 初始时,三个指针都为 1,数组第 1 位为 1,使用标记位 idx 表示当前计算到第 idx 个数
  2. 每次遍历,求出当前指针对应位置的数列中的数与 3 5 7 的乘积,并求出最小乘积
  3. 更新最小乘积对应的指针,需要注意的是,可能有两个指针与数列中的数的乘积相同,此时需要两个指针都需要更新。比如数列中的数为 1 3 5 7 9,因子 3 5 7 对应的指针为 3 2 2,那么下一个数为 min(5 * 3, 3 * 5, 3 * 7) = 15,此时 3 和 5 对应的指针计算的乘积都为 15,两个指针都需要更新

代码:

public int getKthMagicNumber_ans(int k) {
        int[] dp = new int[k + 1];
        dp[1] = 1;  // 放入第一个数
        // 初始化三个指针
        int p3 = 1, p5 = 1, p7 = 1;
        // 表示当前计算到第 idx 个数
        int idx = 1;
        while (idx <= k) {
            // 计算三个指针对应位置的数与 3 5 7 的乘积
            int num3 = dp[p3] * 3, num5 = dp[p5] * 5, num7 = dp[p7] * 7;
            // 当前数应该为最小乘积
            dp[idx] = Math.min(Math.min(num3, num5), num7);
            // 更新乘积对应的指针
            if (dp[idx] == num3) {
                p3++;
            }
            if (dp[idx] == num5) {
                p5++;
            }
            if (dp[idx] == num7) {
                p7++;
            }
            idx++;
        }
        return dp[k];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值