Daily bit(e) C++. std::nth_element

Добавлено 24 июля 2026 в 20:32

Daily bit(e) C++ 40: алгоритм с линейной сложностью, который разбивает диапазон вокруг опорной позиции, содержащей элемент, «как если бы он был отсортирован»: std::nth_element.

Daily bit(e) C++

std::nth_element – это алгоритм разбиения с линейной сложностью, который переупорядочивает элементы заданного диапазона таким образом, чтобы элемент под опорным итератором был тем элементом, который был бы там, если бы диапазон был отсортирован.

Это может быть полезно для выбора различных процентилей вне диапазона (например, медианы) без явной сортировки (которая была бы операцией O(n*logn)).

Линейная сложность алгоритма сопровождается нетривиальной постоянной стоимостью, что означает, что если вы ищете крайний процентиль, алгоритм std::partial_sort может быть быстрее.

Алгоритм предоставляет как параллельную версию в C++17, так и версию для диапазонов в C++20.

#include <algorithm>
#include <vector>
#include <string>

std::vector<int> data{8, 6, 2, 4, 3, 5, 9, 1};

std::nth_element(data.begin(), data.begin()+3, data.end());
// *(data.begin()+3) == 4
// потому что отсортированный диапазон будет {1, 2, 3, 4...}

// В версии для диапазонов опорный элемент указывается во втором аргументе
std::ranges::nth_element(data, data.begin()+3); // то же, что и выше

// Потому что алгоритм также разделяет диапазон на части.
// - 1,2,3 гарантировано будут упорядочены перед 4
// - 5,6,8,9 гарантировано будут упорядочены после 4


std::vector<std::string> labels{"dd", "bbbb", "aaaaa", "e", "xxx"};
// Обе версии поддерживают пользовательский компаратор,
// а версия для диапазонов поддерживает проекцию
std::ranges::nth_element(labels, // входной диапазон
    labels.begin()+2,            // опорный элемент
    std::less<>{},               // компаратор
    [](const std::string& s) {   // проекция
        return s.length();
    });
// *(labels.begin()+2) == "xxx" (строка с медианной длиной)

Пример на Compiler Explorer

Теги

C++ / CppDaily bit(e) C++Программирование