uacs2024

导航

leetcode57. 插入区间

57. 插入区间

微信截图_20251207210320

尝试写过,边界条件处理太笨比了,直接看答案

微信截图_20251207195312

法一:三次遍历法

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        int i = 0, n = intervals.length;
        List<int[]> resArray = new ArrayList<>();
        
        // 第一阶段:处理所有结束点小于新区间开始点的区间(即完全在新区间左侧,无重叠)
        // 这些区间与新区间无任何重叠,直接加入结果列表
        while (i < n && intervals[i][1] < newInterval[0]) {
            resArray.add(intervals[i]);  ++i;
        }
        
        // 第二阶段:处理所有与新区间有重叠的区间,并进行合并
        // 重叠条件:当前区间的开始点 <= 新区间的结束点
        // 注意:在合并过程中,会动态更新新区间的边界,扩展其范围
        while (i < n && newInterval[1] >= intervals[i][0]) {
            // 合并区间的起始点取最小值(扩展左边界)
            newInterval[0] = Math.min(intervals[i][0], newInterval[0]);
            // 合并区间的结束点取最大值(扩展右边界)
            newInterval[1] = Math.max(intervals[i][1], newInterval[1]);
            ++i; // 移动指针,继续检查下一个区间是否仍与合并后的新区间重叠
        }
        // 将合并后的最终新区间加入结果列表
        // 注意:这个add操作只执行一次,因为所有重叠区间已被合并成一个
        resArray.add(newInterval);
        
        // 第三阶段:处理原区间列表中剩余的所有区间(即完全在合并后新区间右侧,无重叠)
        // 这些区间无需任何处理,直接加入结果列表即可
        while (i < n) {
            resArray.add(intervals[i]);
            ++i;
        }
        
        // 将List<int[]>转换为int[][]并返回
        // resArray.size()决定了返回二维数组的行数
        return resArray.toArray(new int[resArray.size()][]);
    }
}

法二:状态标志法

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        // 初始化新区间的左右边界
        int left = newInterval[0];
        int right = newInterval[1];
        // 状态标志:标记新区间是否已被插入结果列表
        boolean placed = false;
        // 使用动态列表存储结果区间
        List<int[]> result = new ArrayList<>();
        
        // 遍历每个原有区间
        for (int[] interval : intervals) {
            if (interval[0] > right) {
                // 情况1:当前区间在新区间右侧且无重叠,相当于上一种方法第三次遍历
                if (!placed) {
                    // 若新区间未插入,先插入合并后的新区间
                    result.add(new int[]{left, right});
                    placed = true; // 更新标志位,表示新区间已插入
                }
                // 插入当前区间(在新区间右侧)
                result.add(interval);
            } else if (interval[1] < left) {
                // 情况2:当前区间在新区间左侧且无重叠,直接插入,相当于法一第一次遍历
                result.add(interval);
            } else {
                // 情况3:当前区间与新区间有重叠,合并区间,相当于法一第二次遍历
                // 扩展新区间的左右边界以覆盖重叠部分
                left = Math.min(left, interval[0]);
                right = Math.max(right, interval[1]);
            }
        }
        
        // 遍历结束后,若新区间仍未插入(说明位于所有区间右侧),则添加到末尾
        if (!placed) {
            result.add(new int[]{left, right});
        }
        
        // 将List转换为二维数组返回
        return result.toArray(new int[result.size()][]);
    }
}

法三:二分查找。某些情况下性能不如三次遍历法

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        // 处理原始区间列表为空的情况
        if (intervals == null || intervals.length == 0) {
            return new int[][]{newInterval};
        }

        int n = intervals.length;
        List<int[]> result = new ArrayList<>();

        // 1. 使用二分查找定位左边界:第一个满足 intervals[i][1] >= newInterval[0] 的区间索引
        int leftIndex = findLeftBoundary(intervals, newInterval[0]);
        
        // 2. 将 leftIndex 之前的所有区间加入结果(这些区间结束点都小于新区间开始点,绝对无重叠)
        for (int i = 0; i < leftIndex; i++) {
            result.add(intervals[i]);
        }

        // 3. 处理重叠区间:合并所有与新区间有重叠的区间
        // 初始化合并区间的左右边界为新区间的值
        int mergeStart = newInterval[0];
        int mergeEnd = newInterval[1];
        
        // 从 leftIndex 开始,遍历所有开始点小于等于新区间结束点的区间(即有重叠的区间)
        int i = leftIndex;
        while (i < n && intervals[i][0] <= newInterval[1]) {
            // 扩展合并区间的边界
            mergeStart = Math.min(mergeStart, intervals[i][0]);
            mergeEnd = Math.max(mergeEnd, intervals[i][1]);
            i++;
        }
        // 将合并后的区间加入结果
        result.add(new int[]{mergeStart, mergeEnd});

        // 4. 添加剩余区间(这些区间开始点都大于合并后区间的结束点,无重叠)
        while (i < n) {
            result.add(intervals[i]);
            i++;
        }

        // 将List转换为二维数组返回
        return result.toArray(new int[result.size()][]);
    }

    /**
     * 二分查找法:寻找第一个满足 intervals[i][1] >= target 的索引 i。
     * 即,第一个结束点大于等于目标值(新区间开始点)的区间位置。
     * 如果所有区间的结束点都小于 target,则返回 n(表示新区间应插入在最末尾)。
     *
     * @param intervals 区间数组
     * @param target 目标值,即新区间的开始点
     * @return 第一个满足条件的区间索引,或 intervals.length
     */
    private int findLeftBoundary(int[][] intervals, int target) {
        int left = 0;
        int right = intervals.length - 1;
        int candidate = intervals.length; // 初始化为数组长度,表示默认在最后插入

        // 标准的二分查找循环
        while (left <= right) {
            int mid = left + (right - left) / 2; // 防止整数溢出的写法
            if (intervals[mid][1] >= target) {
                // 当前中间点满足条件,它可能是一个候选,但我们还要看左边有没有更早满足条件的
                candidate = mid; // 记录候选位置
                right = mid - 1; // 继续在左半部分查找
            } else {
                // 当前中间点不满足条件,说明目标只可能出现在右半部分
                left = mid + 1;
            }
        }
        return candidate; // 返回找到的候选位置
    }
}

 

posted on 2025-12-07 21:24  ᶜʸᵃⁿ  阅读(4)  评论(0)    收藏  举报