15 Ordenação
OBS: questões de estabilidade na ordenação são omitidas neste livro.
15.1 Bubble sort
template <std::forward_iterator It>
void bubblesort(It begin, It end)
{
for (; begin != end; --end)
for (auto left = begin, right = std::next(left);
right != end;
left = right++)
if (*left > *right)
std::iter_swap(left, right);
}
Exercícios:
- Altere o código da função
bubblesort
para interromper as passagens quando não houver mais trocas. - Compare o tempo de execução das duas versões da função
bubblesort
para vetores muito grandes.
15.2 Shaker sort
template <std::bidirectional_iterator It>
void shakersort(It begin, It end)
{
while (true)
{
for (auto left = begin, right = std::next(left);
right != end;
left = right++)
if (*left > *right)
std::iter_swap(left, right);
if (begin == --end)
return;
for (auto right = std::prev(end), left = std::prev(right);
right != begin;
right = left--)
if (*left > *right)
std::iter_swap(left, right);
if (++begin == end)
return;
}
}
Exercícios:
- Altere o código da função
shakersort
para interromper as passagens quando não houver mais trocas. - Compare o tempo de execução das duas versões da função
shakersort
para vetores muito grandes. Qual algoritmo se beneficia mais desta interrupção: bubble sort ou shaker sort?
15.5 Shell sort
template <std::integral T> auto knuth_seq_last(T length) {
T h = 1;
while (h < length)
h = 3 * h + 1;
return h / 3;
}
template <std::random_access_iterator It>
void shellsort(It begin, It end)
{
const auto length = std::distance(begin, end);
for (auto h = knuth_seq_last(length); h > 0; h = h / 3)
for (auto it = std::next(begin, h); it != end; ++it)
for (auto right = it, left = std::prev(right, h);
std::distance(begin, right) >= h && *left > *right;
right = std::exchange(left, std::prev(left, h)))
std::iter_swap(left, right);
}
Exercícios:
- Ignorando questões de desempenho, a função
shellsort
poderia ser implementada para iteradores bidirecionais (e sem acesso aleatório). Justifique a necessidade de acesso aleatório para manter as vantagens do shell sort em comparação com o insertion sort.
15.6 Quick sort
template <std::bidirectional_iterator It>
void quicksort(It begin, It end)
{
if (begin == end)
return;
auto left = begin;
auto right = std::prev(end);
const auto pivot_value = *left;
while (true)
{
while (left != right && *right >= pivot_value)
--right;
if (left == right)
break;
std::iter_swap(left++, right);
while (left != right && *left <= pivot_value)
++left;
if (left == right)
break;
std::iter_swap(left, right--);
}
quicksort(begin, left);
quicksort(std::next(left), end);
}
15.7 Merge sort
template <std::forward_iterator It>
void merge(It begin, It mid, It end)
{
std::vector<std::iter_value_t<It>> tmp;
auto it1 = begin, it2 = mid;
while (it1 != mid && it2 != end)
tmp.push_back(std::move(*it1 < *it2 ? *it1++ : *it2++));
std::move(it1, mid, back_inserter(tmp));
std::move(it2, end, back_inserter(tmp));
std::move(tmp.begin(), tmp.end(), begin);
}
template <std::random_access_iterator It>
void mergesort(It begin, It end)
{
const auto length = std::distance(begin, end);
if (length < 2)
return;
const auto mid = std::next(begin, length / 2);
merge_sort(begin, mid);
merge_sort(mid, end);
merge(begin, mid, end);
}
Exercícios:
- Discuta as desvantagens de permitir que a função
mergesort
aceite iteradores unidirecionais ao invés de requerer acesso aleatório. - Implemente o algoritmo merge sort evitando as inúmeras alocações de memória temporária.