# std::ranges::nth_element

< cpp‎ | algorithm‎ | ranges

定义于头文件 `<algorithm>` |  |
调用签名 |  |
template< std::random_access_iterator I, std::sentinel_for<I> S, class Comp = ranges::less, class Proj = std::identity > requires std::sortable<I, Comp, Proj> constexpr I nth_element( I first, I nth, S last, Comp comp = {}, Proj proj = {} ); | (1) | (C++20 起)
template< ranges::random_access_range R, class Comp = ranges::less, class Proj = std::identity > requires std::sortable<iterator_t<R>, Comp, Proj> constexpr ranges::borrowed_iterator_t<R> nth_element( R&& r, iterator_t<R> nth, Comp comp = {}, Proj proj = {} ); | (2) | (C++20 起)

重排 `[first, last)` 中的元素使得：

- `nth` 所指向的元素被更改为假设 `[first, last)` 按照 `comp` 与 `proj` 排序则会在该位置出现的元素。
- 所有在此新的 `nth` 元素前的元素小于或等于新的 `nth` 后的元素。即对于每个分别在范围 `[first, nth)` 与 `[nth, last)` 中的迭代器 i 与 j ，表达式 std::invoke(comp, std::invoke(proj, *j), std::invoke(proj, *i)) 求值为 false.
- 若 nth == last 则此函数无效果。

1) 用给定的二元比较函数对象 `comp` 与投影对象 `proj` 比较元素。
2) 同 (1) ，但以 `r` 为范围，如同以 ranges::begin(r) 为 `first` 并以 ranges::end(r) 为 `last` 。

此页面上描述的仿函数实体是 niebloid ，即：

- 调用它们中的任何一者时不可指定显式模板实参。
- 它们无一对实参依赖查找可见。
- 在通常无限定查找寻找函数调用运算符左侧的名称找到它们之一时，它禁止实参依赖查找。

实际上，它们能以函数对象，或以某些特殊编译器扩展实现。

### 参数

first, last | - | 要重排序的元素范围
r | - | 要重排序的元素范围
nth | - | 定义划分点的迭代器
comp | - | 用于比较投影后元素的比较器
proj | - | 应用到元素的投影

### 返回值

1) 等于 `last` 的迭代器。
2) 若 `r` 为左值或拥有 `borrowed_range` 类型则同 (1) 。否则返回 std::ranges::dangling 。

### 复杂度

平均与 ranges::distance(first, last) 成线性。

### 注解

使用的算法常为内省选择，尽管允许其他拥有适合的平均情况复杂度的选择算法。

### 可能的实现

参阅 MSVC STL 与 libstdc++ 中的实现。

### 示例

    #include <algorithm>
    #include <array>
    #include <functional>
    #include <iostream>
    #include <ranges>
    #include <string_view>

    void print(std::string_view rem, std::ranges::input_range auto const& a)
    {
        for (std::cout << rem; const auto e : a)
            std::cout << e << ' ';
        std::cout << "\n";
    }

    int main()
    {
        std::array v{5, 6, 4, 3, 2, 6, 7, 9, 3};
        print("Before nth_element: ", v);

        std::ranges::nth_element(v, v.begin() + v.size()/2);
        print("After nth_element:  ", v);
        std::cout << "The median is: " << v[v.size()/2] << '\n';

        std::ranges::nth_element(v, v.begin() + 1, std::greater<int>());
        print("After nth_element:  ", v);
        std::cout << "The second largest element is: " << v[1] << '\n';
        std::cout << "The largest element is: " << v[0] << "\n\n";

        using namespace std::literals;
        std::array names {
            "Diva"sv, "Cornelius"sv, "Munro"sv, "Rhod"sv,
            "Zorg"sv, "Korben"sv, "Bender"sv, "Leeloo"sv,
        };
        print("Before nth_element: ", names);
        auto fifth_element {std::ranges::next(names.begin(), 4)};
        std::ranges::nth_element(names, fifth_element);
        print("After nth_element:  ", names);
        std::cout << "The 5th element is: " << *fifth_element << '\n';
    }

输出：

    Before nth_element: 5 6 4 3 2 6 7 9 3
    After nth_element:  2 3 3 4 5 6 6 7 9
    The median is: 5
    After nth_element:  9 7 6 6 5 4 3 3 2
    The second largest element is: 7
    The largest element is: 9

    Before nth_element: Diva Cornelius Munro Rhod Zorg Korben Bender Leeloo
    After nth_element:  Diva Cornelius Bender Korben Leeloo Rhod Munro Zorg
    The 5th element is: Leeloo

### 参阅

ranges::max_element (C++20) | 返回范围中的最大元素 (niebloid)
ranges::min_element (C++20) | 返回范围中的最小元素 (niebloid)
ranges::partition (C++20) | 将范围中的元素分为二组 (niebloid)
ranges::partial_sort (C++20) | 排序一个范围的前 N 个元素 (niebloid)
nth_element | 将给定的范围部分排序，确保其按给定元素划分 (函数模板)