【困难】力扣算法题解析LeetCode352:将数据流变为多个不相交区间

关注文末推广名片,即可免费获得本题测试源码

题目来源:LeetCode 352. 将数据流变为多个不相交区间

问题抽象: 设计一个 SummaryRanges 类,用于将数据流中的整数动态合并为 多个不相交区间,支持以下核心操作:

  1. 核心功能

    • 初始化:创建空区间集合(无任何区间);
    • 添加整数addNum(int val) 将新整数 val 插入数据流;
    • 获取区间getIntervals() 返回当前所有不相交区间的有序列表(格式 List<int[]>)。
  2. 区间合并规则

    • 新区间生成:若 val 不与任何区间相邻,则生成新区间 [val, val]
    • 区间扩展:若 val 与某区间端点相邻(如 [1,3] 插入 4 → 扩展为 [1,4]);
    • 区间合并:若 val 连接两个区间(如 [1,3][5,7] 插入 4 → 合并为 [1,7])。
  3. 状态维护

    • 去重处理:重复添加的 val 直接忽略(不改变区间结构);
    • 有序存储:区间按起始值升序排列(无重叠);
    • 高效合并:避免每次遍历所有区间(利用有序性二分查找插入点)。
  4. 边界处理

    • 空数据流getIntervals() 返回空列表;
    • 孤立值addNum(5) 后区间为 [[5,5]]
    • 连续扩展
      • 初始:[[1,1]]
      • addNum(2)[[1,2]]
      • addNum(4)[[1,2],[4,4]]
      • addNum(3)[[1,4]](连接 [1,2][4,4]);
    • 大值域val 范围为 32 位有符号整数。

类定义

class SummaryRanges {
    public SummaryRanges() {}  // 初始化  
    public void addNum(int val) {}  // 添加新整数  
    public int[][] getIntervals() {} // 返回区间数组(如 [[1,3],[5,7]])  
}  

输入/输出约束

  • 操作调用次数 ≤ 3×10^4
  • getIntervals 调用次数 ≥ 10 次(需高效实现);
  • 区间输出格式:每个区间 [start, end] 满足 start ≤ end,且全局按 start 升序排列。

解题思路

题目要求设计一个数据结构,能够高效地将输入的整数合并成不相交区间,并支持查询当前区间集合。核心思路如下:

  1. 数据结构选择:使用TreeMap存储区间,以区间左端点为键,右端点为值。TreeMap支持高效查询(O(log n))和有序遍历。
  2. 添加数值逻辑
    • 检查重复:通过floorEntry(val)查找小于等于val的最大区间,若该区间的右端点≥val,说明val已包含在区间中。
    • 合并左区间:若左邻居的右端点+1等于val,则扩展该区间右端点至val
    • 合并右区间:若右邻居的左端点-1等于val,则删除原右区间,插入新区间[val, 原右端点]
    • 合并左右区间:若val同时与左右邻居相邻,则合并左右区间为一个新区间。
    • 独立区间:若无法合并,插入新区间[val, val]
  3. 查询区间逻辑:直接遍历TreeMap,按左端点升序输出所有区间。

代码实现(Java版)🔥点击下载源码

class SummaryRanges {
    private TreeMap<Integer, Integer> intervals; // 存储区间:key=左端点, value=右端点

    public SummaryRanges() {
        intervals = new TreeMap<>();
    }
    
    public void addNum(int val) {
        // 检查是否已存在于区间
        Map.Entry<Integer, Integer> left = intervals.floorEntry(val);
        if (left != null && left.getValue() >= val) 
            return;
        
        // 检查左右邻居
        boolean mergeLeft = (left != null && left.getValue() + 1 == val);
        boolean mergeRight = intervals.containsKey(val + 1);
        
        // 合并左右邻居
        if (mergeLeft && mergeRight) {
            int rightEnd = intervals.get(val + 1);
            intervals.put(left.getKey(), rightEnd);
            intervals.remove(val + 1);
        } 
        // 只合并左邻居
        else if (mergeLeft) {
            intervals.put(left.getKey(), val);
        } 
        // 只合并右邻居
        else if (mergeRight) {
            int rightEnd = intervals.get(val + 1);
            intervals.remove(val + 1);
            intervals.put(val, rightEnd);
        } 
        // 独立区间
        else {
            intervals.put(val, val);
        }
    }
    
    public int[][] getIntervals() {
        int[][] res = new int[intervals.size()][2];
        int idx = 0;
        for (var entry : intervals.entrySet()) {
            res[idx][0] = entry.getKey();
            res[idx][1] = entry.getValue();
            idx++;
        }
        return res;
    }
}

代码说明

  1. 初始化TreeMap初始化用于存储区间。
  2. addNum方法
    • 去重检查floorEntry(val)查找可能包含val的左邻居区间。
    • 合并逻辑
      • 同时合并左右邻居:删除右邻居,扩展左邻居的右端点。
      • 合并左邻居:直接扩展左邻居的右端点。
      • 合并右邻居:删除原右邻居,插入以val为左端点的区间。
      • 无法合并:插入新区间[val, val]
  3. getIntervals方法:遍历TreeMap,按左端点升序输出所有区间。
  4. 复杂度
    • 时间复杂度addNum()O(log n)TreeMap操作),getIntervals()O(n)
    • 空间复杂度O(n),存储所有区间。

提交详情(执行用时、内存消耗)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达文汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值