【Leetcode 每日一题】632. 最小区间

问题背景

你有 k k k非递减排列 的整数列表。找到一个 最小 区间,使得 k k k 个列表中的每个列表至少有一个数包含在其中。
我们定义如果 b − a < d − c b - a < d - c ba<dc 或者在 b − a = = d − c b - a == d - c ba==dc a < c a < c a<c,则区间 [ a , b ] [a,b] [a,b] [ c , d ] [c,d] [c,d] 小。
数据约束

  • n u m s . l e n g t h = = k nums.length == k nums.length==k
  • 1 ≤ k ≤ 3500 1 \le k \le 3500 1k3500
  • 1 ≤ n u m s [ i ] . l e n g t h ≤ 50 1 \le nums[i].length \le 50 1nums[i].length50
  • − 1 0 5 ≤ n u m s [ i ] [ j ] ≤ 1 0 5 -10 ^ 5 \le nums[i][j] \le 10 ^ 5 105nums[i][j]105
  • n u m s [ i ] nums[i] nums[i] 按非递减顺序排列

解题过程

解决这个问题的关键在于,想明白答案是从每个区间的左端获得的。由于给定的列表中的子列表元素有序,所以对于某一个状态的问题列表而言,结果的左端点就是所有列表中第一个元素的最小值,结果的右端点就是所有列表中第一个元素的最大值。
不过由于题中区间的大小是由区间长度来决定的,所以有可能出现除了第一个元素以外的其他元素组成更小的区间的情况。因此还需要考虑使用第一个元素以外的元素时,能否构成更小的区间。也就是说,每一轮循环中,要做的是根据当前所有列表中最小元素确定右端点。因此,我们需要一个能够根据其中元素的情况来维护最小值的数据结构,选用最小堆。
堆中的元素可以是一个三元组,除了最小的元素,额外保存它所在的列表序号,以及在列表中的下标,这样可以方便地在题中所给列表 n u m s nums nums 中进行定位。更新状态时,不必移除元素,改变堆顶三元组中的左端点即可。
这样做的时间复杂度是 O ( L l o g k ) O(Llogk) O(Llogk),其中 L L L 是所有列表的长度之和,其中 k k k 是题中所给数组 n u m s nums nums 的长度。循环 L L L 次,每次循环需要 l o g k logk logk 量级的时间操作堆。空间复杂度是 O ( k ) O(k) O(k),堆需要一定的空间来保存元素。

另一种思路是,记录每个元素所在的列表编号之后,将所有列表中的数字放在一起排序。
这时候问题就转变为,在有序序列中找到最短的子数组,使得其中包含每个列表中至少一个元素。子数组中包含的每个列表中的元素数量随着子数组长度增长而增加,可以滑窗。
这种思路的时间复杂度是 O ( L l o g L ) O(LlogL) O(LlogL) 或者 O ( L ) O(L) O(L),其中 L L L 是所有 n u m s [ i ] nums[i] nums[i] 的长度之和。考虑到瓶颈在排序上,将默认排序算法 T i m S o r t TimSort TimSort 替换成昨天刚刚整理过的计数排序 C o u n t i n g S o r t CountingSort CountingSort,可以有效提升性能。空间复杂度是 O ( L ) O(L) O(L),需要额外空间来保存包含所有元素的数组。

具体实现

维护最小堆

class Solution {
    public int[] smallestRange(List<List<Integer>> nums) {
        // Java 中可以定义优先队列,自定义排序方式来作为堆
        PriorityQueue<int[]> priorityQueue = new PriorityQueue<>((i1, i2) -> i1[0] - i2[0]);
        int right = Integer.MIN_VALUE;
        for(int i = 0; i < nums.size(); i++) {
            // 原列表中所有的第一个元素入堆
            int cur = nums.get(i).get(0);
            // 同时记录该元素在原列表中的坐标
            priorityQueue.offer(new int[]{cur, i, 0});
            // 右端点是所有第一个元素中的最大值
            right = Math.max(right, cur);
        }

        int resL = priorityQueue.peek()[0];
        int resR = right;
        // 堆顶三元组中下标为 2 的元素记录的是当前元素的位置,先自增表示下一个元素的位置
        // 循环结束的条件是出现某个列表里没有下一个元素,无法构成合法区间
        while(++priorityQueue.peek()[2] < nums.get(priorityQueue.peek()[1]).size()) {
            // 获取并更新堆顶三元组
            int[] top = priorityQueue.poll();
            top[0] = nums.get(top[1]).get(top[2]); // 经过自增,这里取得的是下一个元素
            // 取出来之后更新右端点
            right = Math.max(right, top[0]);
            // 复用三元组,将修改了值之后的新三元组入堆
            priorityQueue.offer(top);
            // 从调整之后的合法堆中获取当前第一个元素的最小值
            int left = priorityQueue.peek()[0];
            if(right - left < resR - resL) {
                resL = left;
                resR = right;
            }
        }
        return new int[] {resL, resR};
    }
}

排序后滑窗

class Solution {
    public int[] smallestRange(List<List<Integer>> nums) {
        // 计算保存所有元素需要的数组长度
        int length = 0;
        for(List<Integer> list : nums) {
            length += list.size();
        }

        // 将列表中所有元素记录到新数组中
        int[][] pairs = new int[length][2];
        int index = 0;
        for(int i = 0; i < nums.size(); i++) {
            for(int num : nums.get(i)) {
                pairs[index][0] = num;
                pairs[index++][1] = i;
            }
        }
        
        // 使用 Java 默认的 排序算法 TimSort
        // Arrays.sort(pairs, (a, b) -> a[0] - b[0]);
        // 自己实现二维计数排序 CountingSort2d
        countingSort2d(pairs);

        int resL = pairs[0][0];
        int resR = pairs[length - 1][0];
        int diff = nums.size(); // diff 表示没有元素被包含的列表数量
        int[] count = new int[diff]; // 统计每个列表中被包含的元素数量
        int leftIndex = 0; // 记录当前左端点的所在的列表编号
        for(int[] pair : pairs) {
            int right = pair[0];
            int curIndex = pair[1];
            // 当前列表有元素被包含,diff 减少
            if(count[curIndex]++ == 0) {
                diff--;
            }
            while(diff == 0) {
                int left = pairs[leftIndex][0]; // 取出当前左端点
                // 满足条件的情况下更新答案
                if(right - left < resR - resL) {
                    resL = left;
                    resR = right;
                }
                // 更新左端点所在的列表编号
                curIndex = pairs[leftIndex][1];
                // 如果当前列表中没有元素被包含,diff 增加
                if(--count[curIndex] == 0) {
                    diff++;
                }
                leftIndex++; // 移动左端点
            }
        }
        return new int[] {resL, resR};
    }

    private void countingSort2d(int[][] pairs) {
        // 查找数组中的最小最大值,确定映射范围
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        for(int[] pair : pairs) {
            min = Math.min(min, pair[0]);
            max = Math.max(max, pair[0]);
        }
        int range = max - min + 1;
        int[] count = new int[range];
        // 统计每个元素出现的次数
        for(int[] pair : pairs) {
            count[pair[0] - min]++;
        }
        // 改造成前缀分区的形式,方便快速地确定还原后的元素位置
        for(int i = 1; i < count.length; i++) {
            count[i] += count[i - 1];
        }
        int length = pairs.length;
        // 还原数组元素
        int[][] res = new int[length][2];
        for(int i = length - 1; i >= 0; i--) {
            int cur = pairs[i][0];
            res[--count[cur - min]] = pairs[i];
        }
        // 把结果回写到原数组中
        System.arraycopy(res, 0, pairs, 0, length);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/923264.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ensp动态路由OSPF实验

一、实验目的 1、熟练掌握交换机的基本配置命令 2、熟练掌握ospf的使用规则 3. 熟练掌握交换机端口模式 二、实验内容 需求&#xff1a; 根据要求利用现有实验设备组建小型局域网 实验设备&#xff1a; 交换机S37002台&#xff1b;交换机S57002台&#xff1b;路由器2台。…

Python绘制太极八卦

文章目录 系列目录写在前面技术需求1. 图形绘制库的支持2. 图形绘制功能3. 参数化设计4. 绘制控制5. 数据处理6. 用户界面 完整代码代码分析1. rset() 函数2. offset() 函数3. taiji() 函数4. bagua() 函数5. 绘制过程6. 技术亮点 写在后面 系列目录 序号直达链接爱心系列1Pyth…

package.json中^1.x.x、~1.x.x、1.x.x有什么区别

目录 包版本号的语义化 包版本号的符号 举例 包版本号的语义化 在开始回答这个问题之前&#xff0c;先简单介绍一下包版本号的语义化。 在npm中&#xff0c;包的版本号通常遵循语义化版本规范&#xff08;Semantic Versioning&#xff09;&#xff0c;即采用 major.minor.p…

力扣hot100-->排序

排序 1. 56. 合并区间 中等 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输…

一分钟学习数据安全——数据安全风险的系统化应对思路

数据是组织的重要资产&#xff0c;未经授权的数据访问可能导致数据泄露、数据篡改、隐私侵犯和合规风险等问题。企业可以通过数据访问控制来提高信息系统在数据全生命周期管理中的安全性。企业可以引入IAM系统&#xff0c;来控制身份来管理权限。通过对用户访问权限的管理和合适…

免费实用在线AI工具集合 - 加菲工具

免费在线工具-加菲工具 https://orcc.online/ 在线录屏 https://orcc.online/recorder 时间戳转换 https://orcc.online/timestamp Base64 编码解码 https://orcc.online/base64 URL 编码解码 https://orcc.online/url Hash(MD5/SHA1/SHA256…) 计算 https://orcc.online/h…

五种创建k8s的configMap的方式及configmap使用

configmap介绍 Kubernetes 提供了 ConfigMap 来管理应用配置数据&#xff0c;将配置信息从容器镜像中解耦&#xff0c;使应用更灵活、可移植。 1、基于一个目录来创建ConfigMap ​ 你可以使用 kubectl create configmap 基于同一目录中的多个文件创建 ConfigMap。 当你基于目…

ssm185大学学术交流论坛+vue(论文+源码)_kaic

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于大学学术交流论坛当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了大学学术交流论坛的发展&#xff0c;它彻底…

构建 Java Web 应用程序:从 Servlet 到数据库交互(Eclipse使用JDBC连接Mysql数据库)

第 1 部分&#xff1a;环境设置 安装 Java Development Kit (JDK)&#xff1a;下载并安装 JDK。设置 IDE&#xff1a;安装并配置 IDE&#xff08;如 IntelliJ IDEA 或 Eclipse&#xff09;。安装数据库&#xff1a;下载并安装 MySQL 数据库。配置数据库&#xff1a;创建数据库…

C 语言面向对象

面向对象的基本特性&#xff1a;封装&#xff0c;继承&#xff0c;多态 1.0 面向过程概念 当我们在编写程序时&#xff0c;通常采用以下步骤&#xff1a; 1. 将问题的解法分解成若干步骤 2. 使用函数分别实现这些步骤 3. 依次调用这些函数 这种编程风格的被称作 面向过程…

aws服务--机密数据存储KMS(1)介绍和使用

在AWS(Amazon Web Services)中存储机密数据时,安全性和合规性是最重要的考虑因素。AWS 提供了多个服务和工具,帮助用户确保数据的安全性、机密性以及合规性。AWS Secrets Manager、KMS(Key Management Service)是推荐的存储机密数据的AWS服务和最佳实践。这里先看KMS。 …

ArcGIS应用指南:ArcGIS制作局部放大地图

在地理信息系统&#xff08;GIS&#xff09;中&#xff0c;制作详细且美观的地图是一项重要的技能。地图制作不仅仅是简单地将地理数据可视化&#xff0c;还需要考虑地图的可读性和美观性。局部放大图是一种常见的地图设计技巧&#xff0c;用于展示特定区域的详细信息&#xff…

【案例学习】如何使用Minitab实现包装过程的自动化和改进

Masimo 是一家全球性的医疗技术公司&#xff0c;致力于开发和生产各种行业领先的监控技术&#xff0c;包括创新的测量、传感器和患者监护仪。在 Masimo Hospital Automation 平台的助力下&#xff0c;Masimo 的连接、自动化、远程医疗和远程监控解决方案正在改善医院内外的护理…

【JavaEE初阶】多线程初阶下部

文章目录 前言一、volatile关键字volatile 能保证内存可见性 二、wait 和 notify2.1 wait()方法2.2 notify()方法2.3 notifyAll()方法2.4 wait 和 sleep 的对比&#xff08;面试题&#xff09; 三、多线程案例单例模式 四、总结-保证线程安全的思路五、对比线程和进程总结 前言…

【小白学机器学习33】 大数定律python的 pandas.Dataframe 和 pandas.Series基础内容

目录 0 总结 0.1pd.Dataframe有一个比较麻烦琐碎的地方&#xff0c;就是引号 和括号 0.2 pd.Dataframe关于括号的原则 0.3 分清楚几个数据类型和对应的方法的范围 0.4 几个数据结构的构造关系 list → np.array(list) → pd.Series(np.array)/pd.Dataframe 1 python 里…

《用Python画蔡徐坤:艺术与编程的结合》

简介 大家好&#xff01;今天带来一篇有趣的Python编程项目&#xff0c;用代码画出知名偶像蔡徐坤的形象。这个项目使用了Python的turtle库&#xff0c;通过简单的几何图形和精心设计的代码来展示艺术与编程的结合。 以下是完整的代码和效果介绍&#xff0c;快来试试看吧&…

OSG开发笔记(三十三):同时观察物体不同角度的多视图从相机技术

​若该文为原创文章&#xff0c;未经允许不得转载 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/143932273 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 长沙红胖子Qt…

数据结构(Java版)第二期:包装类和泛型

目录 一、包装类 1.1. 基本类型和对应的包装类 1.2. 装箱和拆箱 1.3. 自动装箱和自动拆箱 二、泛型的概念 三、引出泛型 3.1. 语法规则 3.2. 泛型的优点 四、类型擦除 4.1. 擦除的机制 五、泛型的上界 5.1. 泛型的上界的定义 5.2. 语法规则 六、泛型方法 6.1…

ffmpeg 视频滤镜:高斯模糊-gblur

滤镜描述 gblur 官网地址 > FFmpeg Filters Documentation 这个滤镜会将视频变得模糊。 滤镜使用 参数 gblur AVOptions:sigma <float> ..FV.....T. set sigma (from 0 to 1024) (default 0.5)steps <int> ..FV.....T…

vue中路由缓存

vue中路由缓存 问题描述及截图解决思路关键代码及打印信息截图 问题描述及截图 在使用某一平台时发现当列表页码切换后点击某一卡片进入详情页后&#xff0c;再返回列表页时页面刷新了。这样用户每次看完详情回到列表页都得再重新输入自己的查询条件&#xff0c;或者切换分页到…