libcruft-util/list/sort.hpp

210 lines
5.4 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "node.hpp"
#include "../debug/assert.hpp"
#include <iterator>
#include <utility>
namespace cruft::list {
template <typename NodeT>
std::size_t
size (NodeT const *head)
{
std::size_t count = 0;
while (head) {
++count;
head = head->next;
}
return count;
}
/// Returns the last valid node of a linked list. ie, the node where the
/// successor is null.
///
/// The results are undefined if the head node is null (as it is
/// impossible to return a valid node if none are valid).
template <typename NodeT>
NodeT*
end (NodeT *head)
{
CHECK (head);
while (head->next)
head = head->next;
return head;
}
/// Find the midpoint of the list specified by the sub-list [first, last].
///
/// On even length lists the earlier middle node will be returned.
///
/// Uses the fast/slow walking method. Double advancing the fast iterator,
/// and singly advancing the slow iterator.
///
/// This necessarily walks the entire list, so the performance may be
/// quite low.
///
/// NOTE: `last` is a valid node and forms part of the sub-list.
template <typename NodeT>
NodeT*
midpoint [[gnu::nonnull]] (
NodeT *first,
NodeT *last
) {
// Pre-increment the fast node to trigger a preference for the earlier
// of the middle nodes.
auto slow = first;
auto fast = first->next;
while (fast != last->next) {
fast = fast->next;
if (fast == last->next)
return slow;
fast = fast->next;
slow = slow->next;
}
return slow;
}
/// Merges a list that has been divided into two sorted halves.
///
/// This operation will mutate the `next` pointers but will not mutate
/// the inner data objects.
///
/// NOTE: The list is specified inclusively, and the `last` node is a
/// valid piece of data.
///
/// Returns the first node of the newly sorted list. The caller _must_
/// store this value otherwise a portion of the list will be lost.
template <typename NodeT, typename ComparatorT>
NodeT*
merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
NodeT *first,
NodeT *middle,
NodeT *last,
ComparatorT &&cmp
) {
// clang#xxxx clang mistakenly warns that the values of 'first' and
// 'middle' will be unconditionally true on the first iteration of the
// while loop.
#if defined(COMPILER_CLANG)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
#endif
CHECK (first);
CHECK (middle);
CHECK (last);
if (first == last)
return first;
// Return a reference to the lower of 'first' and 'middle', and
// advance the cursor.
auto extract = [&first, &middle, &cmp] () {
auto &target = cmp (first, middle) ? first : middle;
auto res = target;
target = target->next;
return res;
};
NodeT *cursor = extract ();
auto head = cursor;
// Keep adding the lowest node to the new list until we exhaust one
// or both of our sources.
while (first && middle && first != middle && middle != last->next) {
auto target = extract ();
cursor->next = target;
cursor = target;
}
// Append the remaining data to the new list.
if (first == middle || !first)
cursor->next = middle;
else if (middle == last->next || !middle)
cursor->next = first;
#if defined(COMPILER_CLANG)
#pragma GCC diagnostic pop
#endif
return head;
}
/// An in-place merge sort for singly linked lists.
template <typename NodeT, typename ComparatorT>
NodeT*
sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
NodeT *first,
NodeT *last,
ComparatorT &&cmp
) {
if (first == last)
return first;
auto a_end = ::cruft::list::midpoint (first, last);
auto middle = a_end->next;
a_end->next = nullptr;
auto a = ::cruft::list::sort (first, a_end, cmp);
auto b = ::cruft::list::sort (middle, last, cmp);
return ::cruft::list::merge (a, b, last, cmp);
}
template <typename NodeT>
NodeT*
sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
NodeT *head,
NodeT *tail
) {
return sort (
head,
tail,
node::value_comparator (std::less<> ())
);
}
/// Tests if a singly linked list is sorted.
template <typename NodeT>
bool
is_sorted [[gnu::nonnull]] (
NodeT const *head
) {
if (!head->next)
return true;
auto a = head;
auto b = head->next;
while (b) {
if (b->data < a->data)
return false;
a = a->next;
b = b->next;
}
return true;
}
}