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
|
job/queue.hpp
|
||||||
kmeans.hpp
|
kmeans.hpp
|
||||||
library.hpp
|
library.hpp
|
||||||
|
list/sort.hpp
|
||||||
log.cpp
|
log.cpp
|
||||||
log.hpp
|
log.hpp
|
||||||
map/fixed.cpp
|
map/fixed.cpp
|
||||||
@ -673,6 +674,7 @@ if (TESTS)
|
|||||||
job/dispatch
|
job/dispatch
|
||||||
job/queue
|
job/queue
|
||||||
kmeans
|
kmeans
|
||||||
|
list/sort
|
||||||
map/fixed
|
map/fixed
|
||||||
maths
|
maths
|
||||||
maths/fast
|
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