leetcode
leetcode 701 ~ 750
第 K 个最小的质数分数

第 K 个最小的质数分数

难度:

标签:

题目描述

代码结果

运行时间: 51 ms, 内存: 16.1 MB


import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
/**
 * 思路:
 * 使用 Java Stream API 创建所有可能的分数对。
 * 将分数对根据分数排序,并获取第 k 小的分数对。
 */
public class Solution {
    public int[] kthSmallestPrimeFraction(int[] arr, int k) {
        List<int[]> fractions = IntStream.range(0, arr.length)
            .boxed()
            .flatMap(i -> IntStream.range(i + 1, arr.length)
                .mapToObj(j -> new int[]{i, j}))
            .sorted((a, b) -> Double.compare((double) arr[a[0]] / arr[a[1]], (double) arr[b[0]] / arr[b[1]]))
            .collect(Collectors.toList());
        return new int[]{arr[fractions.get(k - 1)[0]], arr[fractions.get(k - 1)[1]]};
    }
}

解释

方法:

该题解使用二分查找的思路。首先将查找范围设为 [0, 1],因为分数的取值范围在 0 到 1 之间。然后在每次二分查找时,统计小于等于 mid 的分数个数 count。如果 count 等于 k,说明找到了第 k 小的分数;如果 count 小于 k,说明第 k 小的分数在 (mid, right] 范围内,缩小查找范围为 [mid, right];如果 count 大于 k,说明第 k 小的分数在 [left, mid) 范围内,缩小查找范围为 [left, mid]。在统计 count 的过程中,使用双指针分别指向分子和分母,并记录最大的分数。

时间复杂度:

O(nlog(1/eps))

空间复杂度:

O(1)

代码细节讲解

🦆
在二分查找中,为什么选择将查找范围初始化为[0, 1],而不是基于数组`arr`的具体数值来定义范围?
由于`arr`数组中的元素都是质数,且分数的形式为arr[i]/arr[j] (i < j),这意味着所有可能的分数的值必定在0到1之间。最小的可能值0可以通过一个非常小的数除以一个非常大的数趋近得到(虽然实际上不会出现这种分数),而最大的可能值1是通过任何数除以其自身得到。因此,[0,1]是一个合理的初始化范围,能覆盖所有可能的分数值。
🦆
如何确保在while循环中的二分查找过程最终能够停止?即,什么条件下会退出循环?
在题解的实现中,二分查找的终止条件是在找到恰好的第k小的分数时。由于每次迭代都会将搜索区间减半,因此会逐渐接近真正的第k小的分数值。虽然题解中没有明确的退出while循环的条件,实际实现时通常会设置一个足够小的阈值(例如epsilon),当right和left的差值小于这个阈值时,可以认为已经足够接近答案,此时可以终止循环。
🦆
在统计小于等于`mid`的分数个数时,为什么使用双指针方法有效?这种方法有哪些潜在的限制或假设?
双指针方法有效的原因在于它能高效地遍历所有的分数并计数。其中一个指针固定分母,另一个指针遍历可能的分子,从而快速统计出小于等于mid的分数个数。这种方法假设数组`arr`是有序的,因此能保证分数是递增的,从而使双指针方法有效。潜在的限制包括对数组的排序(如果原始数组未排序),以及在某些情况下可能会因为过多的相同比值的分数而导致计数复杂化。
🦆
题解中提到记录最大的分数,这是否与寻找第k小的分数矛盾?是否应该记录最小的分数而不是最大的分数?
这里的“最大的分数”指的是在当前二分查找的mid值下,小于等于mid的所有分数中最接近mid的那个分数。随着二分查找的进行,我们需要不断更新这个“最接近mid的最大分数”,以便当找到恰好有k个分数小于某个mid时,能够返回这个最接近的分数。因此,这并不矛盾,而是为了确保能返回最接近真实第k小分数的值,这个过程是对结果的一种优化和精确控制。

相关问题

有序矩阵中第 K 小的元素

给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是 排序后 的第 k 小元素,而不是第 k不同 的元素。

你必须找到一个内存复杂度优于 O(n2) 的解决方案。

 

示例 1:

输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
输出:13
解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13

示例 2:

输入:matrix = [[-5]], k = 1
输出:-5

 

提示:

  • n == matrix.length
  • n == matrix[i].length
  • 1 <= n <= 300
  • -109 <= matrix[i][j] <= 109
  • 题目数据 保证 matrix 中的所有行和列都按 非递减顺序 排列
  • 1 <= k <= n2

 

进阶:

  • 你能否用一个恒定的内存(即 O(1) 内存复杂度)来解决这个问题?
  • 你能在 O(n) 的时间复杂度下解决这个问题吗?这个方法对于面试来说可能太超前了,但是你会发现阅读这篇文章( this paper )很有趣。

乘法表中第k小的数

几乎每一个人都用 乘法表。但是你能在乘法表中快速找到第 k 小的数字吗?

乘法表是大小为 m x n 的一个整数矩阵,其中 mat[i][j] == i * j(下标从 1 开始)。

给你三个整数 mnk,请你在大小为 m x n 的乘法表中,找出并返回第 k 小的数字。

 

示例 1:

输入:m = 3, n = 3, k = 5
输出:3
解释:第 5 小的数字是 3 。

示例 2:

输入:m = 2, n = 3, k = 6
输出:6
解释:第 6 小的数字是 6 。

 

提示:

  • 1 <= m, n <= 3 * 104
  • 1 <= k <= m * n

找出第 K 小的数对距离

数对 (a,b) 由整数 ab 组成,其数对距离定义为 ab 的绝对差值。

给你一个整数数组 nums 和一个整数 k ,数对由 nums[i]nums[j] 组成且满足 0 <= i < j < nums.length 。返回 所有数对距离中k 小的数对距离。

 

示例 1:

输入:nums = [1,3,1], k = 1
输出:0
解释:数对和对应的距离如下:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
距离第 1 小的数对是 (1,1) ,距离为 0 。

示例 2:

输入:nums = [1,1,1], k = 2
输出:0

示例 3:

输入:nums = [1,6,1], k = 3
输出:5

 

提示:

  • n == nums.length
  • 2 <= n <= 104
  • 0 <= nums[i] <= 106
  • 1 <= k <= n * (n - 1) / 2