-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquick-sort.cpp
172 lines (147 loc) · 6.48 KB
/
quick-sort.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <bits/stdc++.h>
// https://en.wikipedia.org/wiki/Quicksort
template <typename Collection>
class QuickSort {
public:
enum PartitionScheme {
Lomuto, Hoare, Default
};
private:
PartitionScheme partitionScheme;
Collection& collection;
template <typename It>
void sort(It begin, It end) {
// expensive and requires random access iterators:
// it will remain like this to allow differet partition schemes (educational purposes)
if (std::distance(begin, end) <= 1) {
return;
}
// DIVIDE
auto middle = partition(begin, end);
// CONQUER
sort(begin, middle);
sort(partitionScheme == PartitionScheme::Hoare ? middle : std::next(middle), end);
}
template <typename It>
auto partition(It begin, It end) {
switch (partitionScheme) {
case Lomuto:
return partitionLomuto(begin, end);
case Hoare:
return partitionHoare(begin, end);
default:
return partitionDefault(begin, end);
}
}
template <typename It>
It partitionLomuto(It begin, It end) {
auto pivot = getPivot(begin, end);
auto middle = begin;
for (auto it = begin; it != end; std::advance(it, 1)) {
if (*it < pivot) {
std::swap(*middle, *it);
std::advance(middle, 1);
}
}
std::swap(*middle, *std::prev(end));
return middle;
}
template <typename It>
It partitionHoare(It begin, It end) {
// LegacyBidirectionalIterator is needed here
auto pivot = getPivot(begin, end);
std::advance(begin, -1);
while (true) {
for (std::advance(begin, 1); *begin < pivot; std::advance(begin, 1)) {}
for (std::advance(end, -1); *end > pivot; std::advance(end, -1)) {}
if (std::distance(begin, end) <= 0) {
break;
}
std::swap(*begin, *end);
}
// this iterator does not guarantee to "point" to the pivot, we process it again (see conquer step)
return begin;
}
template <typename It>
It partitionDefault(It begin, It end) {
auto pivot = getPivot(begin, end);
auto middle = std::partition(begin, end, std::bind(std::less<>(), std::placeholders::_1, pivot));
std::partition(middle, end, std::bind(std::less_equal<>(), std::placeholders::_1, pivot));
return middle;
}
template <typename It>
auto getPivot(It begin, It end) const {
switch (partitionScheme) {
case Lomuto:
return getPivotLomuto(begin, end);
case Hoare:
return getPivotHoare(begin, end);
default:
return getPivotDefault(begin, end);
}
}
template <typename It>
auto getPivotLomuto(It, It end) const {
// Lomuto's algorithm does not require to choose pivot in this way, but I think this is the most common approach
return *std::prev(end);
}
template <typename It>
auto getPivotHoare(It begin, It end) const {
// Hoare's algorithm does not require to choose pivot in this way, but I think this is the most common approach
return *std::next(begin, std::distance(begin, end) / 2);
}
template <typename It>
auto getPivotDefault(It begin, It end) const {
// after Sedgewick recommendation
auto middle = std::next(begin, std::distance(begin, end) / 2);
std::array<typename Collection::value_type, 3> candidates = {*begin, *middle, *std::prev(end)};
std::sort(std::begin(candidates), std::end(candidates));
return *std::next(std::begin(candidates));
}
public:
QuickSort(Collection& collection, PartitionScheme partitionScheme=Lomuto)
: partitionScheme(partitionScheme), collection(collection) {
sort(std::begin(collection), std::end(collection));
}
};
// tremplate-template only to support custom containerss (and to show off)
template <template<typename> typename Collection=std::vector, typename T=int>
inline Collection<T> quickSort(
Collection<T> collection,
typename QuickSort<Collection<T>>::PartitionScheme partitionScheme=QuickSort<Collection<T>>::PartitionScheme::Lomuto
) {
QuickSort<Collection<T>> a(collection, partitionScheme);
return collection;
}
// see https://github.com/whoan/snip
// snip("cpp/print.hpp")
int main() {
snip::printLoopSpaces(quickSort({5}));
snip::printLoopSpaces(quickSort({5}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({5})));
snip::printLoopSpaces(quickSort({-2}));
snip::printLoopSpaces(quickSort({-2}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({-2})));
snip::printLoopSpaces(quickSort({5, 2}));
snip::printLoopSpaces(quickSort({5, 2}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({5, 2})));
snip::printLoopSpaces(quickSort({2, 2}));
snip::printLoopSpaces(quickSort({2, 2}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({2, 2})));
snip::printLoopSpaces(quickSort({3, 3, 3}));
snip::printLoopSpaces(quickSort({3, 3, 3}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({3, 3, 3})));
snip::printLoopSpaces(quickSort({3, 1, 2}));
snip::printLoopSpaces(quickSort({3, 1, 2}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({3, 1, 2})));
snip::printLoopSpaces(quickSort({5, 2, 1}));
snip::printLoopSpaces(quickSort({5, 2, 1}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({5, 2, 1})));
snip::printLoopSpaces(quickSort({5, 2, 1, 9}));
snip::printLoopSpaces(quickSort({5, 2, 1, 9}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({5, 2, 1, 9})));
snip::printLoopSpaces(quickSort({4, 8, 1, 3, 2, 56, 3, 4, 7, 12, 4, 543, 7, 96, 73, 2, 3, 6, 8, 99, 2}));
snip::printLoopSpaces(quickSort({4, 8, 1, 3, 2, 56, 3, 4, 7, 12, 4, 543, 7, 96, 73, 2, 3, 6, 8, 99, 2}, QuickSort<std::vector<int>>::PartitionScheme::Hoare));
snip::printLoopSpaces(quickSort(std::list<int>({4, 8, 1, 3, 2, 56, 3, 4, 7, 12, 4, 543, 7, 96, 73, 2, 3, 6, 8, 99, 2})));
return 0;
}