团灭丑数问题
Contents
什么是丑数?
先看一下百度百科的解释:
说法一(ugly number):把只包含质因子2,3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但7、14不是,因为它们包含质因子7。 习惯上我们把1当做是第一个丑数。
说法二(humble number):对于一给定的素数集合 S = {p1, p2, …, pK},考虑一个正整数集合,该集合中任一元素的质因数全部属于S。这个正整数集合包括,p1、p1p2、p1p1、p1p2p3…(还有其它)。该集合被称为S集合的“丑数集合”。
相关题目
leetcode上丑数相关题目有四个, 这篇文章来解决并总结这四道题目的解法.
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) 链接:https://leetcode-cn.com/problems/ugly-number 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
思路一:
- 判断一个数字是不是丑数, 从头到位计算每一位比当前数字小或者相等的丑数
- 再计算下一个丑数,如果下一个丑数刚好 == 传入数字。 则是丑数
- 否则不是丑数
|
|
此解法,因为n 的 取值范围**[−231, 231 − 1]**, 当n过大时, 额外申请的数组的存储空间会很大,超出内存限制。
时间复杂度 : O(N)
空间复杂度 : O(1)
思路二:
-
如果是丑数, 则num / 2i /3j / 5k 最终为一
-
如果余数 != 0, 则代表除不尽, 退出循环
-
继续除下一个
-
一直除到5, 如果最终数为1,则为丑数, 否则不是丑数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* * * 思路二: * 如果是丑数, 则 除2 , 除3, 除5 最终为1。 * 如果 余数 != 0, 代表除不尽, 退出循环 * 继续除下一个 * 一直除到5,如果最终数为1, 则为丑数,否则不是丑数 * */ public static boolean isUgly1(int num) { if (num <= 0) return false; while (num % 2 == 0) num /= 2; if (num == 1) return true; while (num % 3 == 0) num /= 3; if (num == 1) return true; while (num % 5 == 0) num /= 5; if (num == 1) return true; return false; }
思路三:
递归
|
|
264. 丑数 II
编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:
输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 说明:
1 是丑数。 n 不超过1690。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/ugly-number-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
思路一:
- 利用最小堆 (Java中的优先级队列)
- 利用优先级队列自动排序的功能
- 每次取出对头元素,存入对头元素 * 2, 对头元素 * 3, 对头元素 * 5
- 但注意,像12这个元素,可以是 4 * 3得到,也可以是 2 * 6得到,所以注意去重
|
|
思路二:
对思路一的优化, 用set去重复
|
|
思路三:
- 动态规划,三指针法
- 起初三个指针都指向数组0的位置
- 当数组的下一个是 *2 得来时, p2 ++, *3得来时,p3++, *5得来时,p5++
- 注意去重, 有可能下一个数字即是 * 2得来,又是 * 3得来时,p2 和 p3都需要 ++
|
|
1201. 丑数 III
请你帮忙设计一个程序,用来找出第 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, 12… 其中第 4 个是 6。 示例 3:
输入:n = 5, a = 2, b = 11, c = 13 输出:10 解释:丑数序列为 2, 4, 6, 8, 10, 11, 12, 13… 其中第 5 个是 10。 示例 4:
输入:n = 1000000000, a = 2, b = 217983653, c = 336916467 输出:1999999984
提示:
1 <= n, a, b, c <= 10^9 1 <= a * b * c <= 10^18 本题结果在 [1, 2 * 10^9] 的范围内
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/ugly-number-iii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
拿到题目只想到, 递推去算下一个丑数,算到末尾,看是否N是不是丑数。
算了半天,还超时了。 想不到思路,去查看题解中思路。
以下是 题解区域大神思路
基础思路
首先,为什么第一时间能想到二分法?
让我们观察题目,可以看到,最终状态(即n)的范围非常大。试图自底向上递推或是按照通常的自顶向下回溯显然会超时(比如动态规划、DFS等方法)
面对这么大的状态空间,二分法的时间复杂度是logN,因此能够大大压缩需要遍历的状态数目
思路剖析
既然已经确定了二分法作为切入点,关键问题来了,如何二分呢?
按照题意,所谓丑数是可以至少被a、b、c三者中的一者整除的,那么对于一个丑数X,我们能够确定它是第几个丑数吗?
–答案显然是可以的,我们只需要计算X中包含了多少个丑数因子即可。
即只需要知道在[0,X]范围内,还有多少个丑数即可,而这些丑数,无非就是一些能被a或者b或者c所整除的数。
那么显然,我们直接用X/a、X/b、X/c就能计算出[0,X]范围内有多少数能被a或者b或者c整除,然后把它们加起来就是答案!
但是仔细思考一下,我们是不是重复计算了些什么?如果一个数既能被a整除,又能被b整除,那么实际上该数在先前的计算中就被重复计算了一次(分别是在计算X/a和X/b时)。
–好吧,让我们思考所有可能的情况
1.该数只能被a整除 (该数一定是a 的整数倍)
2.该数只能被b整除 (该数一定是b 的整数倍)
3.该数只能被c整除 (该数一定是c 的整数倍)
4.该数只能被a和b同时整除 (该数一定是a、b最小公倍数的整数倍)
5.该数只能被a和c同时整除 (该数一定是a、c最小公倍数的整数倍)
6.该数只能被b和c同时整除 (该数一定是b、c最小公倍数的整数倍)
7.该数只能被a和b和c同时整除(该数一定是a、b、c的最小公倍数的整数倍)
所以,我们只需要分别计算以上七项就能得到结果了!让我们分别来看(用MCM+下标表示最小公倍数):
情况1 = X/a - 情况4 - 情况5 - 情况7 情况2 = X/b - 情况4 - 情况6 - 情况7 情况3 = X/c - 情况5 - 情况6 - 情况7 情况4 = X/MCM_a_b - 情况7 情况5 = X/MCM_a_c - 情况7 情况6 = X/MCM_b_c - 情况7 情况7 = X/MCM_a_b_c
让我们整理上述方程后也就得到:
sum(情况) = X/a + X/b + X/c - X/MCM_a_b - X/MCM_a_c - X/MCM_b_c + X/MCM_a_b_c
好了,现在也就得到了计算X中包含多少个丑数因子的方法了!
至于计算最小公倍数的方法,这里不多介绍,概括而言就是对于两个数a和b,它们的最小公倍数 = a*b/(a和b的最大公约数),最大公约数可以通过辗转相除法得到
二分搜索
在得到了计算任意数中包含了多少个丑数因子的方法后,我们实际上只需要通过二分法,不断缩小边界范围,直到某个位置所对应的数恰好包含了n个丑数因子为止。
注意,通过二分法计算的答案并非是最终答案,因为可以有很多数同时包含有n个丑数因子!
比如第n个丑数是X,那么[X,X + min(a,b,c))这个半开区间内的所有数都同时包含n个丑数因子,我们通过二分法得到的答案也随机分布于这个区间中。而实际上我们只需要得到该区间的左端即可。处理方法很简单:假设我们得到的临时答案是K(K∈[X,X + min(a,b,c))),那么K - min(K%a,K%b,K%c) = X.也就是只需要把临时答案减去其与a、b、c三者中取余的最小值即可!
作者:Alfeim 链接:https://leetcode-cn.com/problems/ugly-number-iii/solution/er-fen-fa-si-lu-pou-xi-by-alfeim/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
|
|
313. 超级丑数
编写一段程序来查找第 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) 链接:https://leetcode-cn.com/problems/super-ugly-number 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
思路一:
动态规划
- 此题目与 264 不一样的地方,在于此题质数在数组中,不确定。而264质数就是2 3 5三个
- 使用动态规划, 264三个指针足以。 而此题需要指数数组中元素个数数量的指针
- 所以除了使用 dp 数组以外,还需要一个 pointers 数组,用来存放与质数数组对应的位置指针指向的位置
- 代码思路,跟264 基本类似
- 依然需要注意, 同一个丑数,可能同时由两个质数 * 其dp对应位置得出, 所以需要去重!
代码如下:
|
|
Author 飞熊
LastMod May 30