2020-01-18 08:38:21 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
2022-03-25 16:06:11 +00:00
|
|
|
* Copyright (c) 2022, Marc Luqué <marc.luque@outlook.com>
|
2020-01-18 08:38:21 +00:00
|
|
|
*
|
2021-04-22 08:24:48 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 08:38:21 +00:00
|
|
|
*/
|
|
|
|
|
2019-03-09 15:13:08 +00:00
|
|
|
#pragma once
|
|
|
|
|
2022-03-25 16:06:11 +00:00
|
|
|
#include <AK/InsertionSort.h>
|
2019-06-07 09:46:02 +00:00
|
|
|
#include <AK/StdLibExtras.h>
|
|
|
|
|
2019-03-09 15:13:08 +00:00
|
|
|
namespace AK {
|
|
|
|
|
2020-04-18 11:58:49 +00:00
|
|
|
/* This is a dual pivot quick sort. It is quite a bit faster than the single
|
|
|
|
* pivot quick_sort below. The other quick_sort below should only be used when
|
|
|
|
* you are stuck with simple iterators to a container and you don't have access
|
|
|
|
* to the container itself.
|
2022-03-25 16:06:11 +00:00
|
|
|
*
|
|
|
|
* We use a cutoff to insertion sort for partitions of size 7 or smaller.
|
|
|
|
* The idea is to avoid recursion for small partitions.
|
|
|
|
* The value 7 here is a magic number. According to princeton's CS algorithm class
|
|
|
|
* a value between 5 and 15 should work well in most situations:
|
|
|
|
* https://algs4.cs.princeton.edu/23quicksort/
|
2020-04-18 11:58:49 +00:00
|
|
|
*/
|
2022-03-25 16:06:11 +00:00
|
|
|
|
|
|
|
static constexpr int INSERTION_SORT_CUTOFF = 7;
|
|
|
|
|
2020-04-18 11:58:49 +00:00
|
|
|
template<typename Collection, typename LessThan>
|
|
|
|
void dual_pivot_quick_sort(Collection& col, int start, int end, LessThan less_than)
|
|
|
|
{
|
2022-03-25 16:06:11 +00:00
|
|
|
if ((end + 1) - start <= INSERTION_SORT_CUTOFF) {
|
|
|
|
AK::insertion_sort(col, start, end, less_than);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-28 15:41:43 +00:00
|
|
|
while (start < end) {
|
|
|
|
int size = end - start + 1;
|
|
|
|
if (size > 3) {
|
|
|
|
int third = size / 3;
|
|
|
|
if (less_than(col[start + third], col[end - third])) {
|
|
|
|
swap(col[start + third], col[start]);
|
|
|
|
swap(col[end - third], col[end]);
|
|
|
|
} else {
|
|
|
|
swap(col[start + third], col[end]);
|
|
|
|
swap(col[end - third], col[start]);
|
|
|
|
}
|
2021-04-28 14:53:10 +00:00
|
|
|
} else {
|
2021-04-28 15:41:43 +00:00
|
|
|
if (!less_than(col[start], col[end])) {
|
|
|
|
swap(col[start], col[end]);
|
|
|
|
}
|
2021-04-28 14:53:10 +00:00
|
|
|
}
|
2020-04-18 11:58:49 +00:00
|
|
|
|
2021-04-28 15:41:43 +00:00
|
|
|
int j = start + 1;
|
|
|
|
int k = start + 1;
|
|
|
|
int g = end - 1;
|
2020-04-18 11:58:49 +00:00
|
|
|
|
2021-04-28 15:41:43 +00:00
|
|
|
auto&& left_pivot = col[start];
|
|
|
|
auto&& right_pivot = col[end];
|
2020-04-18 11:58:49 +00:00
|
|
|
|
2021-04-28 15:41:43 +00:00
|
|
|
while (k <= g) {
|
2020-04-18 11:58:49 +00:00
|
|
|
if (less_than(col[k], left_pivot)) {
|
|
|
|
swap(col[k], col[j]);
|
|
|
|
j++;
|
2021-04-28 15:41:43 +00:00
|
|
|
} else if (!less_than(col[k], right_pivot)) {
|
|
|
|
while (!less_than(col[g], right_pivot) && k < g) {
|
|
|
|
g--;
|
|
|
|
}
|
|
|
|
swap(col[k], col[g]);
|
|
|
|
g--;
|
|
|
|
if (less_than(col[k], left_pivot)) {
|
|
|
|
swap(col[k], col[j]);
|
|
|
|
j++;
|
|
|
|
}
|
2020-04-18 11:58:49 +00:00
|
|
|
}
|
2021-04-28 15:41:43 +00:00
|
|
|
k++;
|
|
|
|
}
|
|
|
|
j--;
|
|
|
|
g++;
|
|
|
|
|
|
|
|
swap(col[start], col[j]);
|
|
|
|
swap(col[end], col[g]);
|
|
|
|
|
|
|
|
int left_pointer = j;
|
|
|
|
int right_pointer = g;
|
|
|
|
|
|
|
|
int left_size = left_pointer - start;
|
|
|
|
int middle_size = right_pointer - (left_pointer + 1);
|
|
|
|
int right_size = (end + 1) - (right_pointer + 1);
|
|
|
|
|
|
|
|
if (left_size >= middle_size && left_size >= right_size) {
|
|
|
|
dual_pivot_quick_sort(col, left_pointer + 1, right_pointer - 1, less_than);
|
|
|
|
dual_pivot_quick_sort(col, right_pointer + 1, end, less_than);
|
|
|
|
end = left_pointer - 1;
|
|
|
|
} else if (middle_size >= right_size) {
|
|
|
|
dual_pivot_quick_sort(col, start, left_pointer - 1, less_than);
|
|
|
|
dual_pivot_quick_sort(col, right_pointer + 1, end, less_than);
|
|
|
|
start = left_pointer + 1;
|
|
|
|
end = right_pointer - 1;
|
|
|
|
} else {
|
|
|
|
dual_pivot_quick_sort(col, start, left_pointer - 1, less_than);
|
|
|
|
dual_pivot_quick_sort(col, left_pointer + 1, right_pointer - 1, less_than);
|
|
|
|
start = right_pointer + 1;
|
2020-04-18 11:58:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-28 09:53:16 +00:00
|
|
|
template<typename Iterator, typename LessThan>
|
2021-01-31 12:13:49 +00:00
|
|
|
void single_pivot_quick_sort(Iterator start, Iterator end, LessThan less_than)
|
2019-03-09 15:13:08 +00:00
|
|
|
{
|
2021-01-31 12:13:49 +00:00
|
|
|
for (;;) {
|
|
|
|
int size = end - start;
|
|
|
|
if (size <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int pivot_point = size / 2;
|
|
|
|
if (pivot_point)
|
|
|
|
swap(*(start + pivot_point), *start);
|
|
|
|
|
|
|
|
auto&& pivot = *start;
|
|
|
|
|
|
|
|
int i = 1;
|
|
|
|
for (int j = 1; j < size; ++j) {
|
|
|
|
if (less_than(*(start + j), pivot)) {
|
|
|
|
swap(*(start + j), *(start + i));
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
2019-05-18 23:53:51 +00:00
|
|
|
|
2021-01-31 12:13:49 +00:00
|
|
|
swap(*start, *(start + i - 1));
|
|
|
|
// Recur into the shorter part of the remaining data
|
|
|
|
// to ensure a stack depth of at most log(n).
|
|
|
|
if (i > size / 2) {
|
|
|
|
single_pivot_quick_sort(start + i, end, less_than);
|
|
|
|
end = start + i - 1;
|
|
|
|
} else {
|
|
|
|
single_pivot_quick_sort(start, start + i - 1, less_than);
|
|
|
|
start = start + i;
|
2019-05-18 23:53:51 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-09 15:13:08 +00:00
|
|
|
}
|
|
|
|
|
2020-03-03 15:01:37 +00:00
|
|
|
template<typename Iterator>
|
|
|
|
void quick_sort(Iterator start, Iterator end)
|
|
|
|
{
|
2021-01-31 12:13:49 +00:00
|
|
|
single_pivot_quick_sort(start, end, [](auto& a, auto& b) { return a < b; });
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Iterator, typename LessThan>
|
|
|
|
void quick_sort(Iterator start, Iterator end, LessThan less_than)
|
|
|
|
{
|
|
|
|
single_pivot_quick_sort(start, end, move(less_than));
|
2020-03-03 15:01:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Collection, typename LessThan>
|
|
|
|
void quick_sort(Collection& collection, LessThan less_than)
|
|
|
|
{
|
2020-04-18 11:58:49 +00:00
|
|
|
dual_pivot_quick_sort(collection, 0, collection.size() - 1, move(less_than));
|
2020-03-03 15:01:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Collection>
|
|
|
|
void quick_sort(Collection& collection)
|
|
|
|
{
|
2020-04-18 11:58:49 +00:00
|
|
|
dual_pivot_quick_sort(collection, 0, collection.size() - 1,
|
|
|
|
[](auto& a, auto& b) { return a < b; });
|
2020-03-03 15:01:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-09 15:13:08 +00:00
|
|
|
}
|
|
|
|
|
2022-11-26 11:18:30 +00:00
|
|
|
#if USING_AK_GLOBALLY
|
2019-03-09 15:13:08 +00:00
|
|
|
using AK::quick_sort;
|
2022-11-26 11:18:30 +00:00
|
|
|
#endif
|