[每日一题]264. Ugly Number II
求第n个丑数: 动态规划和堆。...
题目描述
Write a program to find the n-th ugly number.
Ugly numbers are positive numbers whose prime factors only include
2, 3, 5. For example,
1, 2, 3, 4, 5, 6, 8, 9, 10, 12is the sequence of the first
10ugly numbers.
Note that
1is typically treated as an ugly number, and n does not exceed 1690.
题目分析
本题是求“丑数”的第二道题,难度中等。其相关的第一道题是丑数判断,也是我们昨天做的题目,难度是简单。所谓丑数,就是不能被2,3,5以外的其他素数整除的数。1,2,3,4,5,6,8,9,10,12,15,16,18是最前面的13个丑数。
丑数的判断比较简单,就是去因子法:不断的除以2、3、5,如果最后的结果等于 1, 那么这个数就叫做丑数。本题是求第 n 个丑数,这次一共提交了4个方案:暴力法、动态规划、堆和数学方法。
解题方案
暴力法暴力法是最容易想到的方法:从 1 开始,不停的递增。判断当前的数是否为丑数:如果是则 n - 1 ;如果不是则循环到下一位。返回 n = 1 的丑数即可。
暴力法会超时,因为有很多重复计算。在判断 8 的时候,我们需要不停的除以 2、3、5。 其实我们不需要计算这么多步骤,因为 4 是丑数, 并且 4 * 2 = 8 , 所以 8 也一定是丑数。
动态规划
本题的动态规划算法有点复杂:因为第 n 个丑数不是与 第 n - 1 个丑数有关,而是与 前 n - 1 中的某三个丑数相关。
根据丑数的定义,我们可以得出一个结论:除 1 以外,其余的丑数一定是 2、3 或者 5 的倍数。也可以这么说,第 n 个丑数一定大于第 n - 1 个丑数,并且等于 前 n - 1 个丑数分别乘以 2、3 和 5 的最小值。
举个例子,假设我们求的是第 10 个丑数的值。那么前 9 个丑数分别是 1, 2, 3, 4, 5, 6, 8, 9,10。
那么第 10 个丑数一定是如下集合中大于 10 的最小值:
- 乘以 2 的集合: 12 ( 6 x 2 = 12), 16 ,18, 20
- 乘以 3 的集合:12 ( 4 x 3 = 12), 15, 18 ...
- 乘以 5 的集合:15 ( 3 x 5 = 15), 20, 25 ...
上面的例子,我们其实不需要从 1 开始计算 x 2 的值,我们只需要记录上一个2的倍数 的位置就行了。因为当前的丑数,一定是大于 上一个2的倍数乘以 2。 很容易证明:假设当前的丑数小于上一个2的倍数 乘以 2 , 那么根据我们的推论,上次应该选择这个丑数。这与假设矛盾。同理可以证明3和5 也只需要保存上一次的倍数的坐标。
咳咳,是真的有点绕,我就是想表达的更清楚一些,说的有点多。这个方法有点像我们以前总结的用数组记录位置的方法:数组元素保存的是上一次操作的位置,这样下次操作的时候,只需要取下一个位置就行。
如果由伙伴有更清晰的解答和描述麻烦评论留言,谢谢。
描述一下动态规范算法的思路:
- 新建一个Set ,用于存入丑数 [list=1]
- 此处如果用数组,需要考虑去重的问题。由上面的例子可以看出 12 是 2, 3的倍数,在计算的过程中会出现两次。
[/*][*]从 丑数 1 开始计算
[/*]
- 获取 p2 * 2 , p3 * 3, p5 * 5 中的最小值作为当前丑数
- 如果当前的丑数是 2 的倍数,则 p2 +1
- 如果当前的丑数是 3 的倍数,则 p3 +1
- 如果当前的丑数是 5 的倍数,则 p5 +1
[/*][/list]时间复杂度和空间复杂度分别等于 O(N)。
堆
思路是利用最小堆的特性,获取当前最小的丑数,分别乘以 2、3、5,这样可以保证不会遗漏任何丑数。证明: 假设存在一个数是丑数,但是不是前面如何一个数乘以 2、3或者5得到的。那么,这个数于丑数的定义矛盾。所以,不可能存在这样的丑数。
思路:
- 新建一个最小堆,并将1存入最小堆
- pop 堆中的最小值, 分别乘以 2、3、5, 并存入 heap 中,并且 n - 1
- 循环步骤 2
- 返回当 n = 0 时的弹出的最小值
注意:这个方法需要考虑数据溢出的问题,所以需要先将数转换成 Long ,最后在转换会 Integer 。
该方案由 4群的 @不染 提供, 非常感谢!
他的代码用了比较多的技巧:heap 取最小值,Hash 去重,Array 存结果。
@vic 的代码更简洁,清晰一些。
数学
最佳提交
说明:空间复杂度应该是 O(N);在 if 判断的时候,做了去重判断,所以,不需要考虑重复的问题了。
加入我们
后台回复“我要刷题”,跟我们一起每天学习算法!附录
Java5群 @JOKER 的提交
C++
Python
Swift
Ruby
C语言
关注 一个技术人员的胡言乱语
微信扫一扫关注公众号