30、插入区间

本文深入探讨了区间合并算法,提供了一种有效的方法来处理区间列表的插入和合并操作,通过示例展示了如何在保持区间有序且不重叠的前提下,正确地插入新的区间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:
给出一个无重叠的 ,按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

示例 1:

输入: intervals = [[1,3],[6,9]], newInterval = [2,5]
输出: [[1,5],[6,9]]
示例 2:

输入: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出: [[1,2],[3,10],[12,16]]
解释: 这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。

本想的是把每个点存储起来,然后放入,但是超时。。。

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
    List<int[]> list = new ArrayList<>();
		int start = Integer.MAX_VALUE;
		int end = Integer.MIN_VALUE;
		if (newInterval[0] == newInterval[1]) {
			for (int[] is : intervals) {
				list.add(is);
			}
			list.add(newInterval);
			int[][] result = new int[list.size()][2];
			for (int i = 0; i < result.length; i++) {
				result[i] = list.get(i);
			}
			Arrays.sort(result, new Comparator<int[]>() {
				public int compare(int[] a, int[] b) {
					return a[0] - b[0];
				}
			});
			return result;
		}

		Set<Integer> set = new HashSet();
		for (int[] integer : intervals) {
			int temstart = integer[0];
			int temend = integer[1];
			start = Math.min(temstart, start);
			end = Math.max(end, temend);
			for (int i = temstart; i < temend; i++) {
				set.add(i);
			}
			if(temstart == temend){
				list.add(integer);
			}
		}
		// 加入新增的区间
		for (int i = newInterval[0]; i < newInterval[1]; i++) {
			
			set.add(i);
		}
        start = Math.min(newInterval[0], start);
		end = Math.max(end, newInterval[1]);
		System.out.println(set);
		for (int i = start; i < end; i++) {
			if (set.remove(i)) {
				int temshuzu[] = new int[2];
				int tem = i;
				while (set.remove(tem - 1)) {
					tem--;
				}
				temshuzu[0] = tem;
				tem = i;
				while (set.remove(tem + 1)) {
					tem++;
				}
				temshuzu[1] = tem + 1;
				list.add(temshuzu);
			}
		}
		int[][] result = new int[list.size()][2];
		for (int i = 0; i < result.length; i++) {
			result[i] = list.get(i);
		}
		Arrays.sort(result, new Comparator<int[]>() {
			public int compare(int[] a, int[] b) {
				return a[0] - b[0];
			}
		});
		return result;
    }
}

使用二分查找法
思路是先找到要插入的地方,然后向后合并,再向前合并

class Solution {
       public int[][] insert(int[][] intervals, int[] newInterval) {
		if (intervals == null || intervals.length == 0) {
			return new int[][] {newInterval};
		}
		// 二分查找找到最后一个intervals[i][0]小于newInterval[0]的位置
		int index = searchLastLessThan(intervals, newInterval);
		ArrayList<int[]> list = new ArrayList<>();
		// newInterval插入到头部
		if (index < 0) {
			list.add(new int[] {newInterval[0], newInterval[1]});
		} else {
			// 把前半部分加入list中
			for (int i = 0; i <= index; i++) {
				list.add(new int[] {intervals[i][0], intervals[i][1]});
			}
			// 将newInterval插入
			int[] inter = list.get(list.size() - 1);
			if (inter[1] < newInterval[0]) {
				list.add(new int[] {newInterval[0], newInterval[1]});
			} else {
				inter[1] = Math.max(inter[1], newInterval[1]);
			}
		}
		// 合并后半部分
		for (int i = index + 1; i < intervals.length; i++) {
			int[] inter = list.get(list.size() - 1);
			if (inter[1] < intervals[i][0]) {
				list.add(new int[] {intervals[i][0], intervals[i][1]});
			} else {
				inter[1] = Math.max(intervals[i][1], inter[1]);
			}
		}
		// 转换结果返回
		int[][] res = new int[list.size()][2];
		for (int i = 0; i < list.size(); i++) {
			res[i][0] = list.get(i)[0];
			res[i][1] = list.get(i)[1];
		}
		return res;
	}

	private int searchLastLessThan(int[][] intervals, int[] newInterval) {
		int left = 0, right = intervals.length - 1, res = -1, mid;
		while (left <= right) {
			mid = left + ((right - left) >> 1);
			if (intervals[mid][0] > newInterval[0]) {
				right = mid - 1;
			} else {
				if (mid == intervals.length - 1 || intervals[mid + 1][0] > newInterval[0]) {
					res = mid; 
					break;
				}
				left = mid + 1;
			}
		}
		return res;
	}
}

或者借鉴这个思路

  public int[][] insert(int[][] intervals, int[] newInterval){
    	List<int[]> res = new LinkedList<int[]>();
    	int i = 0;
    	int n = intervals.length;
    	int nStart = newInterval[0];
    	int nEnd = newInterval[1];
    	//当前区间的end 小于 插入区间的 start,跳过并加入结果集
    	while(i < n && intervals[i][1] < newInterval[0]) {
    		res.add(intervals[i]);
    		i++;
    	}
    	//如果到最后都没找到,把newInterval加到最后
    	if(i == n) {
    		res.add(newInterval);
    		return res.toArray(new int[0][]);
    	}
    	//选择小的 作为新区的 start
    	nStart = Math.min(intervals[i][0],newInterval[0]);
    	//如果当前的start < end 把当前end 和  newInterval的end 比较
    	while(i < n && intervals[i][0] <= newInterval[1]) {
    		nEnd = Math.max(newInterval[1], intervals[i][1]);
    		i++;
    	}
    	res.add(new int[]{nStart,nEnd});
    	
    	//如果后面有没遍历到的 加入最后的结果
    	while(i < n) {
    		res.add(intervals[i++]);
    	}
    	for(int[] x : res) {
    		System.out.println(x[0]+"------"+x[1]);
    	}
    	return res.toArray(new int[0][]);
    }

作者:chen4547
链接:https://leetcode-cn.com/problems/two-sum/solution/java-by-chen4547/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值