区间合并详解——超级容易理解版本

在面临多个区间的时候,我们经常需要对有交集的区间进行合并这一操作,现在对这一操作进行详细讲解。

为了能更好理解,我们首先熟悉一下我们接下来要使用的一些模板类。

1.为了方便存储每一个区间的左右边界,我们将引入c++中的模板类pair。

typedef pair<int,int> PII;

using PII = pair<int, int>;//这样实现的操作效果一样

        我们使用pair模板类关联两个int型的整数,这两个int型整数分别表示区间左边界和右边界。为了方便使用,我们用typedef关键字定义,给它起名为PII。

2.pair方便我们存储一个区间,但我们需要存储多个区间,这时我们需要引入动态数组vector,它可以根据需要增长或缩小,它是C++标准模板库(STL)中的一个容器类,用于存储同一类型的元素的序列。

typedef pair<int,int> PII;

vector<PII> sesg;

我们定义了一个vector,名为sesg,它存储的元素类型为PII,即pair<int, int>。这里是一个简单的例子,展示了如何向sesg中添加元素以及如何访问这些元素:

#include <iostream>  
#include <vector>  
#include <utility> // 包含pair的定义  
using namespace std;
 
typedef pair<int, int> PII; // 定义别名PII  
  
int main() {  
    vector<PII> sesg; // 声明一个存储PII类型的vector  
  
    // 向sesg中添加元素  
    sesg.push_back(PII(1, 2)); // 使用别名PII添加元素  
    sesg.push_back({3, 4});     // 使用初始化列表添加元素  
    sesg.emplace_back(5, 6);    // 使用emplace_back原地构造元素  
  
    // 遍历sesg并打印元素  
    for (auto pair : sesg) {  
        cout << "(" << pair.first << ", " << pair.second << ")" << endl;  
    }  
  
    return 0;  
}

好了,理解这些,让我们开始大展伸手吧!

区间和并的关键是如何查找到两个及多个有交集的区间并将其合并,我们先举例五个区间,我简单的表示一下:

五个区间的左右边界分别是:
(1,2)  (2,4)  (5,6)  (7,8)  (7,9)

1 2 3 4 5 6 7 8 9

1-2
  2---4
        5-6
            7-8
            7---9

按照上图,合并有交集的区间后的区间为(1,4) (5,6) (7,9) 这三个区间。

我们的具体操作为:

  1. 我们需要做的操作是将每个区间排序,排序的方式是按照左边界大小来排序的,也就是说,(1,2)区间排在(2,4)区间前面,左值小的排在前面。
  2. 遍历存放区间且排好序的数组sesg,如果遍历到当前区间的右边界大于下一个区间的左边界,则说明这两个区间有交集,合并两个区间,以此直到遍历完每一个区间。

好,接下来是具体的代码实现:

void merge(vector<PII> &sesg) // 定义一个名为merge的函数,接受一个类型为vector<PII>的引用参数sesg
{
    vector<PII> res; // 创建一个空的vector<PII>对象res,用于存储合并后的结果

    sort(sesg.begin(), sesg.end()); // 对输入的segs进行排序

    int st = -2e9, ed = -2e9; // 初始化一个区间用来比较,区间起始点st和结束点ed为-2e9(一个非常小的负数)

    for (auto seg : sesg) // 遍历排序后的sesg中的每个区间seg
        if (ed < seg.first) // 如果当前结束点ed小于下一个区间的起始点seg.first
        {
            if (st != -2e9) res.push_back({st, ed}); // 如果起始点st不等于-2e9,将当前的起始点st和结束点ed添加到res中
            st = seg.first, ed = seg.second; // 更新起始点st和结束点ed为当前区间的起始点seg.first和结束点seg.second
        }
        else ed = max(ed, seg.second); // 如果当前区间结束点ed不小于下一个区间的起始点seg.first,则更新结束点ed为当前结束点ed和下一个区间的结束点seg.second中的较大值

    if (st != -2e9) res.push_back({st, ed}); // 如果最后一个区间没有被添加到结果向量res中,将其添加进去

    sesg = res; // 将结果res赋值给输入参数segs,实现原地修改
}
  1. 首先对输入的区间列表segs进行排序,按照区间的起始点从小到大的顺序排列。
  2. 初始化起始点st和结束点ed为一个非常小的负数。
  3. 遍历排序后的区间列表segs中的每个区间seg,判断当前结束点ed是否小于下一个区间的起始点seg.first
  4. 如果当前结束点ed小于下一个区间的起始点seg.first,说明当前区间已经结束,需要将起始点st和结束点ed添加到结果向量res中,并将起始点st和结束点ed更新为下一个区间的起始点seg.first和结束点seg.second
  5. 如果当前结束点ed不小于下一个区间的起始点seg.first,说明两个区间有重叠部分,只需要更新结束点ed为当前结束点ed和下一个区间的结束点seg.second中的较大值即可。
  6. 遍历结束后,如果最后一个区间没有被添加到结果向量res中,将其添加进去。
  7. 最后将结果向量res赋值给输入参数segs,实现原地修改。

讲完具体操作,我们该上手做题了


给定 n个区间 [li,ri],要求合并所有有交集的区间。

注意如果在端点处相交,也算有交集。

输出合并完成后的区间个数。

例如:[1,3][1,3] 和 [2,6][2,6] 可以合并为一个区间 [1,6][1,6]。

输入格式

第一行包含整数 n。

接下来 n行,每行包含两个整数 l 和 r。

输出格式

共一行,包含一个整数,表示合并区间完成后的区间个数。

数据范围

1≤n≤1000001
−10^9 ≤ li ≤ ri ≤ 10^9

输入样例:
5
1 2
2 4
5 6
7 8
7 9
输出样例:
3

希望大家再尝试之后再来理解答案

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;//定义一个数对型的结构

void merge(vector<PII> &sesg)
{
    vector<PII> res;//申请一个数对型数组备用
    sort(sesg.begin(),sesg.end());//排数数对数组,排序方式是按照数对的左值来排序的
    int st=-2e9,ed=-2e9;//定义两个值与每个区间的边界值进行比较
    for(auto ses:sesg)//遍历数对型数组sesg
    {
        if(ses.first>ed)//当遍历的区间的左值大于当前区间的右值,则说明这两个区间没有交集
        {
            if(st!=-2e9) res.push_back({st,ed});//当前区间不为数对型数组中第一个区间时,将该区间存入备用的数对数组中
            st=ses.first,ed=ses.second;//更新当前区间的边界值为遍历区间的边界值
        }
        else ed=max(ed,ses.second);//遍历的区间的左值小于当前区间的右值,则这两个区间有交集,合并这两个区间,并且将合并区间的右值更新
    }
    if(st!=-2e9) res.push_back({st,ed});//在上述遍历中,最后一个区间并没有更新到备用数对数组中,所以之一步骤将弥补这一操作
    sesg=res;//将备用数组赋值给数对数组sesg
}
int main()
{
    int n;
    cin>>n;
    vector<PII> sesg;//申请一个数对型数组
    while(n--)
    {
        int l,r;
        cin>>l>>r;
        sesg.push_back({l,r});//将每个区间的边界存入数对数组中
    }
    merge(sesg);
    cout<<sesg.size();
    return 0;
}

到这里关于区间合并的操作就讲完了,以上如果有错误欢迎大家指正,本篇内容的思想来自yxc的算法基础课

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值