优先队列-多路归并系列题解

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
posted @ 2022-08-27 10:15  hyserendipity  阅读(22)  评论(0编辑  收藏  举报