题目来源:https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/
大致题意:
给定一个递增的素数数组 arr 和一个整数 k,对于满足 0 <= i < j < arr.length 的 i 和 j,可以组成分数 arr[i] / arr[j]
求出能组成的分数中第 k 小的
思路
暴力快排
- 使用双层的 for 循环,将所有可能的分数存入集合中
- 自定义排序,因为是分数,排序时可以将其处理一下,防止精度问题
a1[0] / a1[1] - a2[0] / a2[1] ==> a1[0] * a2[1] - a1[1] * a2[0]
- 取出第 k-1 个元素即可
代码:
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
List<int[]> frac = new ArrayList<>();
int n = arr.length;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 加入集合
frac.add(new int[]{arr[i], arr[j]});
}
}
// 自定义排序
// a1[0] / a1[1] - a2[0] / a2[1] ==> a1[0] * a2[1] - a1[1] * a2[0]
Collections.sort(frac, (a1, a2) -> a1[0] * a2[1] - a1[1] * a2[0]);
return frac.get(k - 1);
}
优先队列
可知,对于满足 0 < j < arr.length 的 j,以 arr[j] 为分母的分数可以有 j 个,其中分子为:
arr[0]、arr[1]、… arr[j - 1]
可知分子是递增的,也就是说这样的分数是递增的
那么,所有满足上述条件的 j 一共有 arr.length - 1 个,也就能形成 arr.length - 1 组分母相等的分数
于是可以先将所有满足条件的 j 对应分组最小的分数加入优先队列,也就是分子为 arr[0],分母为 arr[j],然后每次取出队首分数 arr[x] / arr[y],若 x+1 < y,即对应分母这一组分数还有没放入到队列的,就将其放入。
这样取出 k-1 次后,队首元素就为第 k 小的分数
为了方便处理,队列中只存 arr 对应的索引,不过排序规则仍然是按照 arr 对应的数来,可看代码理解
代码:
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
int n = arr.length;
// 自定义排序,a1 a2 为对应分数的分子分母在 arr 中的索引
// a1[0] 即为分子索引,a1[1] 即为分母索引
// 排序时是按照分数的升序进行排序
PriorityQueue<int[]> queue = new PriorityQueue<>((a1, a2) -> arr[a1[0]] * arr[a2[1]] - arr[a1[1]] * arr[a2[0]]);
// 先放入 n-1 个分数
for (int i = 1; i < n; i++) {
queue.offer(new int[]{0, i});
}
// 取出 k-1 次当前队列中最小的分数
for (int i = 1; i < k; i++) {
int[] frac = queue.poll();
if (frac[0] + 1 < frac[1]) {
queue.offer(new int[]{frac[0] + 1, frac[1]});
}
}
return new int[]{arr[queue.peek()[0]], arr[queue.peek()[1]]};
}