---
主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black
贡献主题:github.com/xitu/juejin…
theme: juejin highlight:
LC 263 丑数
编写一个程序判断给定的数是否为丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。
示例 1:
输入: 6 输出: true 解释: 6 = 2 × 3 示例 2:
输入: 8 输出: true 解释: 8 = 2 × 2 × 2 示例 3:
输入: 14 输出: false 解释: 14 不是丑数,因为它包含了另外一个质因数 7。 说明:
1 是丑数。 输入不会超过 32 位有符号整数的范围: [−231, 231 − 1]
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ug… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
public boolean isUgly(int num) {
// 丑数是只包含质因数2,3,5的正整数,非正整数返回false
if (num < 1) return false;
while (num % 5 == 0) num /= 5;
while (num % 3 == 0) num /= 3;
while ((num & 1) == 0) num >>>= 1;
// 能被2,3,5整除就不断相除,最后余数是1则是丑数,否则不是。
return num == 1;
}
丑数二
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 说明:
1 是丑数。 n 不超过1690。
暴力求解(超时)
class Solution {
public int nthUglyNumber(int n) {
for (int i = 1; ;i++) {
if (isUgly(i) && (--n == 0)) return i;
}
}
public boolean isUgly(int num) {
if (num < 1) return false;
while (num % 5 == 0) num /= 5;
while (num % 3 == 0) num /= 3;
while ((num & 1) == 0) num >>>= 1;
return num == 1;
}
}
三指针
由丑数的定义:一个只有质因数2,3,5的正整数。可以推出一个丑数乘以2、3 或 5 必然也是一个丑数。所有丑数(a > 1)可以分为ABC三个数组。
A: {1*2, 2*2, 3*2, 4*2, ...... ,(n-1)*2, n*2}
B: {1*3, 2*3, 3*3, 4*3, ...... ,(n-1)*3, n*3}
C:{1*5, 2*5, 3*5, 4*5, ...... ,(n-1)*5, n*5}
那么所有丑数就是以上三个数组去重合并后的结果。至此问题转换成了如何对三个有序数组去重合并元素?
合并有序数组一个比较好的办法是,声明 i, j, k 三个指针分别指向三个数组头地址,然和比较三个指针指向元素的大小关系,取最小值。计入丑数数组,相应的指针向前移动一位。考虑到出现多个相同最小元素的情况,如 A[3*2] == B[2*3] 多个相同最小值的指针都前进一位。因此代码实现中使用的是多个 if 而不是 if else。
这里的 A B C 数组实际就是已保存丑数数组的 *2 *3 *5。注意边界测试用例 n=1 也是丑数。
代码如下:
public int nthUglyNumber(int n) {
if (n < 1) return 0;
int[] ugly = new int[n]; ugly[0] = 1;
int i=0, j=0, k=0;
for (int idx=1; idx < n; idx++) {
int tmp = Math.min(ugly[i]*2, Math.min(ugly[j]*3, ugly[k]*5));
if (tmp == ugly[i]*2) i++;
if (tmp == ugly[j]*3) j++;
if (tmp == ugly[k]*5) k++;
ugly[idx] = tmp;
}
return ugly[n-1];
}
丑数三
请你帮忙设计一个程序,用来找出第 n 个丑数。
丑数是可以被 a 或 b 或 c 整除的 正整数。
示例 1:
输入:n = 3, a = 2, b = 3, c = 5 输出:4 解释:丑数序列为 2, 3, 4, 5, 6, 8, 9, 10... 其中第 3 个是 4。 示例 2:
输入:n = 4, a = 2, b = 3, c = 4 输出:6 解释:丑数序列为 2, 3, 4, 6, 8, 9, 10, 12... 其中第 4 个是 6。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ug… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
贴上大佬的题解 二分法思路剖析
class Solution {
public int nthUglyNumber(int n, int a, int b, int c) {
if (a == 1 || b == 1 || c == 1) return n;
// 两两组合的最小公倍数
long lcmAB = lcm(a, b);
long lcmAC = lcm(a, c);
long lcmBC = lcm(b, c);
// 三个的最小公倍数
long lcm = lcm(lcmAB, c);
int min = Math.min(c, Math.min(a, b));
long left = min, right = (long) Math.pow(min, n);
while (left <= right) {
long mid = left + (right - left) / 2;
long count =
(mid/a) + (mid/b) + (mid/c) - (mid/lcmAB) - (mid/lcmAC) - (mid/lcmBC) + (mid/lcm);
if (count == n) {
left = mid;
break;
} else if (count > n) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return (int) (left - Math.min(Math.min(left % a, left % b), left % c));
}
// 求最小公倍数
private long lcm(long a, long b) {
return a * b / gcd(a, b);
}
// 辗转相除法求最大公约数
private long gcd(long a, long b) {
// b 是余数,a 是除数
if (a == 0) return b;
return gcd(b % a, a);
}
}
超级丑数
编写一段程序来查找第 n 个超级丑数。
超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。
示例:
输入: n = 12, primes = [2,7,13,19] 输出: 32 解释: 给定长度为 4 的质数列表 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。 说明:
1 是任何给定 primes 的超级丑数。 给定 primes 中的数字以升序排列。 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000 。 第 n 个超级丑数确保在 32 位有符整数范围内。
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/su…
public int nthSuperUglyNumber(int n, int[] primes) {
// dp[i]代表:第i个丑数
int[] dp = new int[n]; dp[0] = 1;
// index[i]代表:primes[i]要和dp数组相乘的dp数组下标
int[] index = new int[primes.length];
for (int i = 1; i < n; i++) {
int min = Integer.MAX_VALUE;
for (int j = 0; j < index.length; j++) {
min = Math.min(min, (primes[j] * dp[index[j]]));
}
for (int j = 0; j < index.length; j++) {
// primes[j]*index[j]计算结果=本次循环最小值的index[j]++
if (min == (primes[j] * dp[index[j]])) {
index[j]++;
}
}
dp[i] = min;
}
return dp[n-1];
}