面试题 17.09. 第 k 个数
Posted 炫云云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题 17.09. 第 k 个数相关的知识,希望对你有一定的参考价值。
面试题 17.09. 第 k 个数
有些数的素因子只有 3,5,7,请设计一个算法找出第 k 个数。注意,不是必须有这些素因子,而是必须不包含其他的素因子。例如,前几个数按顺序应该是 1,3,5,7,9,15,21。
示例 1:
输入: k = 5
输出: 9
动态规划
不难发现,一个丑数总是由前面的某一个丑数 x3 / x5 / x7 得到。
反过来说也是一样的,一个丑数 x3 / x5 / x7 就会得到某一个更大的丑数。
定义数组 numList \\textit{numList} numList ,其中 numList [ i − 1 ] \\textit{numList}[i-1] numList[i−1] 表示第 i i i 个丑数,第 n n n 个丑数即为 numList [ n − 1 ] \\textit{numList}[n-1] numList[n−1] 。
由于最小的丑数是 1 ,因此 numList [ 0 ] = 1 \\textit{numList}[0]=1 numList[0]=1 。那么考虑一下三个数列:
numList[0]*3,numList[1]*3,numList[2]*3,numList[3]*3,……
numList[0]*5,numList[1]*5,numList[2]*5,numList[3]*5,numList[4]*5,numList[5]*5……
numList[0]*7,numList[1]*7,numList[2]*7,numList[3]*7,numList[4]*7,numList[5]*7……
上面这个三个数列合在一起就形成了新的、更长的丑数数列。
如果合在一起呢?这其实就是一个合并有序线性表的问题。
定义三个p分别指向上面三个数列,下一个丑数一定是三个 p代表的值中最小的那个。然后相应 p +=1 即可。
如何得到其余的丑数呢?定义三个指针
p
3
,
p
5
,
p
7
p_3,p_5,p_7
p3,p5,p7 ,表示下一个丑数是当前指针指向的丑数乘以对应的质因数。初始值 numList[0]=1; p3=0; p5=0; p7=0
numList[1]=Min(numList[p1]*3,numList[p5]*5,numList[p7]*7)
=Min(1*3,1*5,1*7)
=3
于是 p3+=1;
numList[2]=Min(numList[p1]*3,numList[p5]*5,numList[p7]*7)
=Min(3*3,1*5,1*7)
=5
于是 p5+=1;
当 1 ≤ i ≤ n 1 \\le i \\le n 1≤i≤n 时,令 dp [ i ] = min ( numList [ p 7 ] × 7 , numList [ p 3 ] × 3 , numList [ p 5 ] × 5 ) \\textit{dp}[i]=\\min(\\textit{numList}[p_7] \\times 7, \\textit{numList}[p_3] \\times 3, \\textit{numList}[p_5] \\times 5) dp[i]=min(numList[p7]×7,numList[p3]×3,numList[p5]×5) ,然后分别比较 numList [ i ] 和 numList [ p 7 ] , numList [ p 3 ] , numList [ p 5 ] \\textit{numList}[i] 和 \\textit{numList}[p_7],\\textit{numList}[p_3],\\textit{numList}[p_5] numList[i]和numList[p7],numList[p3],numList[p5] 是否相等,如果相等则将对应的指针加 1 。
class Solution:
def getKthMagicNumber(self, k: int) -> int:
numList = [1] *k
p3=0;p5=0;p7=0
for i in range(1,k):
numList[i] = min(min(numList[p3]*3,numList[p5]*5), numList[p7]*7)
if numList[i]==numList[p3]*3:
p3+=1
if numList[i]==numList[p5]*5:
p5+=1
if numList[i]==numList[p7]*7:
p7+=1
return numList[-1]
最小堆
此题为简单堆的应用,堆的基础知识如下:
堆(Heap)是一个可以被看成近似完全二叉树的数组。
堆包括最大堆和最小堆:最大堆的每一个节点(除了根结点)的值不大于其父节点;
最小堆的每一个节点(除了根结点)的值不小于其父节点。
堆常见的操作及python实现如下:
# HEAPPUSH:把一个数值放进已经是堆结构的数组中,并保持堆结构,时间复杂度为O(log n)。
data = [1,5,3,2,8,5]
heap = []
for n in data:
heapq.heappush(heap, n)
#HEAPIFY 建堆:把一个乱序的数组变成堆结构的数组,时间复杂度为 O(n)。
data = [-1,5,3,2,8,5]
heapq.heapify(data)
#HEAPPOP:从最大堆中取出最大值或从最小堆中取出最小值,并将剩余的数组保持堆结构,
#时间复杂度为O(log n)
print(heapq.heappop(data))
# HEAPREPLACE=HEAPPOP+HEAPPUSH
heapq.heapreplace(data, -8)
要得到从小到大的第 n个丑数,可以使用最小堆实现。
初始时堆为空。首先将最小的丑数 1 加入堆。
每次取出堆顶元素 x x x,则 x x x 是堆中最小的丑数,由于 3 x , 5 x , 7 x 3x, 5x, 7x 3x,5x,7x 也是丑数,因此将 3 x , 5 x , 7 x 3x, 5x, 7x 3x,5x,7x 加入堆。
上述做法会导致堆中出现重复元素的情况。为了避免重复元素,可以使用哈希集合去重,避免相同元素多次加入堆。
在排除重复元素的情况下,第 n n n 次从最小堆中取出的元素即为第 n n n 个丑数。
class Solution:
def getKthMagicNumber(self, k):
# 使用小顶堆处理(因为每次需要得到最小的数字)
heap = [1] # 第一个元素是1
for i in range(k):
res = heapq.heappop(heap) # 每次取出堆中的首元素(最小元素)
while heap and res == heap[0]: # 忽略重复的元素
heapq.heappop(heap)
heapq.heappush(heap, res * 3) # 依次将当前元素乘以3,5,7的值压入堆中
heapq.heappush(heap, res * 5)
heapq.heappush(heap, res * 7)
return res
参考
以上是关于面试题 17.09. 第 k 个数的主要内容,如果未能解决你的问题,请参考以下文章