【Leetcode】632. Smallest Range Covering Elements from K Lists

该博客讨论了一种使用多路归并算法解决寻找覆盖K个已排序列表中至少一个元素的最小区间的问题。算法通过维护一个最小堆来跟踪每个列表中的元素,并不断更新最小区间。时间复杂度为O(nlogk),空间复杂度为O(k)。
摘要由CSDN通过智能技术生成

题目地址:

https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/

给定 k k k个非空已排序列表 A A A,要求返回一个最短的区间 [ a , b ] [a,b] [a,b],使得每个列表至少有一个数含在 [ a , b ] [a,b] [a,b]里。如果有多个区间满足要求,则返回左端点最小的那个。

思路是多路归并。开个最小堆,里面存长度是 3 3 3的数组 c c c,其中 c [ 1 ] c[1] c[1]代表列表下标, c [ 2 ] c[2] c[2]代表数字在 A [ c [ 1 ] ] A[c[1]] A[c[1]]里的下标, c [ 0 ] = A [ c [ 1 ] ] [ c [ 2 ] ] c[0]=A[c[1]][c[2]] c[0]=A[c[1]][c[2]]。先将每个列表的第一个数字连同其下标加入这个最小堆,同时开个变量 M M M存一下堆里的最大值,接着开始多路归并。每次从堆中出一个数组 c c c,那么包含 c [ 0 ] c[0] c[0]的且包含所有其他列表里至少一个数的区间就应该是 [ c [ 0 ] , M ] [c[0],M] [c[0],M],以此区间更新答案(就是说如果这个区间更短,那就把答案设为这个区间。由于我们就是按左端点由小到大枚举的,所以不需要考虑左端点的问题)。更新完答案之后,再将 A [ c [ 1 ] ] [ c [ 2 ] + 1 ] A[c[1]][c[2]+1] A[c[1]][c[2]+1]这个数及其下标入堆,然后重复上面的操作。当 A [ c [ 1 ] ] [ c [ 2 ] + 1 ] A[c[1]][c[2]+1] A[c[1]][c[2]+1]这个数不存在的时候(即 A [ c [ 1 ] ] A[c[1]] A[c[1]]这个列表已经枚举完了),此时左端点为 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]的区间就已经枚举完了,后面再枚举就不会得到含 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]这个数的区间了,就不满足条件了,所以可以直接结束枚举,退出循环(这里有一些不严谨,因为如果后面枚举了只含 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]的单点区间的话,这个区间仍然可能作为答案。但是这个单点区间在 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]这个数出堆的时候已经枚举过了,所以并不影响算法的正确性)。代码如下:

import java.util.List;
import java.util.PriorityQueue;

public class Solution {
    public int[] smallestRange(List<List<Integer>> nums) {
        PriorityQueue<int[]> maxHeap = new PriorityQueue<>((p1, p2) -> Integer.compare(p1[0], p2[0]));
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < nums.size(); i++) {
            int val = nums.get(i).get(0);
            maxHeap.offer(new int[]{val, i, 0});
            max = Math.max(max, val);
        }
        
        int[] res = null;
        while (true) {
            int[] top = maxHeap.poll();
            int l = top[0], r = max;
            // 发现了更短的区间,则更新
            if (res == null || res[1] - res[0] > r - l) {
                res = new int[]{l, r};
            }
            
            int row = top[1], idx = top[2];
            if (idx + 1 < nums.get(row).size()) {
                int val = nums.get(row).get(idx + 1);
                maxHeap.offer(new int[]{val, row, idx + 1});
                max = Math.max(max, val);
            } else {
            	// 某一行作为左端点枚举完毕,则退出循环
                break;
            }
        }
        
        return res;
    }
}

时间复杂度 O ( n log ⁡ k ) O(n\log k) O(nlogk) n n n是数字总个数,空间 O ( k ) O(k) O(k)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值