leetcode 786. K-th Smallest Prime Fraction
题目描述
A sorted list A
contains 1, plus some number of primes. Then, for every p < q
in the list, we consider the fraction p/q
.
What is the K-th smallest fraction considered? Return your answer as an array of ints, where answer[0] = p
and answer[1] = q
.
Note:
A
will have length between2
and2000
.- Each
A[i]
will be between1
and30000
. K
will be between1
andA.length * (A.length - 1) / 2
.
Difficulty: hard
786. K-th Smallest Prime Fraction
中文描述
给你一个包含1在内的,排好序的素数列表,找出第K
小的分数p/q
,p < q
的[p, q]
输入格式
输入一个列表A
,列表素数的集合。K
表示需要第k
小的。
Examples:
Input: A = [1, 2, 3, 5], K = 3
Output: [2, 5]
解释:
由A组成的分数排序:1/5, 1/3, 2/5, 1/2, 3/5, 2/3.
第3小的是 2/5.A = [1, 7], K = 1
Output: [1, 7]
解释:
由A组成的分数排序:1/7.
第1小的是 1/7.
解答思路
解法一:运用heapq
1.把A的元素按顺序记为 p1,p2,p3,⋅⋅⋅,pn (A中有 n 个元素)。
2.heapq先添加
p1pn,p2pn,p3pn,⋅⋅⋅,pn−1pn 。3.每次从heapq弹出最小的元素,比如第一次为 p1pn ,然后heapq加入 p1pn−1 (加入 pipj−1 时需要 i>j−1 ,)。
4.重复K次步骤3,得到最后结果。
5.复杂度估计 O(Klog(n))
解法二:二分查找
1.把A的元素按顺序记为 p1,p2,p3,⋅⋅⋅,pn (A中有 n 个元素)。
2.取
m 在[0, 1]取中值, s=∑i=1n−1n−argmaxj{pj≤pim} 表示了前 s 个分数小于m .3.对 m 进行二分查找。
4.如果
s=K , argmaxi{pipj|j=argmaxj{pj≤pim}} ,此时的 [i,j] 就是最后答案。5.复杂度估计 O(αnlog(n)),α 为二分查找的次数
代码
解法一,速度超慢= =,
class Solution(object):
def kthSmallestPrimeFraction(self, A, K):
"""
:type A: List[int]
:type K: int
:rtype: List[int]
12785 ms
"""
import heapq
l = len(A) - 1
h = []
for i in range(l):
# 先往把每个元素的最小值加入heapq中,heapq中的每个元素第一位表示p/q的值,第二个表示p在A中的索引,第三位表示q在A中的索引
heapq.heappush(h, (A[i] / float(A[l]), i, l))
# 一个个查找
while K > 0:
# 输出最小的一个
min_one = heapq.heappop(h)
# 添加保持min_one的p不变,q选择的索引 + 1,并且保证p<q
if min_one[2] - 1 > min_one[1]:
new_l = min_one[2] - 1
heapq.heappush(h, (A[min_one[1]] / float(A[new_l]), min_one[1], new_l))
K -= 1
return [A[min_one[1]], A[min_one[2]]]
解法二,参考了其它人写的代码详细请见Python solution using Binary Search,速度快了 n 倍:
class Solution(object):
def kthSmallestPrimeFraction(self, A, K):
"""
:type A: List[int]
:type K: int
:rtype: List[int]
440ms 只用于python3
"""
import bisect
l, r, N = 0, 1, len(A)
while True:
m = (l + r) / 2
# A[i] / A[j] = m --> A[i] / m = A[j],所以通过每个A[i] / m 在通过二分查找可以找比A[j]小的个数
border = [bisect.bisect(A, A[i] / m) for i in range(N)]
# 然后我们用 N-比A[j]小的个数 就是所有不小于A[j]的个数,然后对所有A[j]求和
cur = sum(N - i for i in border)
if cur > K:
r = m
elif cur < K:
l = m
else:
# 找到这个位置之后,把所有可能最大的那个找出来就是最后答案
return max([(A[i], A[j]) for i, j in enumerate(border) if j < N], key=lambda x: x[0] / x[1])