最后更新于
最后更新于
编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:
说明:
1 是丑数。
n 不超过1690。
对于很多限制了数值范围或数量大小的数学题目, 常常先将范围内所有的结果计算出来, 缓存起来, 之后对于每一个用例, 直接查询答案即可.
由于本题中指定查询的丑数数量不大于1690, 因此首先计算出来这1690个丑数是什么, 然后直接返回对应位置的数值即可.
从1
开始, 我们不断地乘上2
, 3
, 5
, 这样得到数, 它的因子中肯定只有2
, 3
, 5
这三个正整数, 剩下的就是控制顺序, 依次得到递增的丑数, 保证顺序的排列.
状态转移方程可以记为:
堆的方法与上面三指针的方法在思路上类似, 都是不断的乘上2
, 3
, 5
, 但控制下一个丑数的任务简化了, 由三指针交给了擅长存储和排序的堆这种数据结构. 将所有计算得到的丑数都放入堆中, 这里使用最小堆. 每次取堆顶的元素加入到丑数列表中, 这个数肯定是当前未加入到丑数列表中的最小数.
需要注意堆中数字重复的问题, 为此需要额外使用一个哈希表来存储已经在列表中的丑数.
本题的动态规划的形式有些特别, 可以把最终丑数的列表作为状态, 即第个丑数是什么. 状态转移方程也需要借助三指针才能给出.
使用三个指针, , , 指向已有的丑数列表中的位置, 该位置乘以这个指针代表的因子, 就得到了新的丑数. 对于新的丑数, 一定是前面比较小的丑数乘以三个因子得到的, 我们选取, , 中最小的丑数添加到丑数列表中, 作为新的丑数, 并将对应因子的指针前进一位.
可以看到和, 两个下标虽然作用于同一个数组, 但意义不同, 这点是需要注意的.
为什么新的丑数一定在, , 之中, 因为三个指针指示的位置之前的数字, 都分别已经和对应的因子乘过了, 得到的值已经被添加到丑数列表中了(每次循环只有被添加值对应因子的指针左移一位, 其他两个指针保持不变). 因此新的丑数一定在这三个中出现.