list/sort: add initial linked list merge sort
This commit is contained in:
parent
39759805ac
commit
ce03a24f88
@ -419,6 +419,7 @@ list (
|
||||
job/queue.hpp
|
||||
kmeans.hpp
|
||||
library.hpp
|
||||
list/sort.hpp
|
||||
log.cpp
|
||||
log.hpp
|
||||
map/fixed.cpp
|
||||
@ -673,6 +674,7 @@ if (TESTS)
|
||||
job/dispatch
|
||||
job/queue
|
||||
kmeans
|
||||
list/sort
|
||||
map/fixed
|
||||
maths
|
||||
maths/fast
|
||||
|
125
list/sort.hpp
Normal file
125
list/sort.hpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
90
test/list/sort.cpp
Normal file
90
test/list/sort.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#include "list/sort.hpp"
|
||||
#include "random.hpp"
|
||||
#include "tap.hpp"
|
||||
#include "iterator/zip.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <std::size_t N, typename ...ArgsT>
|
||||
static void
|
||||
test_sort (
|
||||
cruft::TAP::logger &tap,
|
||||
std::vector<int> &&data,
|
||||
char const (&fmt)[N],
|
||||
ArgsT&&...args
|
||||
) {
|
||||
// Pre-allocate the array so that we don't get caught by pointer
|
||||
// invalidation as we build the nodes.
|
||||
std::vector<
|
||||
cruft::list::node<int>
|
||||
> nodes (data.size ());
|
||||
|
||||
// Build the list and fold in the supplied data.
|
||||
for (auto [node, value]: cruft::iterator::zip (nodes, data)) {
|
||||
node.next = &node + 1;
|
||||
node.data = value;
|
||||
}
|
||||
|
||||
nodes.back ().next = nullptr;
|
||||
|
||||
|
||||
// Actually do the sort and test the result.
|
||||
auto head = cruft::list::sort (
|
||||
&nodes.front (),
|
||||
&nodes.back ()
|
||||
);
|
||||
|
||||
tap.expect (
|
||||
is_sorted (head),
|
||||
fmt,
|
||||
std::forward<ArgsT> (args)...
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int main ()
|
||||
{
|
||||
cruft::TAP::logger tap;
|
||||
|
||||
// Try sorting a 'large' array of unique, but shuffled, contiguous integers.
|
||||
{
|
||||
// Don't use a power of two here. It won't exercise any edge cases in
|
||||
// the splitting logic of some sorts (like merge).
|
||||
static constexpr int COUNT = 500'000;
|
||||
|
||||
// Create a list of randomly distributed integers. Use a constant seed so
|
||||
// we don't get randomised test results.
|
||||
std::vector<int> data (COUNT);
|
||||
std::iota (std::begin (data), std::end (data), 0);
|
||||
std::shuffle (
|
||||
std::begin (data),
|
||||
std::end (data),
|
||||
std::mt19937 {COUNT}
|
||||
);
|
||||
|
||||
test_sort (tap, std::move (data), "sorting large unique and shuffled array");
|
||||
}
|
||||
|
||||
// Try sorting a small array with identical elements
|
||||
{
|
||||
static constexpr int COUNT = 15;
|
||||
std::vector<int> data (COUNT, COUNT);
|
||||
test_sort (tap, std::move (data), "sorting small identical array");
|
||||
}
|
||||
|
||||
|
||||
return tap.status ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user