面试题 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[i1]​​​ 表示第 i i i​​​ 个丑数,第 n n n​​​ 个丑数即为 numList [ n − 1 ] \\textit{numList}[n-1] numList[n1]​​​ 。

由于最小的丑数是 1 ,因此 numList [ 0 ] = 1 \\textit{numList}[0]=1 numList[0]=1 。那么考虑一下三个数列:

  1. numList[0]*3,numList[1]*3,numList[2]*3,numList[3]*3,……
  2. numList[0]*5,numList[1]*5,numList[2]*5,numList[3]*5,numList[4]*5,numList[5]*5……
  3. 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 1in​​ 时,令 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

参考

Krahets - 力扣(LeetCode) (leetcode-cn.com)

以上是关于面试题 17.09. 第 k 个数的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 面试题 17.09. 第 k 个数

⭐算法入门⭐《堆》中等01 —— LeetCode 面试题 17.09. 第 k 个数

面试题 17.09. 第 k 个数

03K个数或第k个数算法

03K个数或第k个数算法

LeetCode 面试题 17.09 第k个数[动态规划] HERODING的LeetCode之路