Estes algoritmos examinam elementos em contêineres sem alterar seu conteúdo.
1.1. Pesquisa: find e find_if
find(inicio, fim, valor): Retorna um iterador para a primeira ocorrência devalorno intervalo[inicio, fim). Se não encontrado, retornafim.find_if(inicio, fim, predicado): Retorna um iterador para o primeiro elemento que satisfaz a condição definida porpredicado.find_end(b1, e1, sub_b, sub_e): Busca a última ocorrência de uma sub-sequência definida por[sub_b, sub_e)dentro de[b1, e1).
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numeros = {10, 30, 50, 70, 90};
// Encontrar o valor 50
auto it_valor = std::find(numeros.begin(), numeros.end(), 50);
if (it_valor != numeros.end()) {
std::cout << "Valor encontrado: " << *it_valor << std::endl; // Saída: 50
}
// Encontrar o primeiro elemento maior que 60
auto it_condicao = std::find_if(numeros.begin(), numeros.end(), [](int x) {
return x > 60;
});
if (it_condicao != numeros.end()) {
std::cout << "Primeiro elemento > 60: " << *it_condicao << std::endl; // Saída: 70
}
// Encontrar uma sub-sequência
std::vector<int> sub = {30, 50};
auto it_sub = std::find_end(numeros.begin(), numeros.end(), sub.begin(), sub.end());
if (it_sub != numeros.end()) {
std::cout << "Sub-sequência começa no índice: " << (it_sub - numeros.begin()) << std::endl; // Saída: 1
}
return 0;
}
1.2. Contagem: count e count_if
count(inicio, fim, valor): Retorna o número de elementos iguais avalorno intervalo[inicio, fim).count_if(inicio, fim, predicado): Retorna o número de elementos que satisfazem a condiçãopredicado.
#include <vector>
#include <algorithm>
#include <numeric> // Necessário para std::count e std::count_if em algumas implementações
std::vector<int> dados = {1, 2, 3, 2, 4, 2, 5};
int contagem_dois = std::count(dados.begin(), dados.end(), 2); // Resultado: 3
int contagem_impares = std::count_if(dados.begin(), dados.end(), [](int x) {
return x % 2 != 0;
}); // Resultado: 4 (1, 3, 5)
1.3. Iteração: for_each
Aplica uma função a cada elemento do intervalo [inicio, fim).
#include <vector>
#include <algorithm>
std::vector<int> valores = {1, 2, 3, 4, 5};
std::for_each(valores.begin(), valores.end(), [](int& elem) {
elem *= 3; // Multiplica cada elemento por 3
});
// valores agora é {3, 6, 9, 12, 15}
1.4. Comparação: equal e mismatch
equal(b1, e1, b2): Verifica se os elementos no intervalo[b1, e1)são iguais aos elementos a partir deb2.mismatch(b1, e1, b2): Retorna um par de iteradores apontando para o primeiro par de elementos diferentes nos dois intervalos.
#include <vector>
#include <algorithm>
#include <iostream>
std::vector<int> lista_a = {10, 20, 30};
std::vector<int> lista_b = {10, 20, 40};
std::vector<int> lista_c = {10, 20, 30, 40};
// Comparar lista_a com os primeiros 3 elementos de lista_c
bool sao_iguais = std::equal(lista_a.begin(), lista_a.end(), lista_c.begin());
// sao_iguais será true
// Encontrar a primeira diferença entre lista_a e lista_b
auto diferenca = std::mismatch(lista_a.begin(), lista_a.end(), lista_b.begin());
if (diferenca.first != lista_a.end()) {
std::cout << "Primeira diferença: " << *diferenca.first << " vs " << *diferenca.second << std::endl; // Saída: 30 vs 40
}
1.5. Verificação Condicional: all_of, any_of, none_of
all_of(inicio, fim, predicado): Retornatruese todos os elementos satisfazempredicado.any_of(inicio, fim, predicado): Retornatruese pelo menos um elemento satisfazpredicado.none_of(inicio, fim, predicado): Retornatruese nenhum elemento satisfazpredicado.
#include <vector>
#include <algorithm>
std::vector<int> numeros = {2, 4, 6, 8};
bool todos_pares = std::all_of(numeros.begin(), numeros.end(), [](int x) { return x % 2 == 0; }); // true
bool algum_impar = std::any_of(numeros.begin(), numeros.end(), [](int x) { return x % 2 != 0; }); // false
bool nenhum_negativo = std::none_of(numeros.begin(), numeros.end(), [](int x) { return x < 0; }); // true
Estes algoritmos alteram o conteúdo dos contêineres.
2.1. Cópia: copy e copy_if
copy(inicio_orig, fim_orig, inicio_dest): Copia elementos do intervalo de origem para o destino. O destino deve ter espaço suficiente.copy_if(inicio_orig, fim_orig, inicio_dest, predicado): Copia apenas os elementos que satisfazempredicado.
#include <vector>
#include <algorithm>
#include <iterator> // Para std::back_inserter
std::vector<int> fonte = {1, 2, 3, 4, 5, 6};
std::vector<int> destino(fonte.size());
// Copiar todos os elementos
std::copy(fonte.begin(), fonte.end(), destino.begin());
// destino agora é {1, 2, 3, 4, 5, 6}
// Copiar apenas os elementos pares para um novo vetor
std::vector<int> pares;
std::copy_if(fonte.begin(), fonte.end(), std::back_inserter(pares), [](int x) {
return x % 2 == 0;
});
// pares agora é {2, 4, 6}
// std::back_inserter é útil pois gerencia o redimensionamento do contêiner destino.
2.2. Transformação: transform
Aplica uma operação a cada elemento e armazena o resultado em outro local.
#include <vector>
#include <algorithm>
std::vector<int> numeros = {1, 2, 3, 4};
std::vector<int> quadrados(numeros.size());
// Calcula o quadrado de cada elemento
std::transform(numeros.begin(), numeros.end(), quadrados.begin(), [](int x) {
return x * x;
});
// quadrados agora é {1, 4, 9, 16}
// Soma elementos de dois vetores
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {4, 5, 6};
std::vector<int> soma(v1.size());
std::transform(v1.begin(), v1.end(), v2.begin(), soma.begin(), [](int x, int y) {
return x + y;
});
// soma agora é {5, 7, 9}
2.3. Substituição: replace, replace_if, replace_copy
replace(inicio, fim, antigo, novo): Substitui todas as ocorrências deantigopornovo.replace_if(inicio, fim, predicado, novo): Substitui elementos que satisfazempredicadopornovo.replace_copy(inicio_orig, fim_orig, inicio_dest, antigo, novo): Copia elementos, substituindoantigopornovo, sem modificar o original.
#include <vector>
#include <algorithm>
#include <iterator>
std::vector<int> dados = {1, 5, 2, 5, 3, 5};
// Substituir todos os 5 por 50
std::replace(dados.begin(), dados.end(), 5, 50);
// dados agora é {1, 50, 2, 50, 3, 50}
// Substituir elementos maiores que 10 por 0
std::vector<int> mais_dados = {5, 12, 3, 15, 8};
std::replace_if(mais_dados.begin(), mais_dados.end(), [](int x) {
return x > 10;
}, 0);
// mais_dados agora é {5, 0, 3, 0, 8}
// Copiar e substituir 3 por 300
std::vector<int> copia_substituida;
std::replace_copy(dados.begin(), dados.end(), std::back_inserter(copia_substituida), 50, 300);
// copia_substituida agora é {1, 300, 2, 300, 3, 300}
2.4. Remoção Lógica: remove, remove_if (e erase)
Estes algoritmos movem os elementos a serem "removidos" para o final do intervalo e retornam um iterador para o novo fim lógico. Eles não alteram o tamanho do contêiner; erase deve ser usado para a remoção física.
remove(inicio, fim, valor): Move todos os elementos iguais avalorpara o final.remove_if(inicio, fim, predicado): Move elementos que satisfazempredicadopara o final.
#include <vector>
#include <algorithm>
std::vector<int> sequencia = {1, 2, 3, 2, 4, 2, 5};
// Mover todos os 2s para o final
auto novo_fim_logico = std::remove(sequencia.begin(), sequencia.end(), 2);
// sequencia agora é {1, 3, 4, 5, 2, 2, 2} (o conteúdo após novo_fim_logico é indefinido)
// Remover fisicamente os elementos "removidos"
sequencia.erase(novo_fim_logico, sequencia.end());
// sequencia agora é {1, 3, 4, 5}
// Combinação comum: remover elementos ímpares
sequencia = {1, 2, 3, 4, 5, 6};
sequencia.erase(std::remove_if(sequencia.begin(), sequencia.end(), [](int x) {
return x % 2 != 0; // Remover ímpares
}), sequencia.end());
// sequencia agora é {2, 4, 6}
2.5. Remoção de Duplicatas Contíguas: unique
Remove elementos duplicados adjacentes. Requer que o contêiner esteja ordenado para remover todas as duplicatas. Funciona de forma semelhante a remove, exigindo erase para redimensionamento.
#include <vector>
#include <algorithm>
std::vector<int> repetidos = {1, 1, 2, 3, 3, 3, 4, 4, 5};
auto fim_unico = std::unique(repetidos.begin(), repetidos.end());
repetidos.erase(fim_unico, repetidos.end());
// repetidos agora é {1, 2, 3, 4, 5}
2.6. Inversão: reverse
Inverte a ordem dos elementos no intervalo [inicio, fim).
#include <vector>
#include <algorithm>
std::vector<int> ordem = {1, 2, 3, 4, 5};
std::reverse(ordem.begin(), ordem.end());
// ordem agora é {5, 4, 3, 2, 1}
2.7. Rotação: rotate
Gira os elementos de forma que o elemento apontado pelo segundo iterador se torne o primeiro.
#include <vector>
#include <algorithm>
std::vector<int> dados = {1, 2, 3, 4, 5};
// Rotaciona para que 3 seja o primeiro elemento
std::rotate(dados.begin(), dados.begin() + 2, dados.end());
// dados agora é {3, 4, 5, 1, 2}
2.8. Embaralhamento: shuffle (C++11+)
Reordena aleatoriamente os elementos do intervalo usando um gerador de números aleatórios.
#include <vector>
#include <algorithm>
#include <random>
std::vector<int> elementos = {1, 2, 3, 4, 5};
std::random_device rd; // Fonte de sementes aleatórias
std::mt19937 gerador(rd()); // Gerador Mersenne Twister
std::shuffle(elementos.begin(), elementos.end(), gerador);
// elementos agora contém uma permutação aleatória de {1, 2, 3, 4, 5}
3.1. Ordenação: sort, stable_sort, partial_sort
sort(inicio, fim): Ordena o intervalo. Geralmente usa Introsort (uma combinação de QuickSort, HeapSort e InsertionSort), com complexidade média O(N log N). Não garante estabilidade.stable_sort(inicio, fim): Garante que a ordem relativa de elementos iguais seja preservada. Geralmente usa MergeSort, com complexidade O(N log N) e potencialmente maior uso de memória.partial_sort(inicio, meio, fim): Ordena apenas os primeiros(meio - inicio)elementos, colocando os menores do intervalo completo nessas posições.
#include <vector>
#include <algorithm>
#include <functional> // Para std::greater
std::vector<int> numeros = {5, 2, 8, 1, 9, 4};
// Ordenação ascendente padrão
std::sort(numeros.begin(), numeros.end());
// numeros agora é {1, 2, 4, 5, 8, 9}
// Ordenação descendente
std::sort(numeros.begin(), numeros.end(), std::greater<int>());
// numeros agora é {9, 8, 5, 4, 2, 1}
// Ordenação estável (útil com objetos complexos)
std::vector<std::pair<int, char>> pares = {{2, 'b'}, {1, 'a'}, {2, 'c'}, {1, 'd'}};
std::stable_sort(pares.begin(), pares.end(), [](const auto& p1, const auto& p2) {
return p1.first < p2.first; // Ordena pelo primeiro elemento, mantendo a ordem original para chaves iguais
});
// pares agora é {{1, 'a'}, {1, 'd'}, {2, 'b'}, {2, 'c'}}
// Ordenação parcial
std::vector<int> nums_parcial = {8, 3, 1, 9, 4, 2, 7};
// Coloca os 3 menores elementos ordenados no início
std::partial_sort(nums_parcial.begin(), nums_parcial.begin() + 3, nums_parcial.end());
// nums_parcial agora é {1, 2, 3, ...} (os 3 primeiros estão ordenados, o resto não está garantido)
3.2. Posição N-ésima: nth_element
Reorganiza o intervalo de forma que o elemento na posição n seja o que estaria lá se o intervalo estivesse totalmente ordenado. Todos os elementos antes de n são menores ou iguais, e todos os elementos depois são maiores ou iguais.
#include <vector>
#include <algorithm>
std::vector<int> dados = {7, 2, 9, 1, 5, 3, 8};
// Encontrar o 4º menor elemento (índice 3)
std::nth_element(dados.begin(), dados.begin() + 3, dados.end());
// O elemento em dados[3] é agora 5.
// Os elementos em dados[0..2] são <= 5.
// Os elementos em dados[4..6] são >= 5.
// A ordem dentro das partições não é garantida.
3.3. Busca Binária (em intervalos ordenados): binary_search, lower_bound, upper_bound
Estes algoritmos operam eficientemente em contêineres já ordenados.
binary_search(inicio, fim, valor): Retornatruesevalorexiste no intervalo.lower_bound(inicio, fim, valor): Retorna um iterador para o primeiro elemento que não é menor quevalor.upper_bound(inicio, fim, valor): Retorna um iterador para o primeiro elemento que é maior quevalor.
#include <vector>
#include <algorithm>
#include <iostream>
std::vector<int> ordenado = {10, 20, 20, 30, 40, 50};
// Verificar se 20 existe
bool existe_20 = std::binary_search(ordenado.begin(), ordenado.end(), 20); // true
// Encontrar o primeiro elemento >= 20
auto it_lower = std::lower_bound(ordenado.begin(), ordenado.end(), 20);
// it_lower aponta para o primeiro 20 (índice 1)
// Encontrar o primeiro elemento > 20
auto it_upper = std::upper_bound(ordenado.begin(), ordenado.end(), 20);
// it_upper aponta para o 30 (índice 3)
// Contar ocorrências de 20: std::distance(it_lower, it_upper) = 2
3.4. Mesclagem: merge
Combina dois intervlaos ordenados em um único intervalo ordenado de destino. O destino deve ter espaço suficiente.
#include <vector>
#include <algorithm>
std::vector<int> parte1 = {1, 3, 5};
std::vector<int> parte2 = {2, 4, 6};
std::vector<int> resultado(parte1.size() + parte2.size());
std::merge(parte1.begin(), parte1.end(), parte2.begin(), parte2.end(), resultado.begin());
// resultado agora é {1, 2, 3, 4, 5, 6}
A STL fornece algoritmos para gerenciar dados como um heap (geralmente um max-heap por padrão).
make_heap(inicio, fim): Constrói um heap a partir do intervalo.push_heap(inicio, fim): Adiciona o último elemento (assumido como não pertencente ao heap) ao heap.pop_heap(inicio, fim): Move o elemento do topo do heap para o final do intervalo e reorganiza o restante como um heap.sort_heap(inicio, fim): Transforma um heap em uma sequência ordanada.
#include <vector>
#include <algorithm>
#include <iostream>
std::vector<int> dados_heap = {3, 1, 4, 1, 5, 9, 2};
std::make_heap(dados_heap.begin(), dados_heap.end());
// dados_heap agora representa um max-heap. O maior elemento (9) está em dados_heap.front().
// Adicionar um novo elemento
dados_heap.push_back(6);
std::push_heap(dados_heap.begin(), dados_heap.end());
// O heap é atualizado. O novo maior elemento é o topo.
// Remover o maior elemento
std::pop_heap(dados_heap.begin(), dados_heap.end());
int maior = dados_heap.back(); // Pega o maior elemento que foi movido para o fim
dados_heap.pop_back(); // Remove-o do vetor
// maior é 9. dados_heap agora tem o restante como um heap.
// Ordenar o heap
std::sort_heap(dados_heap.begin(), dados_heap.end());
// dados_heap agora está ordenado em ordem ascendente.
5.1. Comparação Simples: min e max
Retorna o menor ou maior de dois valores ou de uma lista inicializada.
#include <algorithm>
#include <initializer_list>
int a = 10, b = 20;
int menor = std::min(a, b); // 10
int maior = std::max(a, b); // 20
int min_lista = std::min({5, 1, 8, 3}); // 1
int max_lista = std::max({5, 1, 8, 3}); // 8
5.2. Elemento Mínimo/Máximo: min_element e max_element
Retornam iteradores para o menor ou maior elemento em um intervalo.
#include <vector>
#include <algorithm>
std::vector<int> valores = {4, 1, 7, 3, 9, 2};
auto it_min = std::min_element(valores.begin(), valores.end()); // Aponta para 1
auto it_max = std::max_element(valores.begin(), valores.end()); // Aponta para 9
5.3. Mínimo e Máximo Juntos: minmax_element (C++11+)
Retorna um par de iteradores para o menor e o maior elemento simultaneamente.
#include <vector>
#include <algorithm>
std::vector<int> dados = {4, 1, 7, 3, 9, 2};
auto resultado_minmax = std::minmax_element(dados.begin(), dados.end());
// resultado_minmax.first aponta para 1
// resultado_minmax.second aponta para 9
6.1. Acumulação: accumulate
Calcula a soma (ou outra operação binária) de todos os elementos em um intervalo, começando com um valor inicial.
#include <vector>
#include <numeric> // Necessário para std::accumulate
#include <functional> // Para std::multiplies
std::vector<int> nums = {1, 2, 3, 4, 5};
// Soma dos elementos com valor inicial 0
int soma = std::accumulate(nums.begin(), nums.end(), 0); // 15
// Produto dos elementos com valor inicial 1
int produto = std::accumulate(nums.begin(), nums.end(), 1, std::multiplies<int>()); // 120
6.2. Produto Escalar: inner_product
Calcula o produto escalar de dois intervalos.
#include <vector>
#include <numeric>
std::vector<int> v_a = {1, 2, 3};
std::vector<int> v_b = {4, 5, 6};
// Produto escalar: (1*4) + (2*5) + (3*6)
int prod_escalar = std::inner_product(v_a.begin(), v_a.end(), v_b.begin(), 0); // 32
6.3. Preenchimento Sequencial: iota
Preenche um intervalo com valores sequencialmente incrementados, começando de um valor especificado.
#include <vector>
#include <numeric>
std::vector<int> sequencia(5);
std::iota(sequencia.begin(), sequencia.end(), 10); // sequencia agora é {10, 11, 12, 13, 14}
6.4. Soma Parcial: partial_sum
Calcula as somas parciais de um intervalo e armazena os resultados em outro. [a, b, c] -> [a, a+b, a+b+c].
#include <vector>
#include <numeric>
std::vector<int> entrada = {1, 2, 3, 4, 5};
std::vector<int> saida(entrada.size());
std::partial_sum(entrada.begin(), entrada.end(), saida.begin());
// saida agora é {1, 3, 6, 10, 15}
6.5. Diferença Adjacente: adjacent_difference
Calcula a diferença entre elementos adjacentes. [a, b, c] -> [a, b-a, c-b].
#include <vector>
#include <numeric>
std::vector<int> entrada = {1, 2, 4, 8, 16};
std::vector<int> saida(entrada.size());
std::adjacent_difference(entrada.begin(), entrada.end(), saida.begin());
// saida agora é {1, 1, 2, 4, 8}
7.1. Geração de Valores: generate
Preenche um intervalo com valores gerados por uma função.
#include <vector>
#include <algorithm>
std::vector<int> dados(5);
int contador = 0;
std::generate(dados.begin(), dados.end(), [&contador]() {
return contador++; // Gera 0, 1, 2, 3, 4
});
7.2. Geração de N Valores: generate_n
Gera n valores usando uma função e os insere em um iterador de saída.
#include <vector>
#include <algorithm>
#include <iterator>
std::vector<int> saida(5);
int valor_inicial = 100;
// Gera 3 valores a partir de valor_inicial e os insere em 'saida'
std::generate_n(std::back_inserter(saida), 3, [&valor_inicial]() {
return valor_inicial++;
});
// saida agora é {100, 101, 102, 0, 0} (os últimos dois permanecem não inicializados ou com seu valor padrão)
7.3. Verificação de Inclusão: includes
Verifica se um intervalo ordenado contém todos os elementos de outro intervalo ordenado.
#include <vector>
#include <algorithm>
std::vector<int> maior_conjunto = {1, 2, 3, 4, 5, 6};
std::vector<int> sub_conjunto = {2, 4};
bool contem = std::includes(maior_conjunto.begin(), maior_conjunto.end(), sub_conjunto.begin(), sub_conjunto.end()); // true
7.4. Operações de Conjunto (em intervalos ordenados): set_union, set_intersection, set_difference, set_symmetric_difference
Executam operações de teoria dos conjuntos em dois intervalos ordenados e gravam o resultado em um iterador de saída.
#include <vector>
#include <algorithm>
#include <iterator>
std::vector<int> c1 = {1, 2, 3, 4, 5};
std::vector<int> c2 = {3, 4, 5, 6, 7};
std::vector<int> res;
// União (todos os elementos únicos)
std::set_union(c1.begin(), c1.end(), c2.begin(), c2.end(), std::back_inserter(res));
// res: {1, 2, 3, 4, 5, 6, 7}
// Interseção (elementos comuns)
res.clear();
std::set_intersection(c1.begin(), c1.end(), c2.begin(), c2.end(), std::back_inserter(res));
// res: {3, 4, 5}
// Diferença (c1 - c2)
res.clear();
std::set_difference(c1.begin(), c1.end(), c2.begin(), c2.end(), std::back_inserter(res));
// res: {1, 2}
// Diferença Simétrica (elementos em um ou outro, mas não em ambos)
res.clear();
std::set_symmetric_difference(c1.begin(), c1.end(), c2.begin(), c2.end(), std::back_inserter(res));
// res: {1, 2, 6, 7}
- Diferença entre
sortestable_sort?sorté geralmente mais rápido, mas não garante a ordem relativa de elementos iguais.stable_sortpreserva essa ordem, o que pode ser crucial ao ordenar objetos com base em múltiplos critérios. - Por que
removeprecisa deerase?removeapenas reorganiza os elementos, movendo os que não devem ser removidos para o início e retornando um iterader para o novo fim "lógico". O tamanho do contêiner não muda.eraseé então necessário para realmente redimensionar o contêiner, removendo os elementos indesejados do final. - **Quais algoritmos exigem um contêiner ordenado?**Algoritmos de busca binária (
binary_search,lower_bound,upper_bound), operações de conjunto (set_intersection, etc.) emergedependem da ordenação para sua eficiência e correção. A ordenação permite reduzir a complexidade de busca de O(N) para O(log N).