加载中...
786-第 K 个最小的素数分数(K-th Smallest Prime Fraction)
发表于:2021-12-03 | 分类: 困难
字数统计: 3.7k | 阅读时长: 15分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/k-th-smallest-prime-fraction

英文原文

You are given a sorted integer array arr containing 1 and prime numbers, where all the integers of arr are unique. You are also given an integer k.

For every i and j where 0 <= i < j < arr.length, we consider the fraction arr[i] / arr[j].

Return the kth smallest fraction considered. Return your answer as an array of integers of size 2, where answer[0] == arr[i] and answer[1] == arr[j].

 

Example 1:

Input: arr = [1,2,3,5], k = 3
Output: [2,5]
Explanation: The fractions to be considered in sorted order are:
1/5, 1/3, 2/5, 1/2, 3/5, and 2/3.
The third fraction is 2/5.

Example 2:

Input: arr = [1,7], k = 1
Output: [1,7]

 

Constraints:

  • 2 <= arr.length <= 1000
  • 1 <= arr[i] <= 3 * 104
  • arr[0] == 1
  • arr[i] is a prime number for i > 0.
  • All the numbers of arr are unique and sorted in strictly increasing order.
  • 1 <= k <= arr.length * (arr.length - 1) / 2

中文题目

给你一个按递增顺序排序的数组 arr 和一个整数 k 。数组 arr1 和若干 素数  组成,且其中所有整数互不相同。

对于每对满足 0 <= i < j < arr.lengthij ,可以得到分数 arr[i] / arr[j]

那么第 k 个最小的分数是多少呢?  以长度为 2 的整数数组返回你的答案, 这里 answer[0] == arr[i] 且 answer[1] == arr[j]

 

示例 1:

输入:arr = [1,2,3,5], k = 3
输出:[2,5]
解释:已构造好的分数,排序后如下所示: 
1/5, 1/3, 2/5, 1/2, 3/5, 2/3
很明显第三个最小的分数是 2/5

示例 2:

输入:arr = [1,7], k = 1
输出:[1,7]

 

提示:

  • 2 <= arr.length <= 1000
  • 1 <= arr[i] <= 3 * 104
  • arr[0] == 1
  • arr[i] 是一个 素数i > 0
  • arr 中的所有数字 互不相同 ,且按 严格递增 排序
  • 1 <= k <= arr.length * (arr.length - 1) / 2

通过代码

高赞题解

优先队列(堆)

数据范围只有 $10^3$,直接扫描所有点对的计算量不超过 $10^6$。

因此我们可以使用「扫描点对」+「优先队列(堆)」的做法,使用二元组 $(arr[i], arr[j])$ 进行存储,构建大小为 $k$ 的大根堆。

根据「堆内元素多少」和「当前计算值与堆顶元素的大小关系」决定入堆行为:

  • 若堆内元素不足 $k$ 个,直接将当前二元组进行入堆;
  • 若堆内元素已达 $k$ 个,根据「当前计算值 $\frac{arr[i]}{arr[j]}$ 与堆顶元素 $\frac{peek[0]}{peek[1]}$ 的大小关系」进行分情况讨论:
    • 如果当前计算值比堆顶元素大,那么当前元素不可能是第 $k$ 小的值,直接丢弃;
    • 如果当前计算值比堆顶元素小,那么堆顶元素不可能是第 $k$ 小的值,使用当前计算值置换掉堆顶元素。

代码:

[]
class Solution { public int[] kthSmallestPrimeFraction(int[] arr, int k) { int n = arr.length; PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->Double.compare(b[0]*1.0/b[1],a[0]*1.0/a[1])); for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { double t = arr[i] * 1.0 / arr[j]; if (q.size() < k || q.peek()[0] * 1.0 / q.peek()[1] > t) { if (q.size() == k) q.poll(); q.add(new int[]{arr[i], arr[j]}); } } } return q.poll(); } }
  • 时间复杂度:扫描所有的点对复杂度为 $O(n^2)$;将二元组入堆和出堆的复杂度为 $O(\log{k})$。整体复杂度为 $O(n^2 * \log{k})$
  • 空间复杂度:$O(k)$

多路归并

在解法一中,我们没有利用「数组内元素严格单调递增」的特性。

由于题目规定所有的点对 $(i, j)$ 必须满足 $i < j$,即给定 $arr[j]$ 后,其所能构建的分数个数为 $j$ 个,而这 $j$ 个分数值满足严格单调递增:$\frac{arr[0]}{arr[j]} < \frac{arr[1]}{arr[j]} < \frac{arr[2]}{arr[j]} < … < \frac{arr[j - 1]}{arr[j]}$。

问题等价于我们从 $n - 1$ 个(下标 $0$ 作为分母的话,不存在任何分数)有序序列中找到第 $k$ 小的数值。这 $n - 1$ 个序列分别为:

  • $[\frac{arr[0]}{arr[1]}]$
  • $[\frac{arr[0]}{arr[2]}, \frac{arr[1]}{arr[2]}]$
  • $[\frac{arr[0]}{arr[3]}, \frac{arr[1]}{arr[3]}, \frac{arr[2]}{arr[3]}]$
  • $[\frac{arr[0]}{arr[j]}, \frac{arr[1]}{arr[j]}, \frac{arr[2]}{arr[j]}, … , \frac{arr[j - 1]}{arr[j]}]$

问题彻底切换为「多路归并」问题,我们使用「优先队列(堆)」来维护多个有序序列的当前头部的最小值即可。

代码:

[]
class Solution { public int[] kthSmallestPrimeFraction(int[] arr, int k) { int n = arr.length; PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->{ double i1 = arr[a[0]] * 1.0 / arr[a[1]], i2 = arr[b[0]] * 1.0 / arr[b[1]]; return Double.compare(i1, i2); }); for (int i = 1; i < n; i++) q.add(new int[]{0, i}); while (k-- > 1) { int[] poll = q.poll(); int i = poll[0], j = poll[1]; if (i + 1 < j) q.add(new int[]{i + 1, j}); } int[] poll = q.poll(); return new int[]{arr[poll[0]], arr[poll[1]]}; } }
  • 时间复杂度:起始将 $n - 1$ 个序列的头部元素放入堆中,复杂度为 $O(n\log{n})$;然后重复 $k$ 次操作得到第 $k$ 小的值,复杂度为 $O(k\log{n})$。整体复杂度为 $O(\max(n, k) * \log{n})$
  • 空间复杂度:$O(n)$

二分 + 双指针

进一步,利用 $arr$ 递增,且每个点对 $(i, j)$ 满足 $i < j$,我们可以确定 $(i, j)$ 对应的分数 $\frac{arr[i]}{arr[j]}$ 必然落在 $[0, 1]$ 范围内。

假设最终答案 $\frac{arr[i]}{arr[j]}$ 为 $x$,那么以 $x$ 为分割点的数轴(该数轴上的点为 $arr$ 所能构造的分数值)上具有「二段性」:

  • 小于等于 $x$ 的值满足:其左边分数值个数小于 $k$ 个;
  • 大于 $x$ 的值不满足:其左边分数值个数小于 $k$ 个(即至少有 $k$ 个)。

而当确定 $arr[j]$ 时,利用 $arr$ 有序,我们可以通过「双指针」快速得知,满足 $\frac{arr[i]}{arr[j]} <= x$ 的分子位置在哪(找到最近一个满足 $\frac{arr[i]}{arr[j]} > x$ 的位置)。

另外,我们可以在每次 check 的同时,记录下相应的 $arr[i]$ 和 $arr[j]$。

代码:

[]
class Solution { double eps = 1e-8; int[] arr; int n, a, b; public int[] kthSmallestPrimeFraction(int[] _arr, int k) { arr = _arr; n = arr.length; double l = 0, r = 1; while (r - l > eps) { double mid = (l + r) / 2; if (check(mid) >= k) r = mid; else l = mid; } return new int[]{a, b}; } int check(double x){ int ans = 0; for (int i = 0, j = 1; j < n; j++) { while (arr[i + 1] * 1.0 / arr[j] <= x) i++; if (arr[i] * 1.0 / arr[j] <= x) ans += i + 1; if (Math.abs(arr[i] * 1.0 / arr[j] - x) < eps) { a = arr[i]; b = arr[j]; } } return ans; } }
  • 时间复杂度:二分次数取决于精度,精度为 $C = 10^8$,二分复杂度为 $O(\log{C});$check 的复杂度为 $O(n)$。整体复杂度为 $O(n * \log{C})$
  • 空间复杂度:$O(1)$

其他相关内容

1. 双指针

题目 题解 难度 推荐指数
3. 无重复字符的最长子串 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
11. 盛最多水的容器 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
15. 三数之和 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
16. 最接近的三数之和 LeetCode 题解链接 中等 🤩🤩🤩🤩
18. 四数之和 LeetCode 题解链接 中等 🤩🤩🤩🤩
19. 删除链表的倒数第 N 个结点 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
26. 删除有序数组中的重复项 LeetCode 题解链接 简单 🤩🤩🤩🤩
27. 移除元素 LeetCode 题解链接 简单 🤩🤩🤩🤩
45. 跳跃游戏 II LeetCode 题解链接 中等 🤩🤩🤩🤩
88. 合并两个有序数组 LeetCode 题解链接 简单 🤩🤩🤩
345. 反转字符串中的元音字母 LeetCode 题解链接 简单 🤩🤩🤩
395. 至少有 K 个重复字符的最长子串 LeetCode 题解链接 中等 🤩🤩🤩
413. 等差数列划分 LeetCode 题解链接 中等 🤩🤩🤩🤩
424. 替换后的最长重复字符 LeetCode 题解链接 中等 🤩🤩🤩🤩
438. 找到字符串中所有字母异位词 LeetCode 题解链接 中等 🤩🤩🤩🤩
443. 压缩字符串 LeetCode 题解链接 中等 🤩🤩🤩🤩
485. 最大连续 1 的个数 LeetCode 题解链接 简单 🤩🤩🤩🤩
519. 随机翻转矩阵 LeetCode 题解链接 中等 🤩🤩🤩🤩
524. 通过删除字母匹配到字典里最长单词 LeetCode 题解链接 中等 🤩🤩🤩🤩
581. 最短无序连续子数组 LeetCode 题解链接 中等 🤩🤩🤩🤩
594. 最长和谐子序列 LeetCode 题解链接 简单 🤩🤩🤩🤩
611. 有效三角形的个数 LeetCode 题解链接 中等 🤩🤩🤩🤩
633. 平方数之和 LeetCode 题解链接 简单 🤩🤩
786. 第 K 个最小的素数分数 LeetCode 题解链接 中等 🤩🤩🤩
832. 翻转图像 LeetCode 题解链接 简单 🤩🤩
881. 救生艇 LeetCode 题解链接 中等 🤩🤩🤩🤩
930. 和相同的二元子数组 LeetCode 题解链接 中等 🤩🤩🤩
992. K 个不同整数的子数组 LeetCode 题解链接 困难 🤩🤩🤩🤩
1004. 最大连续1的个数 III LeetCode 题解链接 中等 🤩🤩🤩
1052. 爱生气的书店老板 LeetCode 题解链接 中等 🤩🤩🤩
1221. 分割平衡字符串 LeetCode 题解链接 简单 🤩🤩🤩🤩
1743. 从相邻元素对还原数组 LeetCode 题解链接 中等 🤩🤩🤩🤩
1764. 通过连接另一个数组的子数组得到一个数组 LeetCode 题解链接 中等 🤩🤩🤩🤩

注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。


2. 二分

题目 题解 难度 推荐指数
4. 寻找两个正序数组的中位数 LeetCode 题解链接 困难 🤩🤩🤩🤩
29. 两数相除 LeetCode 题解链接 中等 🤩🤩🤩
33. 搜索旋转排序数组 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
34. 在排序数组中查找元素的第一个和最后一个位置 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
35. 搜索插入位置 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
74. 搜索二维矩阵 LeetCode 题解链接 中等 🤩🤩🤩🤩
81. 搜索旋转排序数组 II LeetCode 题解链接 中等 🤩🤩🤩🤩
153. 寻找旋转排序数组中的最小值 LeetCode 题解链接 中等 🤩🤩🤩
154. 寻找旋转排序数组中的最小值 II LeetCode 题解链接 困难 🤩🤩🤩
162. 寻找峰值 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
220. 存在重复元素 III LeetCode 题解链接 中等 🤩🤩🤩
240. 搜索二维矩阵 II LeetCode 题解链接 中等 🤩🤩🤩🤩
274. H 指数 LeetCode 题解链接 中等 🤩🤩🤩
275. H 指数 II LeetCode 题解链接 中等 🤩🤩🤩
278. 第一个错误的版本 LeetCode 题解链接 简单 🤩🤩🤩🤩
352. 将数据流变为多个不相交区间 LeetCode 题解链接 困难 🤩🤩🤩🤩
354. 俄罗斯套娃信封问题 LeetCode 题解链接 困难 🤩🤩🤩
363. 矩形区域不超过 K 的最大数值和 LeetCode 题解链接 困难 🤩🤩🤩
367. 有效的完全平方数 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
374. 猜数字大小 LeetCode 题解链接 简单 🤩🤩🤩
441. 排列硬币 LeetCode 题解链接 简单 🤩🤩🤩
528. 按权重随机选择 LeetCode 题解链接 中等 🤩🤩🤩🤩
611. 有效三角形的个数 LeetCode 题解链接 中等 🤩🤩🤩🤩
704. 二分查找 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
778. 水位上升的泳池中游泳 LeetCode 题解链接 困难 🤩🤩🤩
786. 第 K 个最小的素数分数 LeetCode 题解链接 中等 🤩🤩🤩
852. 山脉数组的峰顶索引 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
981. 基于时间的键值存储 LeetCode 题解链接 中等 🤩🤩🤩🤩
1004. 最大连续1的个数 III LeetCode 题解链接 中等 🤩🤩🤩
1011. 在 D 天内送达包裹的能力 LeetCode 题解链接 中等 🤩🤩🤩🤩
1208. 尽可能使字符串相等 LeetCode 题解链接 中等 🤩🤩🤩
1337. 矩阵中战斗力最弱的 K 行 LeetCode 题解链接 简单 🤩🤩🤩
1438. 绝对差不超过限制的最长连续子数组 LeetCode 题解链接 中等 🤩🤩🤩
1482. 制作 m 束花所需的最少天数 LeetCode 题解链接 中等 🤩🤩🤩
1707. 与数组中元素的最大异或值 LeetCode 题解链接 困难 🤩🤩🤩
1713. 得到子序列的最少操作次数 LeetCode 题解链接 困难 🤩🤩🤩
1751. 最多可以参加的会议数目 II LeetCode 题解链接 困难 🤩🤩🤩
1818. 绝对差值和 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
1838. 最高频元素的频数 LeetCode 题解链接 中等 🤩🤩🤩
1894. 找到需要补充粉笔的学生编号 LeetCode 题解链接 中等 🤩🤩🤩🤩
剑指 Offer 53 - I. 在排序数组中查找数字 I LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
剑指 Offer II 069. 山峰数组的顶部 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩

注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。


3. 堆

题目 题解 难度 推荐指数
23. 合并K个升序链表 LeetCode 题解链接 困难 🤩🤩🤩
218. 天际线问题 LeetCode 题解链接 困难 🤩🤩🤩
264. 丑数 II LeetCode 题解链接 中等 🤩🤩🤩
295. 数据流的中位数 LeetCode 题解链接 中等 🤩🤩🤩🤩
313. 超级丑数 LeetCode 题解链接 中等 🤩🤩🤩
407. 接雨水 II LeetCode 题解链接 困难 🤩🤩🤩🤩
451. 根据字符出现频率排序 LeetCode 题解链接 中等 🤩🤩🤩🤩
480. 滑动窗口中位数 LeetCode 题解链接 困难 🤩🤩🤩🤩
502. IPO LeetCode 题解链接 困难 🤩🤩🤩
692. 前K个高频单词 LeetCode 题解链接 中等 🤩🤩🤩🤩
703. 数据流中的第 K 大元素 LeetCode 题解链接 简单 🤩🤩🤩
726. 原子的数量 LeetCode 题解链接 困难 🤩🤩🤩🤩
786. 第 K 个最小的素数分数 LeetCode 题解链接 中等 🤩🤩🤩
987. 二叉树的垂序遍历 LeetCode 题解链接 困难 🤩🤩🤩🤩🤩
1337. 矩阵中战斗力最弱的 K 行 LeetCode 题解链接 简单 🤩🤩🤩
1834. 单线程 CPU LeetCode 题解链接 中等 🤩🤩🤩🤩
面试题 17.14. 最小K个数 LeetCode 题解链接 中等 🤩🤩🤩🤩

注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。


4. 多路归并

题目 题解 难度 推荐指数
21. 合并两个有序链表 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
264. 丑数 II LeetCode 题解链接 中等 🤩🤩🤩🤩
313. 超级丑数 LeetCode 题解链接 中等 🤩🤩🤩
786. 第 K 个最小的素数分数 LeetCode 题解链接 中等 🤩🤩🤩

注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。


最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ (“▔□▔)/

也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

统计信息

通过次数 提交次数 AC比率
24916 36882 67.6%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
有序矩阵中第 K 小的元素 中等
乘法表中第k小的数 困难
找出第 k 小的距离对 困难
上一篇:
785-判断二分图(Is Graph Bipartite?)
下一篇:
787-K 站中转内最便宜的航班(Cheapest Flights Within K Stops)
本文目录
本文目录