Domine os Algoritmos da Biblioteca Padrão do C++

Algoritmos de Consulta e Leitura

Esta categoria engloba funções que examinam os elementos de um container sem alterar seu estado interno. Elas são fundamentais para validações e buscas.

Busca e Contagem

Para localizar elementos específicos, utilizamos std::find para valores exatos ou std::find_if quando uma condição lógica precisa ser satisfeita. A função std::find_end é útil para identificar a última ocorrência de uma subsequência.

std::vector<int> dados = {10, 20, 30, 40, 50};

// Buscar o valor 30
auto it = std::find(dados.begin(), dados.end(), 30);
if (it != dados.end()) {
    std::cout << "Valor encontrado: " << *it << std::endl;
}

// Buscar primeiro valor múltiplo de 20
auto it2 = std::find_if(dados.begin(), dados.end(), [](int n) {
    return n % 20 == 0;
});

Para quantificar ocorrências, std::count verifica igualdade direta, enquanto std::count_if aplica um predicado personalizado.

Comparação e Verificação

A função std::equal valida se dois intervalos possuem conteúdos idênticos. Já std::mismatch retorna um par de iteradores apontando para a primeira divergência entre dois ranges.

Para validações booleanas em massa, existem std::all_of (todos satisfazem), std::any_of (algum satisfaz) e std::none_of (nenhum satisfaz).

std::vector<int> valores = {2, 4, 6, 8};
bool todosPares = std::all_of(valores.begin(), valores.end(), [](int n) {
    return n % 2 == 0;
});

Algoritmos de Modificação de Sequência

Estas funções alteram o conteúdo dos containers ou movem elementos entre eles.

Cópia e Transformação

std::copy transfere elementos para outro destino. Se houver uma condição de filtro, std::copy_if deve ser usado. O std::back_inserter é frequentemente utilizado para expandir containers dinamicamente durante a cópia.

Para aplicar operações matemáticas ou lógicas durante a transferência, std::transform é a escolha ideal, suportando operações unárias ou binárias (entre dois ranges).

std::vector<int> origem = {1, 2, 3, 4};
std::vector<int> destino(4);

// Elevar ao quadrado
std::transform(origem.begin(), origem.end(), destino.begin(), [](int n) {
    return n * n;
});

Substituição e Remoção

Substituições podem ser feitas in-place com std::replace ou std::replace_if. A variante std::replace_copy gera um novo container com as alterações.

Um ponto crucial é o comportamento do std::remove. Ele não diminui o tamanho do container; apenas move os elementos válidos para o início e retorna um iterador para o novo fim lógico. Para efetivar a remoção de memória, deve-se chaining com erase.

std::vector<int> lista = {1, 9, 2, 9, 3};
// Move os 9s para o final
auto novo_fim = std::remove(lista.begin(), lista.end(), 9);
// Reduz o tamanho real do vector
lista.erase(novo_fim, lista.end());

Outras utilidades incluem std::unique (remove duplicatas adjacentes), std::reverse (inverte ordem), std::rotate (rotaciona elementos) e std::shuffle (embaralha aleatoriamente).

Ordenação e Estruturas de Dados

Algoritmos de Sort

O std::sort é geralmente implementado via Introsort, oferecendo performance média O(n log n) sem garantir estabilidade. Quando a ordem relativa de elementos equivalentes precisa ser mantida, std::stable_sort é necessário. Para otimizar casos onde apenas os menores elementos interessam, std::partial_sort ordena apenas uma parte do range.

std::nth_element reorganiza o container de forma que o elemento na posição n esteja na posição correta como se estivesse ordenado, com todos à esquerda sendo menores e à direita maiores.

Busca Binária e Merge

Em containers já ordenados, std::binary_search verifica existência rapidamente. std::lower_bound e std::upper_bound retornam iteradores para posições específicas relativas ao valor buscado.

Para unir dois ranges ordenados mantendo a ordem, utiliza-se std::merge.

Operações de Heap

A STL permite tratar um range como um heap (pilha de prioridade). std::make_heap organiza os elementos, std::push_heap insere um novo eleemnto mantendo a propriedade, e std::pop_heap move o maior elemento para o final. std::sort_heap ordena o range baseado na estrutura de heap.

Utilitários Numéricos e de Conjunto

Localizados principalmente em <numeric>, estes algoritmos lidam com acumulação e geração.

  • std::accumulate: Soma ou reduz elementos de um range.
  • std::inner_product: Calcula o produto interno de dois ranges.
  • std::iota: Preenche um range com valores sequencialmente crescentes.
  • std::partial_sum: Gera um range com as somas parciais acumuladas.

Para operações de teoria dos conjuntos em ranges ordenados, existem std::set_union, std::set_intersection, std::set_difference e std::set_symmetric_difference.

std::vector<int> a = {1, 2, 3};
std::vector<int> b = {3, 4, 5};
std::vector<int> resultado;

// União de conjuntos
std::set_union(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(resultado));

Práticas e Considerações Técnicas

A escolha entre std::sort e std::stable_sort depende da necessidade de preservar a ordem original de elementos iguais. A estabilidade tem um custo de memória adicional.

O padrão "erase-remove" é obrigatório para remover elementos de containers sequenciais como std::vector, pois os algoritmos de remoção da STL operam apenas em iteradores e não conhecem o tamanho do container.

Algoritmos de busca binária e operações de conjunto exigem que os dados de entrada estejam previamente ordenados, caso contrário o comportamento é indefinido ou incorreto.

Tags: C++ STL Algoritmos cpp11 containers

Publicado em 8-27 09:58