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


    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
    
  • 相关阅读:
    PHP 正则表达式抓取网页内容。
    FZU 2252 Yu-Gi-Oh!(枚举+贪心)
    Flask 学习篇一: 搭建Python虚拟环境,安装flask,并设计RESTful API。
    Flask 学习笔记
    SSH框架搭建
    javaWeb项目(SSH框架+AJAX+百度地图API+Oracle数据库+MyEclipse+Tomcat)之二 基础Hibernate框架搭建篇
    天梯赛 大区赛 L3-014.周游世界 (Dijkstra)
    Windows 和 Mac 系统下安装git 并上传,修改项目
    浙江工业大学校赛 小M和天平
    Java实现 蓝桥杯VIP 算法训练 非递归(暴力)
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/16629906.html
Copyright © 2020-2023  润新知