区间合并
场景介绍
最近项目中有一个模块中需要将多个时间段进行合并,这些时间段可能存在交叉、重叠 ,例如时间段:
date1: 20180101~20180109
date2: 20180301~20180701
date3: 20180309~20180315
date4: 20180105~20180205
这四个无序的时间段,需要合并成有序的、没有交叉、没有重叠的时间段,合并之后是这样的:
date1: 20180101~20180205
date2: 20180301~20180701
这其实是区间合并问题,有多种解决方法,这里mark两种解决方法。
首先,定义一个区间结构体,包含区间左端点start和区间右端点end两个元素
第一种方法
将所有的区间按照区间左端点start排序,这时所有的区间一个端点已经有序了,只需要比较区间右端点的大小。第一个区间先加入结果集,从第二个区间开始,比较结果集最后一个区间的右端点是否小于当前区间的左端点,如果是,说明当前区间与上一个区间没有交集,当前区间加入结果集;如果不是,说明结果集最后一个区间和当前区间有交集,这时候更新结果集最后一个区间的右端点,其值为当前区间右端点和结果集最后一个区间右端点的最大值。
#include<iostream>
#include<vector>
#include<string>
#include<sstream>
#include<iterator>
#include<algorithm>
using namespace std;
struct Interval {
int start;
int end;
};
vector<string> split(const string &str, const string &delim, vector<string> &elems) { //将分割后的子字符串存储在vector中
if (str == "") {
return elems;
}
string strs = str + delim; //*****扩展字符串以方便检索最后一个分隔出的字符串
size_t size = strs.size();
int pos; // 发现delim的下标
int i = 0; // 待加入elem容器字符串的起始位置
while (i < size) {
pos = strs.find(delim, i); //pos为分隔符第一次出现的位置,从i到pos之前的字符串是分隔出来的字符串
if (pos < size) { //如果查找到,如果没有查找到分隔符,pos为string::npos
string s = strs.substr(i, pos - i);
elems.push_back(s);// 这里没有判断s是否为空,可能会有空串
i = pos + delim.size();
}
}
return elems;
}
// sort的自定义排序函数
bool sortFun(Interval i, Interval j) {
bool res = i.start < j.start;
return res;
}
// 核心部分
void sortInterval(vector<Interval> &list) {
sort(list.begin(), list.end(), sortFun);
vector<Interval> result;
if (list.size() > 0) {
result.push_back(list[0]);
}
for (int i = 1; i < list.size(); i++) {
if (result.back().end < list[i].start) {
result.push_back(list[i]);
} else {
result.back().end = max(list[i].end, result.back().end);
}
}
for (int i = 0; i < result.size(); i++) {
cout << result[i].start << " " << result[i].end << endl;
}
}
void test1() {
vector<Interval> list;
int M;
cin >> M;
string str = "";
for (int i = 0; i < M; i++) {
cin >> str;
vector<string> line;
line = split(str, ";", line);
for (int j = 0; j < line.size(); j++) {
vector<string> ele;
ele = split(line[j], ",", ele);
Interval tmp = {stoi(ele[0]), stoi(ele[1])};
list.push_back(tmp);
}
line.clear();
cout << endl;
}
sortInterval(list);
}
int main() {
test1();
}
Input:
3 // 代表3组数据,每组可以有多个区间,每个区间由;分隔
2,6
1,3
15,18;8,10
Output:
1 6
8 10
15 18
第二种方法
将所有的区间左端点组成的数组starts进行排序,所有区间的右端点组成的数组ends进行排序。如下面4个无序区间
[2,6] [1,3] [15,18] [8,10]
排序后为
index | 0 | 1 | 2 | 3 |
---|---|---|---|---|
starts | 1 | 2 | 8 | 15 |
ends | 3 | 6 | 10 | 18 |
在如果区间左端点数组starts的index+1小于区间右端点的index,说明区间存在交集;如果index+1位置的starts[index+1]大于index位置的ends[index],说明中间不连续了;如上表,index = 1时,starts[1] < ends[0] ,说明有交集,继续。index = 2时,starts[2] > ends[1],说明不连续了,前面1~6是一个区间,后面就从8开始,继续下去
#include<iostream>
#include<vector>
#include<string>
#include<sstream>
#include<iterator>
#include<algorithm>
using namespace std;
struct Interval {
int start;
int end;
};
vector<string> split(const string &str, const string &delim, vector<string> &elems) { //将分割后的子字符串存储在vector中
if (str == "") {
return elems;
}
string strs = str + delim; //*****扩展字符串以方便检索最后一个分隔出的字符串
size_t size = strs.size();
int pos; // 发现delim的下标
int i = 0; // 待加入elem容器字符串的起始位置
while (i < size) {
pos = strs.find(delim, i); //pos为分隔符第一次出现的位置,从i到pos之前的字符串是分隔出来的字符串
if (pos < size) { //如果查找到,如果没有查找到分隔符,pos为string::npos
string s = strs.substr(i, pos - i);
elems.push_back(s);// 这里没有判断s是否为空,可能会有空串
i = pos + delim.size();
}
}
return elems;
}
bool sortFun(Interval i, Interval j) {
bool res = i.start < j.start;
return res;
}
void sortInterval_1(vector<Interval> &list) {
int n = list.size();
int *starts = new int[n];
int *ends = new int[n];
for (int i = 0; i < list.size(); i++) {
Interval tmp = list[i];
starts[i] = tmp.start;
ends[i] = tmp.end;
}
sort(starts, starts + n);
sort(ends, ends + n);
vector<Interval> result;
int cur_start = 1, cur_end = 0, index_start = 0;
while (cur_start < n) {
if (starts[cur_start] > ends[cur_end]) {
int start_tmp = starts[index_start];
int end_tmp = ends[cur_end];
Interval tmp = {start_tmp, end_tmp};
result.push_back(tmp);
index_start = cur_start;
}
cur_start++;
cur_end++;
}
if (cur_end == n - 1) {
Interval tmp = {starts[index_start], ends[n - 1]};
result.push_back(tmp);
}
cout << "res:" << endl;
for (int i = 0; i < result.size(); i++) {
cout << result[i].start << " " << result[i].end << endl;
}
delete[]starts;
delete[]ends;
}
void test1() {
vector<Interval> list;
int M;
cin >> M;
string str = "";
for (int i = 0; i < M; i++) {
cin >> str;
vector<string> line;
line = split(str, ";", line);
for (int j = 0; j < line.size(); j++) {
vector<string> ele;
ele = split(line[j], ",", ele);
Interval tmp = {stoi(ele[0]), stoi(ele[1])};
list.push_back(tmp);
}
line.clear();
cout << endl;
}
sortInterval_1(list);
}
int main() {
test1();
}
Input:
3 // 代表3组数据,每组可以有多个区间,每个区间由;分隔
2,6
1,3
15,18;8,10
Output:
1 6
8 10
15 18
原网址
[1] [LeetCode] Merge Intervals 合并区间
[2] 区间合并问题(merge-intervals)
[3] C++ sort排序函数用法
[4] C/C++结构体初始化与赋值