区间合并

区间合并

场景介绍

最近项目中有一个模块中需要将多个时间段进行合并,这些时间段可能存在交叉、重叠 ,例如时间段:

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]
排序后为

index0123
starts12815
ends361018

在如果区间左端点数组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++结构体初始化与赋值

<think>好的,用户想找关于区间合并的例题或练习题。首先,我需要回忆一下区间合并的基本概念和应用场景。区间合并通常用于将重叠或相邻的区间合并成一个更大的区间,这在处理时间调度、区间覆盖等问题时非常有用。 接下来,我需要查看用户提供的引用资料。引用[2]提到合并区间和最大数,说明区间合并是排序算法的一种应用。引用[4]给出了一个具体的例子,输入是[[1,3],[2,6],[8,10],[15,18]],输出合并后的结果。引用[5]提到使用贪心算法来合并区间,通常的做法是先排序再合并。 用户需要的是例题或练习题,所以应该提供几个不同难度的例子,并附上解题思路和代码实现。比如,可以包括基础题如LeetCode 56,进阶题如插入新区间,以及变形题如计算合并后的区间长度总和。此外,用户可能需要Python的示例代码,因为之前的示例中有Python代码。 还需要注意格式要求,行内数学公式用$...$,独立公式用$$...$$,并且正确使用LaTeX语法。例如,区间可以写成$[a_i, b_i]$。同时,每个问题后面需要生成相关问题,并正确添加引用标识。 检查引用的来源,确保每个引用对应的内容准确。例如,引用[4]对应LeetCode 56题,引用[5]对应贪心算法的应用。在回答中引用这些内容时,需要在末尾添加对应的标识,如[^4]或[^5]。 最后,确保回答结构清晰,分点列出例题,每个例题包含问题描述、示例、解题思路和代码实现。代码部分需要用Python,并且按照格式要求使用代码块。同时,生成的相关问题要围绕区间合并的应用、优化和扩展题型展开。</think>### 区间合并算法详解与例题 区间合并的核心是通过排序和贪心策略,将重叠或相邻的区间合并为更大的区间。以下是具体示例和实现方法: --- #### **例题1:基础合并区间(LeetCode 56)** **问题描述** 给定一个区间集合 `intervals`,合并所有重叠的区间。 **示例输入与输出** 输入:$[[1,3],[2,6],[8,10],[15,18]]$ 输出:$[[1,6],[8,10],[15,18]]$ **解题思路** 1. **排序**:按区间左端点升序排列,使重叠区间连续。 2. **合并**:遍历排序后的区间,若当前区间左端点 ≤ 前一个区间的右端点,则合并为 $[\text{prev\_left}, \max(\text{prev\_right}, \text{current\_right})]$[^4]。 **Python实现** ```python def merge(intervals): if not intervals: return [] intervals.sort(key=lambda x: x[0]) # 按左端点排序 merged = [intervals[0]] for current in intervals[1:]: last = merged[-1] if current[0] <= last[1]: last[1] = max(last[1], current[1]) # 合并区间 else: merged.append(current) return merged ``` --- #### **例题2:插入并合并区间(LeetCode 57)** **问题描述** 在已排序的非重叠区间列表中插入一个新区间合并后返回结果。 **示例输入与输出** 输入:$[[1,3],[6,9]]$,插入 $[2,5]$ 输出:$[[1,5],[6,9]]$ **解题思路** 1. **插入**:将新区间加入列表并重新排序。 2. **合并**:沿用基础合并方法。 **Python实现** ```python def insert(intervals, new_interval): intervals.append(new_interval) intervals.sort(key=lambda x: x[0]) return merge(intervals) # 复用例题1的合并函数 ``` --- #### **例题3:计算合并后的总长度** **问题描述** 给定区间列表,计算合并后所有区间覆盖的总长度。 **示例输入与输出** 输入:$[[1,4],[2,5],[7,10]]$ 输出:$8$(合并后为 $[1,5]$ 和 $[7,10]$,总长度 $4+3=7$) **解题思路** 1. **合并区间**:先合并所有重叠区间。 2. **累加长度**:对每个合并后的区间计算 $b_i - a_i$ 并求和[^2]。 **Python实现** ```python def total_covered_length(intervals): merged = merge(intervals) # 复用例题1的合并函数 return sum(end - start for start, end in merged) ``` --- ### **相关问题** 1. 如何优化区间合并的时间复杂度? 2. 区间合并算法在日程安排问题中的应用? 3. 如何处理包含离散点的区间合并问题? [^1]: 算法练习题--滑动窗口算法详细解析 : 合并区间、最大数 : LeetCode 56合并区间示例 : 贪心算法合并区间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值