373. 查找和最小的 K 对数字
问题描述
给定两个以 升序排列 的整数数组 nums1 和 nums2 , 以及一个整数 k 。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。
请找到和最小的 k 个数对 (u1,v1), (u2,v2) ... (uk,vk) 。
示例 1:
输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
示例 2:
输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出: [1,1],[1,1]
解释: 返回序列中的前 2 对数:
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
示例 3:
输入: nums1 = [1,2], nums2 = [3], k = 3
输出: [1,3],[2,3]
解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]
提示:
1 <= nums1.length, nums2.length <= 105
-109 <= nums1[i], nums2[i] <= 109
nums1 和 nums2 均为升序排列
1 <= k <= 1000
问题求解
可以将原问题转为多路归并问题:可以看作是以(i,0)开头的多个列表进行归并。
需要注意的是这里由于是构造题,因此使用这种方式比较好,如果是直接求第k大,则最优解是二分搜索,详见1439. 有序矩阵中的第 k 个最小数组和
class Solution:
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
n, m = len(nums1), len(nums2)
res = []
h = []
for i in range(n):
heappush(h, (nums1[i] + nums2[0], i, 0))
while len(res) < k and h:
s, i, j = heappop(h)
res.append([nums1[i], nums2[j]])
if j + 1 < m: heappush(h, (nums1[i] + nums2[j + 1], i, j + 1))
return res
786. 第 K 个最小的素数分数
问题描述
给你一个按递增顺序排序的数组 arr 和一个整数 k 。数组 arr 由 1 和若干 素数 组成,且其中所有整数互不相同。
对于每对满足 0 <= i < j < arr.length 的 i 和 j ,可以得到分数 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
问题求解
class Solution:
def kthSmallestPrimeFraction(self, arr: List[int], k: int) -> List[int]:
n = len(arr)
res = []
h = []
for i in range(0, n - 1):
heappush(h, (arr[i] / arr[n - 1], i, n - 1))
while len(res) < k:
_, i, j = heappop(h)
res.append([arr[i], arr[j]])
if j - 1 > i:
heappush(h, (arr[i] / arr[j - 1], i, j - 1))
return res[-1]
632. 最小区间
问题描述
你有 k 个 非递减排列 的整数列表。找到一个 最小 区间,使得 k 个列表中的每个列表至少有一个数包含在其中。
我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。
示例 1:
输入:nums = [[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出:[20,24]
解释:
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。
示例 2:
输入:nums = [[1,2,3],[1,2,3],[1,2,3]]
输出:[1,1]
提示:
nums.length == k
1 <= k <= 3500
1 <= nums[i].length <= 50
-105 <= nums[i][j] <= 105
nums[i] 按非递减顺序排列
问题求解
多路归并的变种题。
按照多路归并流程进行合并,同时记录最大的数值并维护区间即可。
这一题有个变种题,详见1675. 数组的最小偏移量
class Solution:
def smallestRange(self, nums: List[List[int]]) -> List[int]:
n = len(nums)
res = [float("inf"), -float("inf")]
h = []
for i in range(n):
heappush(h, (nums[i][0], i, 0))
res[0] = min(res[0], nums[i][0])
res[1] = max(res[1], nums[i][0])
r = res[1]
while len(h) == n:
l, i, j = heappop(h)
if r - l < res[1] - res[0]:
res = [l, r]
if j + 1 < len(nums[i]):
heappush(h, (nums[i][j + 1], i, j + 1))
r = max(r, nums[i][j + 1])
return res
1508. 子数组和排序后的区间和
问题描述
给你一个数组 nums ,它包含 n 个正整数。你需要计算所有非空连续子数组的和,并将它们按升序排序,得到一个新的包含 n * (n + 1) / 2 个数字的数组。
请你返回在新数组中下标为 left 到 right (下标从 1 开始)的所有数字和(包括左右端点)。由于答案可能很大,请你将它对 10^9 + 7 取模后返回。
示例 1:
输入:nums = [1,2,3,4], n = 4, left = 1, right = 5
输出:13
解释:所有的子数组和为 1, 3, 6, 10, 2, 5, 9, 3, 7, 4 。将它们升序排序后,我们得到新的数组 [1, 2, 3, 3, 4, 5, 6, 7, 9, 10] 。下标从 le = 1 到 ri = 5 的和为 1 + 2 + 3 + 3 + 4 = 13 。
示例 2:
输入:nums = [1,2,3,4], n = 4, left = 3, right = 4
输出:6
解释:给定数组与示例 1 一样,所以新数组为 [1, 2, 3, 3, 4, 5, 6, 7, 9, 10] 。下标从 le = 3 到 ri = 4 的和为 3 + 3 = 6 。
示例 3:
输入:nums = [1,2,3,4], n = 4, left = 1, right = 10
输出:50
提示:
1 <= nums.length <= 10^3
nums.length == n
1 <= nums[i] <= 100
1 <= left <= right <= n * (n + 1) / 2
问题求解
class Solution:
def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int:
res = 0
mod = 10 ** 9 + 7
h = []
for i in range(n):
heappush(h, (nums[i], i))
cnt = 0
while cnt < right:
s, i = heappop(h)
cnt += 1
if cnt >= left and cnt <= right:
res = (res + s) % mod
if i + 1 < n:
heappush(h, (s + nums[i + 1], i + 1))
return res