Skip to content

Commit 9782ff1

Browse files
authored
Merge pull request #5 from AsherJingkongChen/main
Add three different partition schemes
2 parents c7357c4 + c69d2cf commit 9782ff1

File tree

1 file changed

+309
-1
lines changed

1 file changed

+309
-1
lines changed

include/sorting_algorithms/sort.hpp

Lines changed: 309 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#ifndef SORT_HPP
2222
#define SORT_HPP
2323

24+
#define ENABLE_OPTIMIZATION 0
25+
2426
#include <algorithm>
2527
#include <chrono>
2628
#include <cmath>
@@ -39,6 +41,7 @@ template <class ForwardIterator, class Compare>
3941
inline void
4042
bubble_sort_impl(ForwardIterator first, ForwardIterator last, Compare compare, std::forward_iterator_tag) noexcept {
4143
ForwardIterator current, next;
44+
4245
for (; first != last; last = current) {
4346
current = next = first;
4447

@@ -188,7 +191,7 @@ inline void selection_sort(BidirectionalIterator first, BidirectionalIterator la
188191
template <class RandomAccessIterator, class Compare>
189192
inline void
190193
heapify_down(RandomAccessIterator first, RandomAccessIterator last, std::size_t i, Compare compare) noexcept {
191-
auto n = last - first;
194+
std::size_t n = last - first;
192195
while (true) {
193196
auto left = i * 2 + 1;
194197
auto right = i * 2 + 2;
@@ -413,7 +416,11 @@ template <class RandomAccessIterator,
413416
class Compare,
414417
class T = typename std::iterator_traits<RandomAccessIterator>::value_type>
415418
inline void merge_sort_buf(RandomAccessIterator first, RandomAccessIterator last, T* buffer, Compare compare) {
419+
#if ENABLE_OPTIMIZATION
416420
detail::MergeSorter<RandomAccessIterator, Compare, 16>::sort(first, last, buffer, compare);
421+
#else // Not ENABLE_OPTIMIZATION
422+
detail::MergeSorter<RandomAccessIterator, Compare, 0>::sort(first, last, buffer, compare);
423+
#endif // ENABLE_OPTIMIZATION
417424
}
418425

419426
template <class RandomAccessIterator, class T = typename std::iterator_traits<RandomAccessIterator>::value_type>
@@ -857,4 +864,305 @@ inline void bucket_sort(RandomAccessIterator first, RandomAccessIterator last) {
857864

858865
} // namespace alg
859866

867+
// namespace extra
868+
#include <utility>
869+
#include <functional>
870+
#include <iterator>
871+
872+
namespace extra {
873+
874+
struct PartitionScheme {
875+
struct Lomuto {};
876+
struct Hoare {};
877+
struct DutchFlag {};
878+
};
879+
880+
/* @reference Lomuto's Partitioning Scheme
881+
882+
@link https://github.com/raywenderlich/swift-algorithm-club/tree/master/Quicksort
883+
884+
func partitionLomuto<T: Comparable>(_ a: inout [T], low: Int, high: Int) -> Int {
885+
let pivot = a[high]
886+
887+
var i = low
888+
for j in low..<high {
889+
if a[j] <= pivot {
890+
(a[i], a[j]) = (a[j], a[i])
891+
i += 1
892+
}
893+
}
894+
895+
(a[i], a[high]) = (a[high], a[i])
896+
return i
897+
}
898+
899+
func quicksortLomuto<T: Comparable>(_ a: inout [T], low: Int, high: Int) {
900+
if low < high {
901+
let p = partitionLomuto(&a, low: low, high: high)
902+
quicksortLomuto(&a, low: low, high: p - 1)
903+
quicksortLomuto(&a, low: p + 1, high: high)
904+
}
905+
}
906+
*/
907+
template <class BidirectionalIterator, class Compare>
908+
inline BidirectionalIterator
909+
partition_lomuto_scheme(BidirectionalIterator first,
910+
BidirectionalIterator last,
911+
Compare compare) noexcept {
912+
--last;
913+
914+
auto it = first;
915+
for (; first != last; ++first) {
916+
if (compare(*first, *last)) {
917+
std::iter_swap(first, it);
918+
++it;
919+
}
920+
}
921+
922+
std::iter_swap(last, it);
923+
return it;
924+
}
925+
926+
/* @reference Hoare's Partitioning Scheme
927+
928+
@link https://github.com/raywenderlich/swift-algorithm-club/tree/master/Quicksort
929+
930+
func partitionHoare<T: Comparable>(_ a: inout [T], low: Int, high: Int) -> Int {
931+
let pivot = a[low]
932+
var i = low - 1
933+
var j = high + 1
934+
935+
while true {
936+
repeat { j -= 1 } while a[j] > pivot
937+
repeat { i += 1 } while a[i] < pivot
938+
939+
if i < j {
940+
a.swapAt(i, j)
941+
} else {
942+
return j
943+
}
944+
}
945+
}
946+
947+
func quicksortHoare<T: Comparable>(_ a: inout [T], low: Int, high: Int) {
948+
if low < high {
949+
let p = partitionHoare(&a, low: low, high: high)
950+
quicksortHoare(&a, low: low, high: p)
951+
quicksortHoare(&a, low: p + 1, high: high)
952+
}
953+
}
954+
*/
955+
template <class RandomAccessIterator, class Compare>
956+
inline RandomAccessIterator
957+
partition_hoare_scheme(RandomAccessIterator first,
958+
RandomAccessIterator last,
959+
Compare compare) noexcept {
960+
auto i = --first;
961+
auto j = last;
962+
++first;
963+
964+
while (true) {
965+
do { --j; } while (compare(*first, *j));
966+
do { ++i; } while (compare(*i, *first));
967+
968+
if (i >= j) {
969+
return j;
970+
}
971+
std::iter_swap(i, j);
972+
}
973+
}
974+
975+
/* @reference Dutch National Flag Partitioning Scheme
976+
977+
@link https://github.com/raywenderlich/swift-algorithm-club/tree/master/Quicksort
978+
979+
func partitionDutchFlag<T: Comparable>(_ a: inout [T], low: Int, high: Int, pivotIndex: Int) -> (Int, Int) {
980+
let pivot = a[pivotIndex]
981+
982+
var smaller = low
983+
var equal = low
984+
var larger = high
985+
986+
while equal <= larger {
987+
if a[equal] < pivot {
988+
swap(&a, smaller, equal)
989+
smaller += 1
990+
equal += 1
991+
} else if a[equal] == pivot {
992+
equal += 1
993+
} else {
994+
swap(&a, equal, larger)
995+
larger -= 1
996+
}
997+
}
998+
return (smaller, larger)
999+
}
1000+
1001+
func quicksortDutchFlag<T: Comparable>(_ a: inout [T], low: Int, high: Int) {
1002+
if low < high {
1003+
let pivotIndex = random(min: low, max: high)
1004+
let (p, q) = partitionDutchFlag(&a, low: low, high: high, pivotIndex: pivotIndex)
1005+
quicksortDutchFlag(&a, low: low, high: p - 1)
1006+
quicksortDutchFlag(&a, low: q + 1, high: high)
1007+
}
1008+
}
1009+
*/
1010+
template <class RandomAccessIterator, class Compare>
1011+
inline std::pair<RandomAccessIterator, RandomAccessIterator>
1012+
partition_dutchflag_scheme(RandomAccessIterator first,
1013+
RandomAccessIterator last,
1014+
Compare compare) noexcept {
1015+
auto p = *(--last);
1016+
auto e = first;
1017+
1018+
while (e <= last) {
1019+
if (compare(*e, p)) { std::iter_swap(first++, e++); }
1020+
else if (compare(p, *e)) { std::iter_swap(e, last--); }
1021+
else { e++; }
1022+
}
1023+
1024+
return {first, last};
1025+
}
1026+
1027+
namespace detail {
1028+
1029+
#if ENABLE_OPTIMIZATION
1030+
1031+
template <class RandomAccessIterator, class Compare, class _PartitionScheme>
1032+
inline void
1033+
quick_sort_impl_helper(RandomAccessIterator first,
1034+
RandomAccessIterator last,
1035+
Compare compare,
1036+
int recursion_count,
1037+
_PartitionScheme scheme) noexcept {
1038+
if (last - first <= 16) { // small
1039+
alg::insertion_sort(first, last, compare);
1040+
return;
1041+
}
1042+
if (recursion_count <= 0) { // too many divisions
1043+
alg::heap_sort(first, last, compare);
1044+
return;
1045+
}
1046+
1047+
if constexpr (std::is_same<_PartitionScheme, PartitionScheme::Lomuto>::value) {
1048+
auto pivot = partition_lomuto_scheme(first, last, compare);
1049+
quick_sort_impl_helper(first, pivot, compare, recursion_count - 1, scheme);
1050+
quick_sort_impl_helper(++pivot, last, compare, recursion_count - 1, scheme);
1051+
return;
1052+
}
1053+
if constexpr (std::is_same<_PartitionScheme, PartitionScheme::Hoare>::value) {
1054+
auto pivot = partition_hoare_scheme(first, last, compare);
1055+
quick_sort_impl_helper(first, ++pivot, compare, recursion_count - 1, scheme);
1056+
quick_sort_impl_helper(pivot, last, compare, recursion_count - 1, scheme);
1057+
return;
1058+
}
1059+
if constexpr (std::is_same<_PartitionScheme, PartitionScheme::DutchFlag>::value) {
1060+
auto pivots = partition_dutchflag_scheme(first, last, compare);
1061+
quick_sort_impl_helper(first, pivots.first, compare, recursion_count - 1, scheme);
1062+
quick_sort_impl_helper(++pivots.second, last, compare, recursion_count - 1, scheme);
1063+
return;
1064+
}
1065+
}
1066+
1067+
template <class RandomAccessIterator, class Compare, class PartitionScheme>
1068+
inline void quick_sort_impl(RandomAccessIterator first,
1069+
RandomAccessIterator last,
1070+
Compare compare,
1071+
std::random_access_iterator_tag,
1072+
PartitionScheme scheme) {
1073+
auto recursion_count = 2 * alg::detail::log2(last - first);
1074+
quick_sort_impl_helper(first, last, compare, recursion_count, scheme);
1075+
}
1076+
1077+
#else // Not ENABLE_OPTIMIZATION
1078+
1079+
template <class RandomAccessIterator, class Compare, class _PartitionScheme>
1080+
inline void quick_sort_impl(RandomAccessIterator first,
1081+
RandomAccessIterator last,
1082+
Compare compare,
1083+
std::random_access_iterator_tag iter_tag,
1084+
_PartitionScheme scheme) {
1085+
if (first == last || first == --last) { return; }
1086+
++last;
1087+
1088+
if constexpr (std::is_same<_PartitionScheme, PartitionScheme::Lomuto>::value) {
1089+
auto pivot = partition_lomuto_scheme(first, last, compare);
1090+
quick_sort_impl(first, pivot, compare, iter_tag, scheme);
1091+
quick_sort_impl(++pivot, last, compare, iter_tag, scheme);
1092+
return;
1093+
}
1094+
if constexpr (std::is_same<_PartitionScheme, PartitionScheme::Hoare>::value) {
1095+
auto pivot = partition_hoare_scheme(first, last, compare);
1096+
quick_sort_impl(first, ++pivot, compare, iter_tag, scheme);
1097+
quick_sort_impl(pivot, last, compare, iter_tag, scheme);
1098+
return;
1099+
}
1100+
if constexpr (std::is_same<_PartitionScheme, PartitionScheme::DutchFlag>::value) {
1101+
auto pivots = partition_dutchflag_scheme(first, last, compare);
1102+
quick_sort_impl(first, pivots.first, compare, iter_tag, scheme);
1103+
quick_sort_impl(++pivots.second, last, compare, iter_tag, scheme);
1104+
return;
1105+
}
1106+
}
1107+
1108+
#endif // ENABLE_OPTIMIZATION
1109+
1110+
template <class BidirectionalIterator, class Compare, class _PartitionScheme>
1111+
inline void quick_sort_impl(BidirectionalIterator first,
1112+
BidirectionalIterator last,
1113+
Compare compare,
1114+
std::bidirectional_iterator_tag iter_tag,
1115+
_PartitionScheme scheme) noexcept {
1116+
if (first == last || first == --last) { return; }
1117+
++last;
1118+
1119+
if constexpr (std::is_same<_PartitionScheme, PartitionScheme::Lomuto>::value) {
1120+
auto pivot = partition_lomuto_scheme(first, last, compare);
1121+
quick_sort_impl(first, pivot, compare, iter_tag, scheme);
1122+
quick_sort_impl(++pivot, last, compare, iter_tag, scheme);
1123+
return;
1124+
}
1125+
static_assert(!std::is_same<_PartitionScheme, PartitionScheme::Hoare>::value);
1126+
static_assert(!std::is_same<_PartitionScheme, PartitionScheme::DutchFlag>::value);
1127+
}
1128+
1129+
} // namespace detail
1130+
1131+
/**
1132+
* @brief quick sort algorithm
1133+
*
1134+
* @details Uses introsort if the iterator is a random access iterator.
1135+
* Introsort uses insertion sort once the range gets small, and if the recursion depth
1136+
* becomes more than 2*log2(n) it uses heapsort.
1137+
* A random pivot is used for the partitioning if the iterator is a random access iterator.
1138+
* The last element is used as pivot if the iterator is a bidirectional iterator.
1139+
*
1140+
* @param first a bidirectional iterator
1141+
* @param last a bidirectional iterator
1142+
* @param compare a comparison functor
1143+
* @param scheme a partition scheme identifier
1144+
*/
1145+
template <class BidirectionalIterator, class Compare, class PartitionScheme>
1146+
inline void quick_sort(BidirectionalIterator first,
1147+
BidirectionalIterator last,
1148+
Compare compare,
1149+
PartitionScheme scheme) {
1150+
using iter_category = typename std::iterator_traits<BidirectionalIterator>::iterator_category;
1151+
return detail::quick_sort_impl(first, last, compare, iter_category{}, scheme);
1152+
}
1153+
1154+
template <class BidirectionalIterator, class PartitionScheme>
1155+
inline void quick_sort(BidirectionalIterator first, BidirectionalIterator last, PartitionScheme scheme) {
1156+
using value_type = typename std::iterator_traits<BidirectionalIterator>::value_type;
1157+
quick_sort(first, last, std::less<value_type>(), scheme);
1158+
}
1159+
1160+
template <class BidirectionalIterator>
1161+
inline void quick_sort(BidirectionalIterator first, BidirectionalIterator last) {
1162+
using value_type = typename std::iterator_traits<BidirectionalIterator>::value_type;
1163+
quick_sort(first, last, std::less<value_type>(), PartitionScheme::Lomuto());
1164+
}
1165+
1166+
} // namespace extra
1167+
8601168
#endif // SORT_HPP

0 commit comments

Comments
 (0)