區間合併

區間合併

場景介紹

最近項目中有一個模塊中需要將多個時間段進行合併,這些時間段可能存在交叉、重疊 ,例如時間段:

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++結構體初始化與賦值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章