libcruft-util/list/sort.hpp

126 lines
2.7 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 "../debug/assert.hpp"
#include <iterator>
#include <utility>
namespace cruft::list {
template <typename ValueT>
struct node {
node *next;
ValueT data;
bool operator< (node const &rhs) const
{
return data < rhs.data;
}
};
template <typename ValueT>
node<ValueT>*
midpoint (node<ValueT> *first, node<ValueT> *last)
{
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;
}
template <typename ValueT>
node<ValueT>*
merge (
node<ValueT> *first,
node<ValueT> *middle,
node<ValueT> *last
) {
CHECK (first);
CHECK (middle);
CHECK (last);
if (first == last)
return first;
auto extract = [&] () {
auto &target = *first < *middle ? first : middle;
auto res = target;
target = target->next;
return res;
};
node<ValueT> *cursor = extract ();
auto head = cursor;
while (first && middle && first != middle && middle != last->next) {
auto target = extract ();
cursor->next = target;
cursor = target;
}
if (first == middle || !first)
cursor->next = middle;
else if (middle == last->next || !middle)
cursor->next = first;
return head;
}
// Merge sort for singly-linked lists
template <typename ValueT>
node<ValueT>*
sort (node<ValueT> *first, node<ValueT> *last)
{
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);
auto b = ::cruft::list::sort (middle, last);
return ::cruft::list::merge (a, b, last);
}
template <typename ValueT>
bool is_sorted (node<ValueT> const *head)
{
if (!head->next)
return true;
auto a = head;
auto b = head->next;
while (b) {
if (*b < *a)
return false;
a = a->next;
b = b->next;
}
return true;
}
}