基础算法学习:区间合并
区间合并算法用于解决一类经典问题:给定若干个区间,要求将它们合并成最小的非重叠区间。常见的应用场景包括时间区间的合并、区间覆盖问题等。
问题描述
给定一个区间数组,其中每个区间用一对整数表示 [start, end]
。要求合并这些区间,使得合并后的区间没有重叠。
算法步骤
-
排序: 首先,根据每个区间的起始点对区间数组进行排序。如果起始点相同,则按终点排序。
-
合并区间: 遍历排序后的区间列表,使用一个变量保存当前合并的区间:
- 如果当前区间的起始点大于上一个合并区间的终点,说明没有重叠,直接将当前区间加入结果。
- 如果当前区间与上一个合并区间重叠,更新上一个合并区间的终点为当前区间的终点与上一个区间的终点中的最大值。
-
返回合并后的区间列表。
板子例题:
区间合并
给定 n 个区间 [li,ri],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
例如:[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。
输入格式
第一行包含整数 nn。
接下来 nn 行,每行包含两个整数 l 和 r。
输出格式
共一行,包含一个整数,表示合并区间完成后的区间个数。
数据范围
1≤n≤100000,
−109≤li≤ri≤109
输入样例:
5
1 2
2 4
5 6
7 8
7 9
输出样例:
3
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 定义一个 pair 类型,表示区间,first 表示区间的左端点,second 表示右端点
typedef pair<int, int> PII;
int n; // 区间的数量
// merge 函数用于合并重叠的区间,输入参数 interval 为区间集合
void merge(vector<PII> &interval)
{
// 用 ans 存储合并后的区间结果
vector<PII> ans;
// 对区间进行排序,排序规则是先按照区间的左端点升序,
// 如果左端点相同,则按照右端点升序排序
sort(interval.begin(), interval.end()); //! pair排序 优先左端点, 再以右端点排序
// 初始化当前区间的左右边界 st 和 ed
// 初始化为一个很小的值,确保第一次比较时一定满足条件 ed < item.first
int st = -1e9 - 10, ed = -1e9 - 10; //! 只要比 -1e9 小就可以
// 遍历排序后的区间
for(auto item : interval)
{
// 如果当前区间与遍历到的区间没有重叠(即当前区间的结束点小于新区间的起始点)
if(ed < item.first)
{
// 如果 st 不是初始值,则说明前面存在一个合法区间,加入 ans
if(st != -1e9 - 10)
ans.push_back({st, ed}); //! 第一次在这里初始化
// 更新当前区间为新区间
st = item.first;
ed = item.second; //! 第一段区间从这里开始
}
else
{
// 如果有重叠,则更新当前区间的结束点为两个区间结束点的最大值
ed = max(ed, item.second);
}
}
// todo 这个循环结束之后还会剩下一个未加入的区间
// 最后一次合并后的区间需要加入结果中
if(st != -1e9 - 10)
ans.push_back({st, ed}); //! 如果不是空的 那我们就加上一段
// 更新输入的区间集合为合并后的结果
interval = ans;
}
int main(void)
{
// 提高输入输出效率
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 输入区间数量
cin >> n;
// 定义一个 vector 用来存储所有区间
vector<PII> interval;
while(n--)
{
int l, r;
// 输入每个区间的左右端点
cin >> l >> r;
// 将输入的区间加入集合
interval.push_back({l, r});
}
// 调用 merge 函数合并所有重叠区间
merge(interval);
// 输出合并后区间的数量
cout << interval.size() << endl;
return 0;
}