曾经绊倒我的 “超级丑数”

点击蓝色字体关注,欢迎星标此号


既然来了,何不认真读完此文呢?每天多花20分钟,做一些别人不愿做的事,坚持下去,会有一个结果的。废话少说,通过此文,你将会学到如下知识:

  1. 学会列表和排序很难求解的场景

  2. 学会使用堆的场景

  3. 学会一个使用堆的案例

  4. 进一步提高对内置模块heapq的使用能力

1 超级抽数

题目来自 https://leetcode-cn.com/problems/super-ugly-number/,阿里面试曾考过此题,大家务必重视此题。

首先要理解题目,我做此题时,读题好几遍,才完全明白超级丑数的定义。

给定一个质数列表primes,如果一个数的所有质数构成的列表是primes的子集,则此数为超级丑数。

因此,超级抽数依赖于给定的primes,要求求出第n个丑数。

示例

输入: 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] 。

列表和排序很难求解

先从暴力枚举开始分析,假定primes等于[2,5,13],依次列举出所有可能的丑数:1, 2, 4, 8, 16, 32, …. 糟糕!因为仅仅使用一个素数2,就能列举出很多。幸好此题限定一个丑数的上限,在32位有符整数范围内(最大值为: ),即便如此,穷举的情况依然非常复杂,更别提求解第n个丑数了!

的确,此题不太容易确定所有的丑数序列,完整的序列无法确定,排序也就无从谈起。因此,通过从小到大排序后找出第n个丑数的方法就不可行。

经验:对于无法提前预知整个列表,或者构建出整个序列耗费时间较长,或占用内存过大时,求第n个丑数,往往不太适合使用列表!

使用堆的场景

考虑使用堆,对应Python中heapq模块,它专治以上三种情况发生时,求解第n个丑数。

这道题使用heapq的求解思路如下:

  • step1 构建heapq,装入第一个元素,即素数1;

  • step2 移出heapq的根元素ugly,遍历primes拿出prime,同时与prime的元素相乘,得到一个新的丑数,并装入到heapq中。备注:Python中heapq是一个小根堆,也叫做优先级队列,在装入heapq中时,对象内部总会维护一个小根堆,所以每次pop时,都是当前heapq的最小值。

  • step3 利用上述特性,当移出n个元素时,实际上相当于从已排序好的列表中找到其第n个小的元素,这不就是丑数列表排序好后,第n个丑数吗!正是想要的结果第n个丑数。

需要注意,丑数装入heapq时,不能出现重复。解决起来也很方便,使用集合set防止重复添加。

代码

将上述思路兑现为代码:

class Solution(object):
    def nthSuperUglyNumber(self, n, primes):
        """
        :type n: int
        :type primes: List[int]
        :rtype: int
        """
        nums, i, s = [], 0, set()
        heapify(nums)
        heappush(nums, 1) # step1
        ugly = 0
        for _ in range(n): # step2
            ugly = heappop(nums)
            for prime in primes:
                uglyCombine = ugly * prime 
                if uglyCombine not in s: # step3
                    s.add(uglyCombine)
                    heappush(nums, uglyCombine)
        return ugly

时间复杂度等于 O(knlogn),k为primes长度,n为第几个丑数; 空间复杂度为O(n).

每天下班9点多到家,然后电脑前一顿狂刷,已快3年,有苦有甜,更多是一种习惯。留言告诉我,你大概什么时候关注我的,有没有一直关注我快3年的铁粉,有奖品要发。

看完这篇不过瘾,关注刷题日记,立即领取665题分析.pdf:

长按关注,领取665题.pdf

为我点个赞

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值