题目描述
这是 LeetCode 上的 786. 第 K 个最小的素数分数 ,难度为 中等。
Tag : 「优先队列(堆)」、「多路归并」、「二分」、「双指针」
给你一个按递增顺序排序的数组 arr
和一个整数 k
。
数组 arr
由 和若干 素数 组成,且其中所有整数互不相同。
对于每对满足 的
和
,可以得到分数
那么第 个最小的分数是多少呢? 以长度为
的整数数组返回你的答案, 这里
且
示例 1:
示例 2:
提示:
是一个 素数 ,
中的所有数字 互不相同 ,且按严格递增排序
优先队列(堆)
数据范围只有 ,直接扫描所有点对的计算量不超过
。
因此我们可以使用「扫描点对」+「优先队列(堆)」的做法,使用二元组 进行存储,构建大小为
根据「堆内元素多少」和「当前计算值与堆顶元素的大小关系」决定入堆行为:
- 若堆内元素不足
- 若堆内元素已达
个,根据「当前计算值
与堆顶元素
的大小关系」进行分情况讨论:
- 如果当前计算值比堆顶元素大,那么当前元素不可能是第
- 如果当前计算值比堆顶元素小,那么堆顶元素不可能是第
代码:
- 时间复杂度:扫描所有的点对复杂度为
;将二元组入堆和出堆的复杂度为
。整体复杂度为
- 空间复杂度:
多路归并
在解法一中,我们没有利用「数组内元素严格单调递增」的特性。
由于题目规定所有的点对 必须满足
,即给定
后,其所能构建的分数个数为
个,而这
个分数值满足严格单调递增:
。
问题等价于我们从 个(下标
作为分母的话,不存在任何分数)有序序列中找到第
小的数值。这
问题彻底切换为「多路归并」问题,我们使用「优先队列(堆)」来维护多个有序序列的当前头部的最小值即可。
代码:
- 时间复杂度:起始将
个序列的头部元素放入堆中,复杂度为
;然后重复
次操作得到第
小的值,复杂度为
。整体复杂度为
- 空间复杂度:
二分 + 双指针
进一步,利用 递增,且每个点对
满足
,我们可以确定
对应的分数
必然落在
假设最终答案 为
,那么以
为分割点的数轴(该数轴上的点为
- 小于等于
的值满足:其左边分数值个数小于
- 大于
的值不满足:其左边分数值个数小于
个(即至少有
而当确定 时,利用
有序,我们可以通过「双指针」快速得知,满足
的分子位置在哪(找到最近一个满足
另外,我们可以在每次 check
的同时,记录下相应的 和
。
代码:
- 时间复杂度:二分次数取决于精度,精度为
,二分复杂度为
check
的复杂度为。整体复杂度为
- 空间复杂度:
最后
这是我们「刷穿 LeetCode」系列文章的第 No.786
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。