面试题17.09.第k个数,大乱炖

面试题17.09.第k个数

题意分析:

找出素因子3、5、7乘积的顺序序列的第k个数

1, 3, 5, 7, 9, 15, 21…

k = 5 ——》9

3 a × 5 b × 7 c 3^a \times 5^b \times 7^c 3a×5b×7c形式的数num

很自然会想到当 a + b + c ≤ k a + b + c \le k a+b+ck时,排序后的第k个数,注意对应唯一的三元组(a, b, c),num是唯一的

这样的三元组有 ( k + 2 ) ( k + 1 ) 2 + . . . + 2 × 1 2 \frac{(k+2)(k+1)}{2} + ... + \frac{2 \times 1}{2} 2(k+2)(k+1)+...+22×1

// 时间复杂度为O(N^3logN)
public int getKthMagicNumber1(int k) {
    ArrayList<Long> arr = new ArrayList<>();
    for(int i = 0; i <= k; i++) {
        for (int j = 0; j <= k - i; j++) {
            for (int m = 0; m <= k - i - j; m++) {
                long num = (long)(Math.pow(3, i) * Math.pow(5, j) * Math.pow(7, m));
                arr.add(num);
            }
        }
    }
    Collections.sort(arr);
    return arr.get(k - 1).intValue();
}

复杂度太高,超时

三元组列举提交结果

列举的三元组数量太多,得另辟蹊径

观察 3 a × 5 b × 7 c 3^a \times 5^b \times 7^c 3a×5b×7c,发现后面的数都是由前面的数 × 3 , × 5 , × 7 \times3, \times 5, \times7 ×3,×5,×7所得

那就生成序列,维持其有序性,可以用PriorityQueue或者TreeSet,注意去重

// 时间复杂度为O(NlogN)
public int getKthMagicNumber2(int k) {
    TreeSet<Long> set = new TreeSet<>();
    set.add(1L);
    for(int i = 0; i < k; i++) {
        long num = set.pollFirst();
        set.add(num * 3);
        set.add(num * 5);
        set.add(num * 7);
    }
    return (int)set.pollFirst().longValue();
}

去重有序提交结果

半山腰的风景也很美,但我们不能止步不前

基本达标,但只击败了22%的人,可以再优化

优化的点在于去重和有序,本来由前面生产后面的数都是相对有序,相对有序,再判断一下都可以自行去重了

将一个队列拆分为三个队列

1, 3, 9, 15, …

1, 5, 25, 35, …

1, 7, 21, 35, …

双向队列实现

// 时间复杂度为O(N)
public int getKthMagicNumber3(int k) {
    Deque<Integer> arrNum3 = new LinkedList<>();
    Deque<Integer> arrNum5 = new LinkedList<>();
    Deque<Integer> arrNum7 = new LinkedList<>();
    arrNum3.addLast(1);
    arrNum5.addLast(1);
    arrNum7.addLast(1);
    int minValue = 1;
    for(int i = 0; i < k; i++) {
        minValue = Math.min(Math.min(arrNum3.peekFirst(), arrNum5.peekFirst()), arrNum7.peekFirst());
        if (arrNum3.peekFirst() == minValue) {
            arrNum3.pollFirst();
        }
        if (arrNum5.peekFirst() == minValue) {
            arrNum5.pollFirst();
        }
        if (arrNum7.peekFirst() == minValue) {
            arrNum7.pollFirst();
        }
        arrNum3.addLast(minValue * 3);
        arrNum5.addLast(minValue * 5);
        arrNum7.addLast(minValue * 7);
    }
    return minValue;
}

动态数组 + 索引

// 时间复杂度为O(N)
public static int getKthMagicNumber4(int k) {
    ArrayList<Integer> arrNum3 = new ArrayList<>(k);
    ArrayList<Integer> arrNum5 = new ArrayList<>(k);
    ArrayList<Integer> arrNum7 = new ArrayList<>(k);
    arrNum3.add(1);
    arrNum5.add(1);
    arrNum7.add(1);
    int arrNum3Idx = 0;
    int arrNum5Idx = 0;
    int arrNum7Idx = 0;
    int minValue = 1;
    for(int i = 0; i < k; i++) {
        minValue = Math.min(Math.min(arrNum3.get(arrNum3Idx), arrNum5.get(arrNum5Idx)), arrNum7.get(arrNum7Idx));
        if (arrNum3.get(arrNum3Idx) == minValue) {
            arrNum3Idx++;
        }
        if (arrNum5.get(arrNum5Idx) == minValue) {
            arrNum5Idx++;
        }
        if (arrNum7.get(arrNum7Idx) == minValue) {
            arrNum7Idx++;
        }
        arrNum3.add(minValue * 3);
        arrNum5.add(minValue * 5);
        arrNum7.add(minValue * 7);
    }
    return minValue;
}

数组 + 索引,避免装箱/拆箱带来的性能损耗

// 时间复杂度为O(N)
public static int getKthMagicNumber4_2(int k) {
    int[] arrNum3 = new int[k + 7];
    int[] arrNum5 = new int[k + 7];
    int[] arrNum7 = new int[k + 7];
    arrNum3[0] = 1;
    arrNum5[0] = 1;
    arrNum7[0] = 1;
    int arrNum3Idx = 0;
    int arrNum5Idx = 0;
    int arrNum7Idx = 0;
    int minValue = 1;
    for(int i = 1; i <= k; i++) {
        minValue = Math.min(Math.min(arrNum3[arrNum3Idx], arrNum5[arrNum5Idx]), arrNum7[arrNum7Idx]);
        if (arrNum3[arrNum3Idx] == minValue) {
            arrNum3Idx++;
        }
        if (arrNum5[arrNum5Idx] == minValue) {
            arrNum5Idx++;
        }
        if (arrNum7[arrNum7Idx] == minValue) {
            arrNum7Idx++;
        }
        arrNum3[i] = minValue * 3;
        arrNum5[i] = minValue * 5;
        arrNum7[i] = minValue * 7;
    }
    return minValue;
}

多个有序队列提交结果

击败了100%的人,到这里差不多了,但还有最优解

在人间已是癫,何苦要上青天

空间优化,仔细再想想,这三个队列有重复值

能不能不忘初心,再将三者合二为一呢

有点像多个有序列表合并

// 时间复杂度为O(N)
public static int getKthMagicNumber5(int k) {
    int[] arrNum = new int[k + 7];
    arrNum[0] = 1;
    int arrNum3Idx = 0;
    int arrNum5Idx = 0;
    int arrNum7Idx = 0;
    int minValue = 1;
    for(int i = 1; i < k; i++) {
        minValue = Math.min(Math.min(arrNum[arrNum3Idx] * 3, arrNum[arrNum5Idx] * 5), arrNum[arrNum7Idx] * 7);
        if (minValue % 3 == 0) {
            arrNum3Idx++;
        }
        if (minValue % 5 == 0) {
            arrNum5Idx++;
        }
        if (minValue % 7== 0) {
            arrNum7Idx++;
        }
        arrNum[i] = minValue;
    }
    return minValue;
}

在这里插入图片描述

内存击败66%——》内存击败88%


这题目再进阶下,更改下素数因子的值,或者增加几个素数因子,有没有**普世版**?
// 时间复杂度为O(N),扩展版
public static int getKthMagicNumber5_2(int k) {
    int[] arrNum = new int[k + 7];
    int[] bases = {7, 5, 3};
    int[] idxs = new int[bases.length];
    arrNum[0] = 1;
    int minValue = 1;
    for(int i = 1; i < k; i++) {
        minValue = Integer.MAX_VALUE;
        for (int c = 0; c < bases.length; c++) {
            minValue = Math.min(minValue, arrNum[idxs[c]] * bases[c]);
        }
        for (int c = 0; c < bases.length; c++) {
            if (minValue % bases[c] == 0) {
                idxs[c]++;
            }
        }
        arrNum[i] = minValue;
    }
    return minValue;
}

更改素数因此列表bases即可,填写的顺序不重要

如果是非素数因子,即因此之间有公约数,那这样以乘积生成的数就会有重复,就不能合并为一个队列,简单去重了,那就可以按照多个因数队列处理,时间复杂度为O(MN),空间复杂度为O(MN),其中M为因子个数,N为第k个数

引用

第 k 个数-就是想不通为何三指针?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值