LeetCode #57 Insert Interval

文章目录

Insert Interval

题目

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).

You may assume that the intervals were initially sorted according to their start times.

Example 1:

Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]

Example 2:

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Output: [[1,2],[3,10],[12,16]]

Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

Difficulty: Hard

分析

这道题,乍看可以暴力求解,直接遍历一遍数组,找到合适的开始区间和结束区间。时间复杂度是 O ( n ) O(n) O(n)

但是考虑到区间的有序性,我们可以进行二分查找,将时间复杂度缩小到 O ( l o g n ) O(logn) O(logn)

我们要考虑到起点/终点位置的各种可能性:

  • 起点/终点处于某个区间内

  • 起点/终点处于两个区间之间

  • 起点/终点处于第一个区间之前

  • 起点/终点处于最后一个区间之后

这样,会组合出不同的情况。

为了方便表示起点/终点的位置,我们设两个变量 startItvendItv ,它们都是某个区间在区间数组 intervals 里的索引。初始值为 0size - 1

进行二分查找,每次找 startItvendItv 的中间区间 medium ,通过比较点和 medium 两端的值,设置新的 startItvendItv

查找完后:

  • 如果点在第一个区间之前,会有 startItv == -1

  • 如果点在最后一个区间之后,会有 startItv == size - 1endItv == size

  • startItv == endItv ,表示点在 intervals[startItv] 这个区间内

  • startItv != endItv ,表示点在 intervals[startItv]intervals[endItv] 这两个区间之间

有进行查找、求 startItvendItv 的函数如下:

    void find(const vector<Interval>& intervals, int num, int& startItv, int& endItv){
    	startItv = 0;
    	endItv = intervals.size() - 1;
    	int result;
    	while(1){
    		int size = endItv - startItv + 1;
    		int medium = startItv + size / 2;
    		const Interval& itv1 = intervals[medium];
    		if(size == 1){
    			if(num < itv1.start) startItv--;
    			else if(itv1.end < num) endItv++;
    			break;
			}
    		const Interval& itv2 = intervals[medium - 1];
    		if(itv1.start <= num) startItv = medium;				// 点在 intervals[medium] 之内或之后
			else if(num <= itv2.end) endItv = medium - 1;	// 点在 intervals[medium - 1] 之内或之前
			else{					// 点在 intervals[medium - 1] 和 intervals[medium] 之间
				startItv = medium - 1;
				endItv = medium;
				break;
			} 
			// cout << num << " : " << startItv << " " << endItv << endl;
		}
	}

接下来,需要由起点 A 和终点 BstartItvendItv 来判断,怎么创造新的区间。

起点要插入的开始区间,由起点的 endItv 确定;终点要插入的结束区间,由终点的 startItv 确定。

startItvendItv 是否相等,决定是否将开始区间的起点/结束区间的终点替换成 A / B

可以直接将开始区间和结束区间合并,除了以下几种特殊情况:

① 终点的 startItv == -1 ,则要在原来的区间数组之前插入新区间。

② 起点的插入区间的索引等于 size ,则要在原来的区间数组之后插入新区间。

③ 起点和终点位于同样的两个区间之间,在这种情况下,起点的 endItv 和终点的 startItv 顺序会相反。即是说,确定的开始区间在结束区间之后。

可以看出,①②的判断条件,本质上也是起点的 endItv 和终点的 startItv 顺序相反。

对①②③,都只需要将新区间插入区间数组的,起点的 endItv 处。

最终,进行插入操作的函数的代码如下:

    vector<Interval> insert(vector<Interval>& intervals, Interval newInterval) {
    	if(intervals.size() == 0){
    		intervals.push_back(newInterval);
    		return intervals;
		}
        int A = newInterval.start, B = newInterval.end;
        int startItvA, endItvA;
        find(intervals, A, startItvA, endItvA);
        int startItvB, endItvB;
        find(intervals, B, startItvB, endItvB);
        
        int insertA;	// 表示起点要插入的开始区间
        int insertB;	// 表示终点要插入的结束区间
        insertA = endItvA;
		insertB = startItvB;
		
		if(insertB < insertA){ 
			intervals.insert(intervals.begin() + insertA, Interval(A, B));
			return intervals;
		}
        
		if(startItvA != endItvA) intervals[insertA].start = A;
		if(startItvB != endItvB) intervals[insertB].end = B;
		intervals[insertA].end = intervals[insertB].end;
		intervals.erase(intervals.begin() + insertA + 1, intervals.begin() + insertB + 1);
		return intervals;
    }

提供几组测试数据,方便后来人:

区间数组 1 :[[1,4][6,12][16,25]]
要插入的新区间:
[-7,0]
[-2,14]
[-6,23]
[-5,31]
[2,14]
[7,23]
[7,33]
[3,15]
[7,23]
[13,33]
[30,39]
[5,5]

区间数组 2 :[]
要插入的新区间:
[-7,0]

结语

这道题虽然难度是 Hard ,提交率也是比较低的 29.9% ,但其实,输入的区间数组的有序性使得它的核心算法很简单,只是插入区间需要考虑各种特殊情况,这可能是提交率低的原因。大概是一道假的难题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]提供了一些关于区间编程的参考链接,其中包括了一些关于区间调度和加权区间调度的内容。引用\[2\]列举了一些与区间相关的几何和统计概念。引用\[3\]提到了区间编程是动态规划问题的一个变种,其中通过选择一个中间点k,并通过遍历所有可能的值来不断最大化价值。 区间编程是一种优化问题,其目标是在给定一组区间的情况下,找到一个最优的区间选择方案。在区间编程中,每个区间都有一个开始时间和结束时间,并且每个区间都有一个关联的权重或价值。目标是选择一组不相交的区间,使得这些区间的总权重或价值最大化。 解决区间编程问题的一种常见方法是使用动态规划。动态规划的基本思想是将问题分解为子问题,并使用已解决的子问题的解来构建更大的问题的解。在区间编程中,可以使用动态规划来计算每个区间的最大权重或价值,并根据这些计算结果来选择最优的区间组合。 具体的区间编程算法和实现细节可以参考引用\[1\]中提供的链接,其中包括了一些关于区间调度和加权区间调度的算法和代码示例。这些资源可以帮助你更深入地了解区间编程的原理和实践应用。 #### 引用[.reference_title] - *1* [Weighted Interval Scheduling VS Interval Scheduling](https://blog.csdn.net/linxid/article/details/79655238)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Interval (mathematics)](https://blog.csdn.net/qq_66485519/article/details/127022620)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Dynamic Programming on Intervals II (LeetCode #312)](https://blog.csdn.net/SpeedowaGONE/article/details/123791749)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值