- Algoritmos de Leitura e Consulta
Esta categoria de algoritmos examina os elementos dentro de um intervalo sem alterar o estado do contêiner original.
1.1 Localização de Elementos (find)
std::find(inicio, fim, valor): Retorna um iterador para a primeira ocorrência devalor.std::find_if(inicio, fim, predicado): Retorna um iterador para o primeiro elemento que satisfaça a condição lógica.std::find_end(inicio, fim, sub_inicio, sub_fim): Localiza a última ocorrência de uma subsequência.
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> historico = {10, 25, 30, 45, 50};
// Busca por um valor exato
auto ocorrencia = std::find(historico.begin(), historico.end(), 30);
if (ocorrencia != historico.end()) {
std::cout << "Valor localizado: " << *ocorrencia << "\n";
}
// Busca condicional
auto primeiro_par = std::find_if(historico.begin(), historico.end(), [](int n) {
return n % 2 == 0;
});
std::cout << "Primeiro par: " << *primeiro_par << "\n";
return 0;
}
1.2 Contagem de Ocorrências (count)
std::count(inicio, fim, valor): Retorna a quantidade de elementos iguais avalor.std::count_if(inicio, fim, predicado): Conta quantos elementos atendem a uma condição específica.
std::vector<char> sequencia = {'a', 'b', 'a', 'c', 'a'};
int total_a = std::count(sequencia.begin(), sequencia.end(), 'a'); // Resultado: 3
std::vector<int> notas = {8, 5, 9, 4, 7, 6};
int aprovados = std::count_if(notas.begin(), notas.end(), [](int nota) {
return nota >= 6;
}); // Resultado: 4
1.3 Iteração com for_each
Aplica uma função ou functor a cada elemento no intervalo especificado.
std::vector<double> precos = {100.0, 250.0, 75.0};
// Aplicando um desconto de 10% em todos os itens
std::for_each(precos.begin(), precos.end(), [](double& preco) {
preco *= 0.90;
});
1.4 Comparação de Sequências (equal e mismatch)
std::equal(inicio1, fim1, inicio2): Verifica se dois intervalos possuem os mesmos elementos na mesma ordem.std::mismatch(inicio1, fim1, inicio2): Retorna um par de iteradores apontando para a primeira posição onde os intervalos divergem.
std::vector<int> config_a = {1, 2, 3};
std::vector<int> config_b = {1, 2, 4};
bool identico = std::equal(config_a.begin(), config_a.end(), config_b.begin()); // false
auto divergencia = std::mismatch(config_a.begin(), config_a.end(), config_b.begin());
std::cout << "Diferença: " << *divergencia.first << " vs " << *divergencia.second << "\n";
1.5 Validação de Condições Globais
std::all_of: Verdadeiro se todos os elementos satisfizerem o predicado.std::any_of: Verdadeiro se pelo menos um elemento satisfizer o predicado.std::none_of: Verdadeiro se nenhum elemento satisfizer o predicado.
std::vector<int> idades = {20, 25, 30, 18};
bool todos_maiores = std::all_of(idades.begin(), idades.end(), [](int i) { return i >= 18; }); // true
bool algum_idoso = std::any_of(idades.begin(), idades.end(), [](int i) { return i > 60; }); // false
- Algoritmos de Transformação e Mutação
Estes algoritmos alteram diretamente os valores ou a estrutura dos elementos dentro do contêiner.
2.1 Cópia de Dados (copy)
std::copy(inicio, fim, destino): Copia elementos para outro intervalo.std::copy_if: Copia apenas os elementos que passam em um teste lógico.
std::vector<int> origem = {1, 2, 3, 4, 5};
std::vector<int> destino(5);
std::copy(origem.begin(), origem.end(), destino.begin());
std::vector<int> apenas_pares;
std::copy_if(origem.begin(), origem.end(), std::back_inserter(apenas_pares), [](int x) {
return x % 2 == 0;
});
2.2 Transformação (transform)
Aplica uma operação e armazena o resultado, seja no próprio contêiner ou em um novo.
std::vector<int> celsius = {0, 20, 30};
std::vector<int> fahrenheit(celsius.size());
// Conversão de unidade
std::transform(celsius.begin(), celsius.end(), fahrenheit.begin(), [](int c) {
return (c * 9/5) + 32;
});
2.3 Substituição (replace)
std::replace: Troca todas as ocorrências de um valor antigo por um novo.std::replace_if: Substitui elementos com base em uma condição.std::replace_copy: Realiza a substituição durante a cópia, preservando o original.
std::vector<int> status = {1, 0, 1, 0, 2};
// Troca 0 (inativo) por -1
std::replace(status.begin(), status.end(), 0, -1);
// Substitui valores negativos por 0
std::replace_if(status.begin(), status.end(), [](int s) { return s < 0; }, 0);
2.4 O Padrão Erase-Remove
Para remover elementos de um contêiner sequencial, usa-se std::remove seguido do método erase do contêiner.
std::vector<int> tokens = {1, 2, 3, 2, 4};
// Move os '2' para o final e retorna o novo iterador lógico de fim
auto novo_fim = std::remove(tokens.begin(), tokens.end(), 2);
// Redimensiona o contêiner fisicamente
tokens.erase(novo_fim, tokens.end());
2.5 Remoção de Duplicatas Consecutivas (unique)
Remove elementos duplicados que estejam adjacentes. Frequentemente usado após uma ordenação.
std::vector<int> dados = {1, 1, 2, 2, 2, 3};
auto fim_unico = std::unique(dados.begin(), dados.end());
dados.erase(fim_unico, dados.end()); // Resultado: {1, 2, 3}
2.6 Reorganização Geométrica
std::reverse: Inverte a ordem dos elementos.std::rotate: Desloca os elementos de forma que um ponto médio se torne o início.std::shuffle: Randomiza a posição dos elementos usando um gerador de números aleatórios.
std::vector<int> fila = {1, 2, 3, 4, 5};
std::rotate(fila.begin(), fila.begin() + 2, fila.end()); // Resultado: {3, 4, 5, 1, 2}
std::random_device rd;
std::mt19937 gerador(rd());
std::shuffle(fila.begin(), fila.end(), gerador);
- Ordenação e Busca Binária
3.1 Estratégias de Ordenação
std::sort: Ordenação rápida (Introsort), não garante estabilidade.std::stable_sort: Ordenação estável (Mergesort), preserva a ordem relativa de elementos equivalentes.std::partial_sort: Ordena apenas os primeiros N elementos menores/maiores.
std::vector<int> ranking = {50, 20, 40, 10, 30};
std::sort(ranking.begin(), ranking.end(), std::greater<int>()); // Ordem decrescente
std::vector<std::pair<int, std::string>> usuarios = {{2, "B"}, {1, "A"}, {2, "C"}};
std::stable_sort(usuarios.begin(), usuarios.end(), [](const auto& a, const auto& b) {
return a.first < b.first;
}); // Mantém "B" antes de "C" pois ambos têm ID 2
3.2 Seleção Rápida (nth_element)
Reorganiza o intervalo de modo que o elemento na posição especificada seja exatamente o que estaria lá se o intervalo estivesse ordenado. Os elementos à esquerda são menores e à direita são maiores.
std::vector<int> metricas = {9, 1, 8, 2, 7, 3};
// Encontra a mediana (ou o 3º menor elemento)
std::nth_element(metricas.begin(), metricas.begin() + 2, metricas.end());
3.3 Busca em Intervalos Ordenados
Estes algoritmos exigem que o contêiner esteja previamente ordenado.
std::binary_search: Verifica a existência de um valor.std::lower_bound: Encontra o primeiro elemento maior ou igual ao valor.std::upper_bound: Encontra o primeiro elemento estritamente maior que o valor.
std::vector<int> indice = {10, 20, 20, 30, 40};
bool existe = std::binary_search(indice.begin(), indice.end(), 20);
auto primeiro_20 = std::lower_bound(indice.begin(), indice.end(), 20);
auto primeiro_maior_20 = std::upper_bound(indice.begin(), indice.end(), 20);
3.4 Fusão de Dados (merge)
Combina dois intervalos já ordenados em um terceiro, mantendo a ordenação.
std::vector<int> lote_a = {1, 3, 5};
std::vector<int> lote_b = {2, 4, 6};
std::vector<int> consolidado(lote_a.size() + lote_b.size());
std::merge(lote_a.begin(), lote_a.end(), lote_b.begin(), lote_b.end(), consolidado.begin());
- Manipulação de Heap
Permite tratar um contêiner sequencial como uma árvore binária heap (geralmente max-heap).
std::vector<int> tarefas = {3, 1, 4, 1, 5};
std::make_heap(tarefas.begin(), tarefas.end()); // Cria um max-heap
tarefas.push_back(9);
std::push_heap(tarefas.begin(), tarefas.end()); // Insere o 9 mantendo a propriedade do heap
std::pop_heap(tarefas.begin(), tarefas.end()); // Move o maior elemento para o final
int prioridade_maxima = tarefas.back();
tarefas.pop_back(); // Remove o elemento do contêiner
- Extração de Valores Extremos
5.1 Mínimos e Máximos
std::min/std::max: Comparam dois valores ou uma lista de inicialização.std::min_element/std::max_element: Retornam iteradores para os extremos em um intervalo.std::minmax_element: Retorna um par de iteradores (min, max) em uma única passagem.
std::vector<double> latencia = {12.5, 8.2, 15.1, 9.0};
auto [minima, maxima] = std::minmax_element(latencia.begin(), latencia.end());
std::cout << "Min: " << *minima << ", Max: " << *maxima << "\n";
- Operações Numéricas (<numeric>)
6.1 Acumulação e Produtos Internos
std::accumulate: Calcula a soma (ou outra operação binária) de um intervalo.std::inner_product: Calcula o produto escalar entre dois intervalos.
#include <numeric>
std::vector<int> valores = {1, 2, 3, 4};
int soma_total = std::accumulate(valores.begin(), valores.end(), 0);
std::vector<int> pesos = {2, 2, 2, 2};
int soma_ponderada = std::inner_product(valores.begin(), valores.end(), pesos.begin(), 0);
6.2 Geração e Diferenciação
std::iota: Preenche o intervalo com valores sequenciais incrementais.std::partial_sum: Calcula a soma cumulativa (prefix sum).std::adjacent_difference: Calcula a diferença entre elementos adjacentes.
std::vector<int> ids(5);
std::iota(ids.begin(), ids.end(), 100); // 100, 101, 102, 103, 104
std::vector<int> transacoes = {10, 20, 30};
std::vector<int> saldo(3);
std::partial_sum(transacoes.begin(), transacoes.end(), saldo.begin()); // 10, 30, 60
- Geração e Teoria dos Conjuntos
7.1 Preenchimento com Funções (generate)
Atribui valores a um intervalo baseando-se no retorno de uma função geradora.
std::vector<int> sequencia(5);
int contador = 0;
std::generate(sequencia.begin(), sequencia.end(), [&contador]() {
return contador += 5; // 5, 10, 15, 20, 25
});
7.2 Operações de Conjuntos
Requer que ambos os intervalos de entrada estejam ordenados.
std::includes: Verifica se um conjunto é subconjunto de outro.std::set_union,std::set_intersection,std::set_difference,std::set_symmetric_difference: Executam as operações matemáticas clássicas de conjuntos.
std::vector<int> grupo_a = {1, 2, 3, 4};
std::vector<int> grupo_b = {3, 4, 5, 6};
std::vector<int> resultado;
std::set_intersection(grupo_a.begin(), grupo_a.end(),
grupo_b.begin(), grupo_b.end(),
std::back_inserter(resultado)); // Resultado: {3, 4}
- Dúvidas Comuns e Boas Práticas
Qual a diferença prática entre std::sort e std::stable_sort?
O std::sort utiliza o algoritmo Introsort, que é extremamente rápido (complexidade O(n log n)), mas não garante que elementos com chaves idênticas mantenham sua ordem original de inserção. O std::stable_sort utiliza uma variação do Mergesort, garantindo a estabilidade (ordem relativa preservada), mas geralmente exige alocação de memória extra (complexidade de espaço O(n)).
Por que o std::remove não diminui o tamanho do contêiner?
Os algoritmos da biblioteca padrão operam em iteradores e não têm conhecimento sobre a estrutura do contêiner subjacente. O std::remove apenas desloca os elementos não-removidos para o início do intervalo e retorna um novo iterador de "fim lógico". Para liberar a memória e alterar o tamanho real do contêiner, é obrigatório chamar o método erase do contêiner passando esse novo iterador de fim, consolidando o padrão Erace-Remove.
Quais algoritmos exigem que o contêiner esteja ordenado previamente?
Algoritmos que utilizam busca binária ou mesclagem dependem da propriedade de ordenação para funcionar corretamente e manter sua complexidade de tempo otimizada. Isso inclui a família binary_search, lower_bound, upper_bound, além de todas as operações de conjuntos (set_union, set_intersection, etc.) e o std::merge.