将一系列排序的元素划分为相邻的组

时间:2018-09-24 18:14:18

标签: c++ sorting c++11 c++17 stl-algorithm

我下面有一个排序的项目列表。我需要确定如何从此列表中选择[包含性,排他性]项目对,以使它们之间的差异超过某个固定值(例如,在下面的示例中为5个)。

因此,这应该导致将列表划分为相邻的范围(不遗漏任何元素)。

我想出了一种蛮力的方法( see live COLIRU demo ),但是我确信必须有一个更优雅的解决方案,而且我可能会遗漏一些边缘情况(例如1包含单个值的列表应产生空对)。我当时在想,可以使用stl范围算法的某些变体std::adjacent_findstd::lower_bound/std::upper_bound的组合来确定这些包含/不包含对-例如,在循环中使用或基于范围的搜索或排序-但我无法弄清楚。

实时搜索值{ 100, 104, 108, 112, 116, 120 },导致以下非重叠范围。请注意,最后一对(相差4(即<5)是一个特例(请参见代码)。

[100,104),[108,112),[116,120)

执行此操作的代码如下:

#include <iostream>
#include <algorithm>
#include <experimental/iterator>
#include <string>
#include <vector>

int main()
{
    std::vector<int> elements = { 100, 104, 108, 112,  116, 120 };
    std::vector<std::pair<int, int>> result;
    auto current = elements.begin();
    while (current != std::prev(elements.cend())) {
        auto next = std::next(current);
        while (((*next - *current) < 5) && (next != std::prev(elements.cend()))) {
            ++next;
        }
        // consider edge case where we are at the end of the list
        if (next != std::prev(elements.cend())) {
            result.emplace_back(*current, *std::prev(next));
        } else {
            result.emplace_back(*current, *next);
        }
        current = next;
    }
    std::transform( result.cbegin(), result.cend(), std::experimental::make_ostream_joiner(std::cout, ","), 
       [](const auto& next){ return std::string("[") + std::to_string(next.first) + ',' +  std::to_string(next.second) + ')'; } );  
}

1 个答案:

答案 0 :(得分:1)

auto next = std::next(current);
while (((*next - *current) < 5) && (next != std::prev(elements.cend()))) {
    ++next;
}

已排序列表中,我们正在寻找第一个元素至少比我们当前元素大5个,对吧?这正是std::lower_bound的目的-它执行二进制搜索而不是线性搜索:

auto next = std::lower_bound(std::next(current), elements.end(), *current + 5);

通过固定循环条件直到列表的 end 而不是末尾的前一个字符(这只是...看起来不对,需要一些严肃的理由)进行组合可以是:

while (current != elements.end()) {
    auto next = std::lower_bound(std::next(current), elements.end(), *current + 5);
    result.emplace_back(*current, *std::prev(next));
    current = next;
}

旁注。这个:

std::transform( result.cbegin(), result.cend(), std::experimental::make_ostream_joiner(std::cout, ","),·
   [](const auto& next){ return std::string("[") + std::to_string(next.first) + ',' +  std::to_string(next.second) + ')'; } );

在我看来,真的没有比这更好的东西了

bool first = true;
for (auto const& [first, second] : result) {
    if (!first) std::cout << ',';
    first = false;
    std::cout << '[' << first << '',' << second << ']';
}

YMMV。我知道人们喜欢说“没有原始循环”,但是我很少看到transform导致可读代码。...