std::sort_heap(3)

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

πŸ’… std::sort_heap β€” the C++ Standard Library page, but make it make sense

okay bestie, welcome to std::sort_heap(3) from the C++ Standard Library β€” yes the manual page is literally titled std::sort_heap, iconic naming, no notes πŸŽ€


✨ NAME ✨

std::sort_heap β€” it's std::sort_heap, bestie. That's the name. We love a self-titled moment πŸ’…


🌸 Synopsis 🌸

okay so this whole thing lives in the header <algorithm> β€” write that down 🫢

There are two overloads, and both are constexpr since C++20:

template< class RandomIt >                               (1)
(constexpr since C++20)
void sort_heap( RandomIt first, RandomIt last );

template< class RandomIt, class Compare >
void sort_heap( RandomIt first, RandomIt last, Compare   (2)
(constexpr since C++20)
comp );

So what's the vibe? It converts the heap [first, last) into a sorted range. The heap property is no longer maintained after this β€” like, it's gone, that's the whole point, the heap becomes a sorted range and moves on with its life 😌

Now the tea on each overload:

  1. The heap is with respect to operator< (until C++20) / std::less{} (since C++20), and will be sorted with respect to operator< (until C++20) / std::less{} (since C++20).
  2. The heap is with respect to comp, and will be sorted with respect to comp.
🚨 Behavior is UNDEFINED if any of these are true β€” like, not "oops it's a little weird", actual undefined behavior, do not pass go: - [first, last) is not a heap. - The type of *first is not Swappable. (until C++11) - RandomIt is not ValueSwappable. - The type of *first is not MoveConstructible. (since C++11) - The type of *first is not MoveAssignable.

πŸ¦‹ Parameters πŸ¦‹

  • first, last β€” the heap to be sorted ✨
  • comp β€” a comparison function object (i.e. an object that satisfies the requirements of Compare) which returns true if the first argument is less than the second.

The signature of the comparison function should be equivalent to the following:

bool cmp(const Type1& a, const Type2& b);

and here's the part everyone skips, so let me say it louder πŸ“’ β€” while the signature does not need to have const&, the function must not modify the objects passed to it, and it must be able to accept all values of type (possibly const) Type1 and Type2 regardless of value category. So like, Type1& is not allowed, nor is Type1 unless for Type1 a move is equivalent to a copy (since C++11). and the types Type1 and Type2 must be such that an object of type RandomIt can be dereferenced and then implicitly converted to both of them πŸ’–


🧚 Type requirements 🧚

  • RandomIt must meet the requirements of LegacyRandomAccessIterator.
  • Compare must meet the requirements of Compare.

no shortcuts, bestie, the iterator has to be legit 😀


πŸ”₯ Complexity πŸ”₯

Given N as std::distance(first, last) (the original writes it as NN because math rendering is a whole thing):

  1. At most 2Nβ‹…log(N) β€” that's 2*N*Β·log(N) β€” comparisons using operator< (until C++20) / std::less{} (since C++20).
  2. At most 2Nβ‹…log(N) β€” again, 2*N*Β·log(N) β€” applications of the comparison function comp.

πŸ‘‘ Possible implementation πŸ‘‘

here's what it could look like under the hood, and honestly it ate:

sort_heap (1)

template<class RandomIt>
void sort_heap(RandomIt first, RandomIt last)
{
    while (first != last)
        std::pop_heap(first, last--);
}

sort_heap (2)

template<class RandomIt, class Compare>
void sort_heap(RandomIt first, RandomIt last, Compare comp)
{
    while (first != last)
        std::pop_heap(first, last--, comp);
}

like?? just popping the heap until it's empty. main character energy fr fr 🌟


πŸ“ Example πŸ“

// Run this code β€” yes, the original says that, go run it bestie πŸ’»

#include <algorithm>
#include <iostream>
#include <string_view>
#include <vector>

void println(std::string_view fmt, const auto& v)
{
    for (std::cout << fmt; const auto &i : v)
        std::cout << i << ' ';
    std::cout << '\n';
}

int main()
{
    std::vector<int> v{3, 1, 4, 1, 5, 9};

    std::make_heap(v.begin(), v.end());
    println("after make_heap, v: ", v);

    std::sort_heap(v.begin(), v.end());
    println("after sort_heap, v: ", v);
}

Output:

after make_heap, v: 9 4 5 1 1 3
after sort_heap, v: 1 1 3 4 5 9

look at that glow-up 😭 from chaos to ascending order, we love to see it ✨


πŸ™ˆ Defect reports πŸ™ˆ

The following behavior-changing defect reports were applied retroactively to previously published C++ standards β€” yes, they went back and fixed things, kings:

  • LWG 2444 β€” applied to C++98. Behavior as published: at most Nβ‹…log(N) comparisons were allowed. Correct behavior: increased to 2Nβ‹…log(N).

so the comparison budget got doubled, and honestly? deserved πŸ’œ


🌈 See also 🌈

  • is_heap (C++11) β€” checks if the given range is a max heap (function template)
  • is_heap_until (C++11) β€” finds the largest subrange that is a max heap (function template)
  • make_heap β€” creates a max heap out of a range of elements (function template)
  • pop_heap β€” removes the largest element from a max heap (function template)
  • push_heap β€” adds an element to a max heap (function template)
  • ranges::sort_heap (C++20) β€” turns a max heap into a range of elements sorted in ascending order (niebloid)

the whole heap friend group, assembled 🫢 go read them too, iykyk.


okay that's the page!! πŸ’– this gem came from http://cppreference.com, version 2024.06.10, and the page itself is std::sort_heap(3) β€” mwah, go sort something, bestie πŸ’…βœ¨πŸŽ€

πŸ“¦ where this page comes from