一个已排序好的表 A,其包含 1 和其他一些素数. 当列表中的每一个 p<q 时,我们可以构造一个分数 p/q 。
那么第 k 个最小的分数是多少呢? 以整数数组的形式返回你的答案, 这里 answer[0] = p 且 answer[1] = q.
示例:
输入: A = [1, 2, 3, 5], K = 3
输出: [2, 5]
解释:
已构造好的分数,排序后如下所示:
1/5, 1/3, 2/5, 1/2, 3/5, 2/3.
很明显第三个最小的分数是 2/5.
输入: A = [1, 7], K = 1
输出: [1, 7]
注意:
A 的取值范围在 2 — 2000.
每个 A[i] 的值在 1 —30000.
K 取值范围为 1 —A.length * (A.length - 1) / 2
方法一:二分答案,我们通过枚举第k个分母应该是多少进行二分,知道找到该答案。
class Solution {
public int[] kthSmallestPrimeFraction(int[] A, int K) {
double l=0,r=1;
int[] ans= new int[]{0,1};
while(r-l>1e-9) {
double mid=l+(r-l)/2.0;
int[] res=work(mid,A);
if(res[0]<K) l=mid;
else {
ans[0]=res[1];
ans[1]=res[2];
r=mid;
}
}
return ans;
}
private int[] work(double x,int[] A) {
int numer=0,denom=1,count=0,i=-1;
for(int j=1;j<A.length;j++) {
while(A[i+1]<A[j]*x) i++;
count+=i+1;
if(i>=0 && numer*A[j]<denom*A[i]) {
numer=A[i];
denom=A[j];
}
}
return new int[] {count,numer,denom};
}
}
方法二:堆,使用一个堆记录所有以 A[j]
为分母且未被弹出的最小分数。依次从堆中弹出 K-1
个元素,此时堆顶的分数就是结果。由于K有n*(n-1)/2这么大,因此方法二的复杂度相对于方法一要高很多。
class Solution {
public int[] kthSmallestPrimeFraction(int[] A, int K) {
PriorityQueue<int[]> q=new PriorityQueue<int[]>((a,b)->A[a[0]]*A[b[1]]-A[a[1]]*A[b[0]]);
for(int i=1;i<A.length;i++)
q.add(new int[]{0,i});
while(--K>0) {
int[] res=q.poll();
if(res[0]++<res[1])
q.add(res);
}
int[] ans=q.poll();
return new int[] {A[ans[0]],A[ans[1]]};
}
}