std::ranges::nth_element(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’… std::ranges::nth_element(3) β€” C++ Standard Library

okay so bestie, buckle up, because we're talking about std::ranges::nth_element from the C++ Standard Library, and honestly? it's giving niche but iconic πŸŽ€

✨ NAME ✨

std::ranges::nth_element β€” yes that's literally it, that's the name, no notes πŸ’…


🌸 SYNOPSIS 🌸

Miss thing lives in the header <algorithm>, and she has two call signatures. Let me lay them out for you, exactly, no gatekeeping 🫢

Signature (1) β€” since C++20:

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 = {} );

Signature (2) β€” also since 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 = {} );

πŸ¦‹ DESCRIPTION πŸ¦‹

So what does she do? She reorders the elements in [first, last) so that all of the following is true, periodt:

  • the element pointed at by nth is changed to whatever element would occur in that position if [first, last) were sorted with respect to comp and proj. like, she's manifesting the position ✨
  • all of the elements before this new nth element are less than or equal to the elements after the new nth element. that is, for every iterator i, j in the ranges [first, nth), [nth, last) respectively, the expression std::invoke(comp, std::invoke(proj, *j), std::invoke(proj, *i)) evaluates to false. yes that's the whole vibe check πŸ’€
  • if nth == last then the function has no effect. she does absolutely nothing, and honestly? valid πŸ™ˆ

Then the two flavors:

  1. elements are compared using the given binary comparison function object comp and projection object proj.
  2. same as (1), but uses r as the range, as if using ranges::begin(r) as first and ranges::end(r) as last.

🧚 the niebloid lore (iykyk)

the function-like entities described on this page are niebloids, that is:

  • explicit template argument lists cannot be specified when calling any of them.
  • none of them are visible to argument-dependent lookup.
  • when any of them are found by normal unqualified lookup as the name to the left of the function-call operator, argument-dependent lookup is inhibited.

in practice, they may be implemented as function objects, or with special compiler extensions. no cap, that's the tea β˜•

πŸ’œ Parameters πŸ’œ

  • first, last β€” the range of elements to reorder
  • r β€” the range of elements to reorder
  • nth β€” the iterator defining the partition point
  • comp β€” comparator used to compare the projected elements
  • proj β€” projection to apply to the elements

πŸ‘‘ Return value πŸ‘‘

  1. an iterator equal to last.
  2. same as (1) if r is an lvalue or of a borrowed_range type. otherwise returns std::ranges::dangling. bestie watch out for the dangling one, that's the plot twist 😭

πŸ”₯ Complexity πŸ”₯

linear in ranges::distance(first, last) on average. she's efficient, she's lowkey humble about it πŸ’…

⭐ Notes ⭐

the algorithm used is typically introselect, although other selection algorithms with suitable average-case complexity are allowed. so like, the vibe is introselect but she's not gonna fight you over it 🌈

πŸŽ€ Possible implementation πŸŽ€

see also the implementation in msvc stl, libstdc++, and libc++: (1) / (2). the girls are all doing it, respectfully 🫢

πŸ“ Example πŸ“

#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';
}

Output β€” she ate and left zero crumbs πŸ”₯

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

🌈 See also 🌈

  • ranges::max_element β€” returns the largest element in a range (C++20) (niebloid)
  • ranges::min_element β€” returns the smallest element in a range (C++20) (niebloid)
  • ranges::partition β€” divides a range of elements into two groups (C++20) (niebloid)
  • ranges::partial_sort β€” sorts the first N elements of a range (C++20) (niebloid)
  • nth_element β€” partially sorts the given range making sure that it is partitioned by the given element (function template)

ok that's the whole page bestie, go forth and partition πŸ’–

and fr fr, this one's brought to you by http://cppreference.com, version 2024.06.10, page std::ranges::nth_element(3) β€” mwah, slay, go read it again if you need to πŸ§šβ€β™€οΈπŸ’…βœ¨

πŸ“¦ where this page comes from