I have implemented a iterator based version of Quicksort, which allows the user to define and pass his own pivot-selection-strategy:
template<typename It>
It Partition(It begin,It end,It pivot)
{
std::swap(*begin, *pivot);
pivot = begin;
It i{ begin }, j = std::next(begin, 1);
for(;j != end; j++)
{
if(*j < *pivot)
std::swap(*j, *++i);
}
std::swap(*pivot, *i);
return i;
}
template<typename It, typename Pivot>
void Quicksort(It begin, It end, Pivot&& GetPivot)
{
if (std::distance(begin, end) == 0)
return;
It pivot{ GetPivot(begin,end) };
It elementAtCorrectPosition = Partition(begin, end,pivot);
Quicksort(begin, elementAtCorrectPosition, GetPivot);
Quicksort(std::next(elementAtCorrectPosition, 1), end, GetPivot);
}
The algorithm can be used in the following way:
std::vector<int> v{ 3, 8, 2, 5, 1, 4, 7, 6 };
auto quickSort = v;
auto stdSort = v;
Quicksort(quickSort.begin(),
quickSort.end(),
[](auto a, auto b) { return a; });
std::sort(stdSort.begin(), stdSort.end());
Assert::IsTrue(quickSort == stdSort);
Where the lambda expression
[](auto a, auto b) { return a; }
is just to demonstrate how a pivot selection strategy can be passed to the algorithm.
Any suggestions that lead to a more robust, concise and readable code are welcome.
Special thanks to @ratchet freak, who discovered an emberassing bug:
The initial implementation only worked for the special case, where the first element is selected as pivot. See above for the corrected version.
swap(*begin, *pivot); pivot = begin;
at the start of partition. Otherwise the pivot could move. \$\endgroup\$std::partition
? \$\endgroup\$