Write a program to find the nth super ugly number.
Super ugly numbers are positive numbers whose all prime factors are in the given prime listprimes of size k. For example,[1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers givenprimes = [2, 7, 13, 19] of size 4.
Note:
(1) 1 is a super ugly number for any givenprimes.
(2) The given numbers in primes are in ascending order.
(3) 0 < k ≤ 100, 0 <n ≤ 106, 0 <primes[i] < 1000.
题意:
写个程序找到第n个 super ugly number
super ugly number 就是一些只包含给定的k个质因子的正数
例如 [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] 就是12 个super ugly number构成的序列,它们由 size 为 4 的质数列表 [2, 7, 13, 19]生成
注:
1 是super ugly number,无论给定怎样的质数列表
给定的质数列表是升序的
0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000
思路:
就是给的size 为 4 的素数列表 [2, 7, 13, 19]
初始的super ugly number 是1
由1生成的 2,7,13,19: [1,2,7,13,19:]
继续由2及列表生成 [4,14,26,38]
由7及列表生成[14,49,91,133]
由13及列表生成[。。。]
由19及列表生成[。。。]
。。。
sort:以上生成的数字要保持由小到大的顺序---heapq.merge
duplicate:遇到重复的数字只要一个---if ugly != uglies[-1]
N:只返回第N个上述序列中的数字---while len(uglies) < n
参考:
http://segmentfault.com/a/1190000004187449
http://bookshadow.com/weblog/2015/12/03/leetcode-super-ugly-number/
这些数字是怎么产生的
除了第一个数字,其余所有数字都是之前已有数字乘以任意一个在质数数组里的质数
所以对于每一个已有的数字,我们都可以分别乘以所有在质数数组里的质数得到一系列的数字,这些数字肯定会存在在以后的序列中
由于我们是要得到从小到大的结果,所以我们可以维护个count数组
来记录对于对应质数下一个需要被乘的已有数的index
Python:
import heapq class Solution(object): def nthSuperUglyNumber(self, n, primes): """ :type n: int :type primes: List[int] :rtype: int """ uglies = [1] def gen(prime): for ugly in uglies: yield ugly * prime merged = heapq.merge(*map(gen, primes)) while len(uglies) < n: ugly = next(merged) if ugly != uglies[-1]: uglies.append(ugly) return uglies[-1] n=12 primes=[2,7,13,19] r=Solution().nthSuperUglyNumber(n, primes) print (r)
不会的地方:
yield:
关于Python中的yield
迭代器:
for循环可用于任何“可迭代对象”,这其实就是迭代器
Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration
迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开
使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。
生成器:
包含yield语句的函数会被特地编译成生成器。当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口
不像一般的函数会生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行和状态,他的本地变量将保存状态信息,这些信息在函数恢复时将再度有效
>>> def g(n): ... for iin range(n): ... yield i**2 ... >>> for iin g(5): ... print i,":", ... 0 : 1 :4 : 9 :16 : |
def gen(prime): for ugly in uglies: yield ugly*prime uglies=[1,2,7,13,19] prime=2 for i in gen(prime): print (i)
2
4
14
26
38
def fab(max):
a,b = 0,1
while a< max:
yield a
a, b = b, a+b
>>> for iin fab(20):
... print i,",",
...
0 , 1 ,1 , 2 ,3 , 5 ,8 , 13 ,
map(function, list):
Python中map()函数浅析
>>>defadd100(x):
... returnx+100
...
>>> hh =[11,22,33]
>>>map(add100,hh)
[111,122,133]
>>>defabc(a, b, c):
... returna*10000+ b*100+ c
...
>>> list1 =[11,22,33]
>>> list2 =[44,55,66]
>>> list3 =[77,88,99]
>>>map(abc,list1,list2,list3)
[114477,225588,336699]
>>> list1 =[11,22,33]
>>>map(None,list1)
[11,22,33]
>>> list1 =[11,22,33]
>>> list2 =[44,55,66]
>>> list3 =[77,88,99]
>>>map(None,list1,list2,list3)
[(11,44,77), (22,55,88), (33,66,99)]
[abc(a,b,c)
for
a
in
list1
for
b
in
list2
for
c
in
list3]
|
堆 结构:
纸上谈兵: 堆 (heap)
堆(heap)又被为优先队列(priority queue)
在堆中,我们不是按照元素进入队列的先后顺序取出元素的,而是按照元素的优先级取出元素
这就好像候机的时候,无论谁先到达候机厅,总是头等舱的乘客先登机,然后是商务舱的乘客,最后是经济舱的乘客。
每个乘客都有头等舱、商务舱、经济舱三种个键值(key)中的一个
Linux内核中的调度器(scheduler)会按照各个进程的优先级来安排CPU执行哪一个进程。
计算机中通常有多个进程,每个进程有不同的优先级(该优先级的计算会综合多个因素,
比如进程所需要耗费的时间,进程已经等待的时间,用户的优先级,用户设定的进程优先程度等等)。
内核会找到优先级最高的进程,并执行。
如果有优先级更高的进程被提交,那么调度器会转而安排该进程运行。优先级比较低的进程则会等待。“堆”是实现调度器的理想数据结构。
(Linux中可以使用nice命令来影响进程的优先级)
堆的实现
堆的一个经典的实现是完全二叉树(complete binary tree)。这样实现的堆成为二叉堆(binary heap)。
完全二叉树是增加了限定条件的二叉树。假设一个二叉树的深度为n。为了满足完全二叉树的要求,该二叉树的前n-1层必须填满,第n层也必须按照从左到右的顺序被填满
为了实现堆的操作,我们额外增加一个要求: 任意节点的优先级不小于它的子节点。如果在上图中,设定小的元素值享有高的优先级,那么上图就符合该要求。
堆的主要操作
是插入和删除最小元素(元素值本身为优先级键值,小元素享有高优先级)
操作之后,我们必须保持该实现应有的性质: 1. 完全二叉树 2. 每个节点值都小于或等于它的子节点。
插入操作的时候,会破坏上述堆的性质,所以需要进行名为percolate_up的操作,以进行恢复。
新插入的节点new放在完全二叉树最后的位置,再和父节点比较。
如果new节点比父节点小,那么交换两者。交换之后,继续和新的父节点比较……
直到new节点不比父节点小,或者new节点成为根节点。
这样得到的树,就恢复了堆的性质。
删除操作只能删除根节点。
根节点删除后,我们会有两个子树,我们需要基于它们重构堆。进行percolate_down的操作:
让最后一个节点last成为新的节点,从而构成一个新的二叉树。
再将last节点不断的和子节点比较。如果last节点比两个子节点中小的那一个大,则和该子节点交换。
直到last节点不大于任一子节点都小,或者last节点成为叶节点。
heapq.merge:
Python使用heapq实现小顶堆(TopK大)、大顶堆(BtmK小)
Python heapq模块
这个模块(build-in)实现了一个堆的数据结构,完美的解决了Top-K问题,以后解决Top-K问题的时候,直接把这个模块拿来用就可以了
注意,默认的heap是一个小顶堆!
heapq模块提供了如下几个函数:
heapq.heappush(heap, item) 把item添加到heap中(heap是一个列表)
heapq.heappop(heap) 把堆顶元素弹出,返回的就是堆顶
heapq.heappushpop(heap, item) 先把item加入到堆中,然后再pop,比heappush()再heappop()要快得多
heapq.heapreplace(heap, item) 先pop,然后再把item加入到堆中,比heappop()再heappush()要快得多
heapq.heapify(x) 将列表x进行堆调整,默认的是小顶堆
heapq.merge(*iterables) 将多个列表合并,并进行堆调整,返回的是合并后的列表的迭代器
heapq.nlargest(n, iterable, key=None) 返回最大的n个元素(Top-K问题)
heapq.nsmallest(n, iterable, key=None) 返回最小的n个元素(Top-K问题)
python中的堆排序peapq模块
>>> a=[2,4,6]
>>> b=[1,3,5]
>>> c=merge(a,b)
>>> list(c)
[1,2, 3,4, 5,6]
next:
next方法
>>> t = g(5)
>>> t.next()
0
>>> t.next()
1
>>> t.next()
4
>>> t.next()
9
>>> t.next()
16
>>> t.next()
Traceback (most recent call last):
File "<stdin>", line1, in<module>
StopIteration
ugly = next(merged)
没有找到想要的说明的文章,只能自己先写下笨笨的理解了:
当 primes 是 [2,7,13,19]时
uglies 是 [1]时
ugly = next(merged) 这条命令运行过程:gen 函数内 的 ugly 为 1,与 primes 里的每一个作用后,有个 yield 值,用 heapq.merge 排序后,next 取到 2
a。已经被计算过的 prime 和 ugly 就不会再一次被 yield,对于每个 prime ,会从上一次的 ugly 之后开始 yield
b。先由 p*u:7*2 生成的14,后由 p*u:2*7 生成的14,会先把 后生成的 14 放在 uglies
A:每生成 一个 yield 的值,就会把这之前 接下来最小的数 放在 uglies 里面
B:优先级:uglies 里最新放入的值 是由哪对 p*u 生成的,则下一个 yield 由 相应的 p 继续生成
A,B 反复进行下去