思路
暴力搜索
因为要求的数只含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*3
、x*5
、x*7
肯定是后续序列中一个满足要求的数,将这三个数加入堆中。
需要注意的是,虽然
x
为int
,但是乘以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得到的。
如果吧丑数序列称为ugly
,ugly[i]
为第i+1
个丑数(下标从0开始)。考虑以下三个数列:
ugly[0]*3, ugly[1]*3, ugly[2]*3, ugly[3]*3, ......
ugly[0]*5, ugly[1]*5, ugly[2]*5, ugly[3]*5, ......
ugly[0]*7, ugly[1]*7, ugly[2]*7, ugly[3]*7, ......
代入答案中的数列ugly = [1,3,5,7,9, ....]
1*3, 3*3, 5*3, 7*3, 9*3, ......
1*5, 3*5, 5*5, 7*5, 9*5, ......
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];
}
}