取成对的数字代表开始/结束,并删除重叠

时间:2018-06-01 00:09:14

标签: c++ arrays sorting c++11

我有一对代表一系列[begin,end]的对。可以假设数组已经按“开始”字段排序。

我想生成一个新数组,删除所有重叠项,并根据需要创建其他对。

例如,假设数组包含以下对:

[1,3],[2,5],[7,15],[8,9],[12,19]

输出应如下:

[1,2],[2,3],[3,5],[7,8],[8,9],[9,12],[12,15],[15,19]

最终,输出数组应该根本不包含任何重叠。

什么是最优解决方案,不超过O(m),其中m是输出数组中所需的条目数?我想我在O(n ^ 2)中看到了一种方法,其中n是输入数组中的条目数,但必须有更好的方法。

最终的实现将在C ++ 11中,使用双对的向量,尽管伪代码解决方案很好。

编辑:

我感谢所有回复,但我会礼貌地请求不要发布任何依赖于特定框架或库的解决方案,除非这些框架是标准c ++ 11的一部分。

2 个答案:

答案 0 :(得分:4)

首先,我将解决相关问题;生成覆盖相同区域但没有邻接或重叠的合并区间。

走输入数组。从第一个元素开始。记录高水位(间隔结束)和低水位(间隔开始)。

继续前进。如果每个元素与间隔重叠,则延伸高水位。如果没有,输出高水位和低水位作为间隔,然后记录一个新的高点和低点。

输入时需要O(n)时间。

必须读取输入的每个元素,因为它们中的任何一个都可以从它们的起始位置到结尾并更改结果。所以这是O-optimal。

这会将间隔合并为您可以制作的最大连续间隔;你想保存所有"边缘"或"接缝"在原来的间隔。要解决您的规范,只需跟踪接缝(按顺序)并在这些接缝处打破生成的间隔。 " Lowater"接缝总会带来越来越多的价值;高水接缝可能不会。因此,一组有序的接缝应该有效。这是O(nlgn)遗憾的是由于集合。

// half open
struct interval {
    int lowater = 0;
    int highwater = 0;
    bool empty() const {
        return lowater == highwater;
    }

    friend std::ostream& operator<<( std::ostream& os, interval i ) {
        return os << "[" << i.lowater << "," << i.highwater << "]";
    }
};


template<class Range, class Out>
void build_intervals( Range&& in, Out out ) {
    std::optional<interval> current;
    std::set<int> seams;

    auto dump_interval = [&](interval i){
        if (i.empty()) return;
        *out = i;
    };
    auto dump_current = [&]{
        if (!current) return;

    //    std::cout << "Dumping " << *current << " with seams: {";
        for (int seam:seams) {
    //        std::cout << seam << ",";
            dump_interval({ current->lowater, seam });
            current->lowater = seam;
        }
    //    std::cout << "}\n";
        dump_interval( *current );
        current = std::nullopt;
        seams.clear();
    };
    for (auto&& e : in) {
        if (current && e.lowater <= current->highwater) {
            seams.insert(e.lowater);
            seams.insert(e.highwater);
    //        std::cout << "No gap between " << *current << " and " << e << "\n";
            current->highwater = (std::max)(e.highwater, current->highwater);
    //        std::cout << "Combined: " << *current << "\n";
            continue;
        }
        if (!current) {
    //        std::cout << "New current " << e << "\n";
        } else {
    //        std::cout << "Gap between " << *current << "  and " << e << "\n";
            dump_current();
        }
        current = e;
        seams.insert(e.lowater);
        seams.insert(e.highwater);
    }
    dump_current();
}

live example

答案 1 :(得分:1)

我提出了这样的事情,如果在O(n)时间完成,只需添加几个。我不确定最后的元素,我的输出:

[1 : 2], [2 : 3], [3 : 5], [7 : 8], [8 : 9], [9 : 12], [12 : 15], [15 : 19]

也许它会有所帮助:

std::vector<std::pair<int, int>> noOverlaps(std::vector<std::pair<int, int>>& input) {
    if (input.size() <= 1) {
        return input;
    }
    std::vector<std::pair<int, int>> result;
    result.push_back(input[0]);

    for (int i = 1; i < input.size(); ++i) {
        //If overlap
        if (input[i].first < result.back().second) {
            auto lastOne = result.back();
            result.pop_back();
            result.push_back(std::make_pair(lastOne.first, input[i].first));
            if (lastOne.second > input[i].second) {
                result.push_back(std::make_pair(input[i].first, input[i].second));
                result.push_back(std::make_pair(input[i].second, lastOne.second));
            } else {
                result.push_back(std::make_pair(input[i].first, lastOne.second));
                result.push_back(std::make_pair(lastOne.second, input[i].second));
            }
        } else {
            result.push_back(input[i]);
        }
    }
    return result;
}

更新1 正如上面的评论所指出的那样,多个重叠区间不起作用,因此可以通过吞咽相互包含的间隔并运行相同的算法来改善上述解决方案:

std::vector<std::pair<int, int>> noOverlaps(std::vector<std::pair<int, int>>& origInput) {
    if (origInput.size() <= 1) {
        return origInput;
    }
    std::vector<std::pair<int, int>> result;
    std::vector<std::pair<int, int>> input;
    input.push_back(origInput[0]);

    for (int i = 1; i < origInput.size(); ++i) {
        if (input[i-1].first <= origInput[i].first && input[i-1].second >= origInput[i].second) {
            continue;
        }
        input.push_back(origInput[i]);
    }

    result.push_back(input[0]);

    for (int i = 1; i < input.size(); ++i) {
        //If overlap
        if (input[i].first < result.back().second) {
            auto lastOne = result.back();
            result.pop_back();
            result.push_back(std::make_pair(lastOne.first, input[i].first));
            if (lastOne.second > input[i].second) {
                result.push_back(std::make_pair(input[i].first, input[i].second));
                result.push_back(std::make_pair(input[i].second, lastOne.second));
            } else {
                result.push_back(std::make_pair(input[i].first, lastOne.second));
                result.push_back(std::make_pair(lastOne.second, input[i].second));
            }
        } else {
            result.push_back(input[i]);
        }
    }
    return result;
}

但是这需要2xO(n)空间复杂度并且代码不是很好。

所以我想知道这还不够:

std::vector<std::pair<int, int>> noOverlaps2(std::vector<std::pair<int, int>>& origInput) {
    if (origInput.size() <= 1) {
        return origInput;
    }
    int low = origInput[0].first, high = origInput[0].second;
    std::vector<std::pair<int, int>> result;

    for (int i = 1; i < origInput.size(); ++i) {
        if (high < origInput[i].first) {
            result.emplace_back(low, high);
            low = origInput[i].first;
            high = origInput[i].second;
        } else {
            high = std::max(origInput[i].second, high);
        }
    }
    result.emplace_back(low, high);

    return result;
}

对于您的数据,它提供输出:[1 : 5], [7 : 19]但它消除了重叠。