【c++】常见区间算法问题整理

一、删除覆盖区间

题目:

给一个区间数组,删除最小个数的、被覆盖的区间,返回最终删除覆盖区间后剩余的区间个数。对于区间[a,b)和[c,d)当且仅当c>=a且d<=b的时候,a-b完全覆盖c-d

区间算法解决的重点

1、升序排列,定出区间之间左端点的关系,只需使用右端点即可得到区间关系,简化操作

2、区间关系有三种:

  • 完全覆盖,即某一区间左右端点都被包含在内
  • 部分覆盖,有一部分伸出来,此时可以扩展现已覆盖的总区间的左右边界
  • 不相交

解题思路:

!注意:在这个题目中,由于升序排列区间之后,能产生覆盖只可能在相邻的区间中产生覆盖,因此,只需要用left和right记录此时最邻近的、已覆盖的最大区间,由此判断下一个区间是否会产生完全覆盖

小坑记录:

bool比较函数传入sort方法的时候,请用static定义

代码如下:

class Solution {
public:
    static bool compare(const vector<int>&a,const vector<int>&b){
        if(a[0]<b[0]) return true;
        else if(a[0]==b[0] && a[1]<b[1]) return true;
        else return false;
    }
    int removeCoveredIntervals(vector<vector<int>>& intervals) {
        //升序排列
        sort(intervals.begin(),intervals.end(),compare);
        //只有当完全被覆盖的时候删除
        //只需要记录left和right即当前已存在的最大区间的左右边界
        //由于本身就是升序排列的,那么,当遇到断开的不交集新区间,重新赋值left、right即可,因为区间升序排列,只可能在邻近区间操作
        

        int left=intervals[0][0],right=intervals[0][1];
        int count=intervals.size();
        for(int i=1;i<intervals.size();i++){
            cout<<left<<" "<<right<<endl;
            //区间关系分为三种情况:
            //1、完全覆盖
            //2、有交集,合并为大区间
            //3、无交集,更新为新区间
            if(intervals[i][0]>=left && intervals[i][1]<=right){
                count--;
            }
            else if(right>=intervals[i][0] && right<=intervals[i][1]){
                //注意,这里之所以不考虑left的关系,是因为left必然小于等于新遍历到的区间->这就是升序排列控制的性质
                right=intervals[i][1];
            }
            else{
                left=intervals[i][0];
                right=intervals[i][1];
            }

        }
        return count;
    }
};

二、合并区间

题目:将区间合并为不相交区间列表

力扣:合并区间

按照上面所说的区间关系,记录left和right,不断更新

在断开区间的时候或者在遍历末尾,将新纪录的区间放入

class Solution {
public:
    static bool compare(const vector<int> & a,const vector<int> &b){
        if(a[0]<b[0]) return true;
        else if(a[0]==b[0] && a[1]<b[1]) return true;
        else return false;
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> mergeInterval;
        //排序
        sort(intervals.begin(),intervals.end(),compare);

        int left=intervals[0][0],right=intervals[0][1];
        for(int i=1;i<intervals.size();i++){
            //部分覆盖
            if(right>=intervals[i][0] && right<=intervals[i][1]) right=intervals[i][1];
            else if(right<intervals[i][0]){//没有交集
                //刷新,存入区间
                vector<int> qujian;
                qujian.push_back(left);
                qujian.push_back(right);
                mergeInterval.push_back(qujian);
                //更新left 和 right
                left=intervals[i][0];
                right=intervals[i][1];
            }
        }

        //最后,记录下来的合并区间,最新的,还没来得及放入到数组中,最后继续放入
        vector<int> qujian;
        qujian.push_back(left);
        qujian.push_back(right);
        mergeInterval.push_back(qujian);

        
        return mergeInterval;
    }
};

三、区间之间的交集

 题目:两个不相交的、已升序排列的区间列表,求出两个区间列表之间存在的相交区间集合(集合左闭右闭)

力扣:区间之间的交集算法

解题思路:

1、双指针法,根据升序、不相交的特性,每次对比,移动具有更小右值的区间列表的指针【这样的移动可能会带来又一次相交

2、计算相交区间

上面讨论了区间有三种宏观上的关系,除了不相交以外,都可以归为相交情况。但是相交区间要求具体的相交范围,可以总结为以下4种可能性

相交区间的关系

对于区间[a1,b1]和[a2,b2]

  • 若a1>=a2并且b1<=b2,则相交区间为[a1,b1]
  • 若a2>=a1并且b2<=b1,则相交区间为[a2,b2]
  • 若a2>=a1并且b1<=b2,则相交区间为[a2,b1]
  • 若a1>=a2并且b2<=b1,则相交区间为[a1,b2]

总结下来如下图所示:

image.png

 由上述规律总结,对于两个区间的交集部分,设为[c1,c2]

c1=max(a1,a2).    c2=min(b1,b2)

代码如下:


class Solution {
public:
    vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList, vector<vector<int>>& secondList) {
        vector<vector<int>> res;
        int len1=firstList.size(),len2=secondList.size();
        //特殊情况,两个区间列表一样没有交集
        if(!len1 || !len2 || firstList[len1-1][1]<secondList[0][0] || secondList[len2-1][1]<firstList[0][0]) return res;

        //双指针法
        int j=0,i=0;
        while(i<len1 && j<len2){
            //如果不是完全不相交
            if(!(firstList[i][0]>secondList[j][1] || firstList[i][1]<secondList[j][0]))  {
                vector<int> seq;
                //相交的区间有四种情况,总结来就是取边界
                int left=max(firstList[i][0],secondList[j][0]);
                int right=min(firstList[i][1],secondList[j][1]);

                seq.push_back(left);
                seq.push_back(right);

                res.push_back(seq);

            }  

            //注意:每个区间列表中都是升序、不相交的;所以下一个区间的左值一定大于上一个区间的有值
            //所以还可能继续被交的【即右值范围更大的留着】,另一个的列表指针移动
            
            if(firstList[i][1]<secondList[j][1]){
                i++;
            }
            else j++;
        }
        return res;
   
    }
};

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个使用C++实现贪心算法解决区间调度问题的示例代码: ```cpp #include <iostream> #include <vector> #include <algorithm> // 区间结构体 struct Interval { int start; int end; }; // 比较函数,根据结束时间排序 bool compareIntervals(Interval a, Interval b) { return a.end < b.end; } // 贪心算法解决区间调度问题 int intervalScheduling(std::vector<Interval>& intervals) { // 按照结束时间排序 std::sort(intervals.begin(), intervals.end(), compareIntervals); int count = 1; // 最少需要一个区间 int endTime = intervals[0].end; // 遍历区间,选择结束时间最早的不冲突区间 for (int i = 1; i < intervals.size(); i++) { if (intervals[i].start >= endTime) { count++; endTime = intervals[i].end; } } return count; } int main() { std::vector<Interval> intervals = {{1, 3}, {2, 4}, {3, 6}, {5, 7}, {6, 8}}; int result = intervalScheduling(intervals); std::cout << "最多可安排的任务数量: " << result << std::endl; return 0; } ``` 在这个示例中,我们定义了一个`Interval`结构体来表示区间,并实现了一个比较函数`compareIntervals`,用于根据结束时间对区间进行排序。`intervalScheduling`函数使用贪心算法解决区间调度问题,首先对区间按照结束时间进行排序,然后从第一个区间开始遍历,选择结束时间最早的不冲突区间,并计数。最后输出最多可安排的任务数量。 在主函数中,我们创建了一个包含几个区间的示例输入,然后调用`intervalScheduling`函数计算结果,并输出最多可安排的任务数量。 注意:这只是一个简单的示例,实际问题可能需要根据具体情况进行更复杂的处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

音仔小瓜皮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值