Guia Completo de Algoritmos STL em C++

Estes algoritmos percorrem elementos sem modificar o conteúdo dos containers.

1.1 Localização de Elementos

  • localizar(inicio, fim, valor): Encontra a primeira ocorrência de valor e retorna um iterador (ou fim se não encontrar).
  • localizar_se(inicio, fim, predicado): Encontra o primeiro elemento que satisfaz o predicado.
  • localizar_fim(inicio, fim, sub_inicio, sub_fim): Localiza a última ocorrência de uma subsequência.
std::vector<int> numeros = {2, 4, 6, 8, 10};

// Procura elemento com valor 6
auto resultado = localizar(numeros.begin(), numeros.end(), 6);
if (resultado != numeros.end()) {
    std::cout << "Encontrado: " << *resultado << std::endl;  // Saída: 6
}

// Encontra primeiro elemento maior que 7
auto resultado2 = localizar_se(numeros.begin(), numeros.end(), [](int elemento) {
    return elemento > 7;
});
std::cout >> "Primeiro >7: " << *resultado2 << std::endl;  // Saída: 8

// Busca subsequência
std::vector<int> sub = {4, 6};
auto resultado3 = localizer_fim(numeros.begin(), numeros.end(), sub.begin(), sub.end());
if (resultado3 != numeros.end()) {
    std::cout << "Subsequência inicia no índice: " << resultado3 - numeros.begin() << std::endl;  // Saída: 1
}

1.2 Contagem de Elementos

  • contar(inicio, fim, valor): Conta elementos iguais a valor.
  • contar_se(inicio, fim, predicado): Conta elementos que satisfazem o predicado.
std::vector<int> dados = {5, 3, 1, 3, 7, 3};
int qtd_tres = contar(dados.begin(), dados.end(), 3); // Resultado: 3
int qtd_pares = contar_se(dados.begin(), dados.end(), [](int x) { 
    return x % 2 == 0; 
}); // Quantidade de pares: 2

1.3 Execução por Elemento

Aplica uma função a cada elemento da faixa especificada.

std::vector<int> collection = {1, 2, 3, 4, 5};
para_cada(collection.begin(), collection.end(), [](int& item) { 
    item *= 3; // Triplica cada elemento
});
// collection agora é {3, 6, 9, 12, 15}

1.4 Comparação de Faixas

  • igual(inicio1, fim1, inicio2): Verifica se duas faixas são idênticas.
  • divergir(inicio1, fim1, inicio2): Retorna o primeiro par de iteradores onde os elementos diferem.
std::vector<int> conjunto_a = {10, 20, 30};
std::vector<int> conjunto_b = {10, 20, 40};
std::vector<int> conjunto_c = {10, 20, 30};

// Compara as três primeiras posições
bool sao_iguais = igual(conjunto_a.begin(), conjunto_a.end(), conjunto_b.begin());
std::cout << "a == b? " << std::boolalpha << sao_iguais << std::endl;  // Saída: false

// Encontra primeira divergência entre a e c
auto div = divergir(conjunto_a.begin(), conjunto_a.end(), conjunto_c.begin());
if (div.first != conjunto_a.end()) {
    std::cout << "Divergência: " << *div.first << " vs " << *div.second << std::endl;
}

1.5 Verificação Universal

Testa condições em todos os elementos da faixa.

std::vector<int> numeros = {4, 6, 8, 10};
bool todos_pares = todos_os(numeros.begin(), numeros.end(), [](int x) { 
    return x % 2 == 0; 
}); // true
bool algum_impar = algum(numeros.begin(), numeros.end(), [](int x) { 
    return x % 2 != 0; 
}); // false
bool nenhum_negativo = nenhum(numeros.begin(), numeros.end(), [](int x) { 
    return x < 0; 
}); // true

2. Algoritmos de Modificação

Estes algoritmos alteram os elementos dos containers.

2.1 Cópia Seletiva

  • copiar(inicio, fim, destino): Copia todos os elementos para o destino.
  • copiar_se(inicio, fim, destino, predicado): Copia apenas elementos que satisfazem o predicado.
std::vector<int> fonte = {1, 2, 3, 4, 5};
std::vector<int> destino(5);  // Necessário pré-alocar espaço

// Copia todos os elementos
copiar(fonte.begin(), fonte.end(), destino.begin());  // destino: [1,2,3,4,5]

// Copia apenas elementos pares para novo container
std::vector<int> pares;
copiar_se(fonte.begin(), fonte.end(), inseridor_fundo(pares), [](int valor) {
    return valor % 2 == 0;
});  // pares: [2,4]

2.2 Transformação

Aplica uma função a cada elemento e armazena os resultados.

std::vector<int> entrada = {1, 2, 3};
std::vector<int> quadrados(3);

// Cálculo de quadrados (transformação unária)
transformar(entrada.begin(), entrada.end(), quadrados.begin(), [](int x) {
    return x * x;
});  // quadrados: [1,4,9]

// Soma de dois containers (transformação binária)
std::vector<int> col_a = {1, 2, 3};
std::vector<int> col_b = {4, 5, 6};
std::vector<int> soma(3);
transformar(col_a.begin(), col_a.end(), col_b.begin(), soma.begin(), [](int p, int s) {
    return p + s;
});  // soma: [5,7,9]

2.3 Substituição de Valores

  • substituir(inicio, fim, valor_antigo, valor_novo): Substitui todas as ocorrências.
  • substituir_se(inicio, fim, predicado, valor_novo): Substitui elementos que satisfazem o predicado.
  • substituir_copiando(inicio, fim, destino, valor_antigo, valor_novo): Copia substituindo (preserva original).
std::vector<int> numeros = {1, 2, 3, 2, 5};

// Substitui todos os 2 por 20
substituir(numeros.begin(), numeros.end(), 2, 20);  // numeros: [1,20,3,20,5]

// Substitui elementos maiores que 10 por 0
substituir_se(numeros.begin(), numeros.end(), [](int x) {
    return x > 10;
}, 0);  // numeros: [1,0,3,0,5]

// Copia substituindo 3 por 300 (container original inalterado)
std::vector<int> resultado;
substituir_copiando(numeros.begin(), numeros.end(), inseridor_fundo(resultado), 3, 300);  // resultado: [1,0,300,0,5]

2.4 Remoção de Elementos

  • remover(inicio, fim, valor): Move elementos iguais ao valor para o final (não remove fisicamente).
  • remover_se(inicio, fim, predicado): Move elementos que satisfazem o predicado.
std::vector<int> dados = {1, 2, 3, 2, 4};

// Remoção lógica de todos os 2 (move para o final)
auto novo_fim = remover(dados.begin(), dados.end(), 2);  // dados: [1,3,4,2,2]

// Remoção física (realmente apaga)
dados.apagar(novo_fim, dados.end());  // dados: [1,3,4]

// Remove elementos pares usando lambda
dados = {1, 2, 3, 4, 5};
dados.apagar(remover_se(dados.begin(), dados.end(), [](int x) {
    return x % 2 == 0;
}), dados.end());  // dados: [1,3,5]

2.5 Eliminação de Duplicatas Consecutivas

std::vector<int> elementos = {1, 1, 2, 2, 3, 3, 3, 4, 5};
auto fim_logico = unico(elementos.begin(), elementos.end());
elementos.apagar(fim_logico, elementos.end()); // elementos: {1, 2, 3, 4, 5}

2.6 Inversão de Ordem

std::vector<int> lista = {1, 2, 3, 4, 5};
inverter(lista.begin(), lista.end()); // lista: {5, 4, 3, 2, 1}

2.7 Rotação de Elementos

std::vector<int> dados = {1, 2, 3, 4, 5};
rotacionar(dados.begin(), dados.begin() + 2, dados.end()); // Rotaciona a partir do 3: {3, 4, 5, 1, 2}

2.8 Embaralhamento

#include <random>
#include <algorithm>

std::vector<int> numeros = {1, 2, 3, 4, 5};
std::random_device dispositivo;
std::mt19937 gerador(dispositivo);
embaralhar(numeros.begin(), numeros.end(), gerador); // Ordem aleatória

3. Algoritmos de Ordenação

3.1 Ordenação Completa e Parcial

  • ordenar(inicio, fim): Ordenação rápida (instrosort), não estável, O(n log n).
  • ordenar_estavel(inicio, fim): Ordenação estável com merge sort.
  • ordenar_parcial(inicio, meio, fim): Organiza os menores elementos até meio.
std::vector<int> numeros = {5, 3, 1, 4, 2};
ordenar(numeros.begin(), numeros.end()); // Crescente: {1, 2, 3, 4, 5}
ordenar(numeros.begin(), numeros.end(), std::greater<int>()); // Decrescente: {5, 4, 3, 2, 1}
ordenar(numeros.begin(), numeros.end(), [](int a, int b) { 
    return a < b; 
}); // Crescente com comparador customizado

std::vector<std::pair<int, int>> pares = {{1, 2}, {2, 1}, {1, 1}, {2, 2}};
ordenar_estavel(pares.begin(), pares.end(), [](const auto& p1, const auto& p2) {
    return p1.first < p2.first; // Ordena pelo primeiro elemento
});

std::vector<int> elementos = {5, 3, 1, 4, 2, 6};
ordenar_parcial(elementos.begin(), elementos.begin() + 3, elementos.end());
// Primeiros 3 elementos: 1, 2, 3; restantes: 4, 5, 6 (não ordenados)

3.2 Seleção por Posição

std::vector<int> dados = {5, 3, 1, 4, 2, 6};
elemento_n(dados.begin(), dados.begin() + 2, dados.end());
// dados[2] agora contém o 3º menor elemento

3.3 Busca em Container Ordneado

  • busca_binaria(inicio, fim, valor): Retorna bool indicando existência.
  • limite_inferior(inicio, fim, valor): Primeiro elemento não menor que valor.
  • limite_superior(inicio, fim, valor): Primeiro elemento maior que valor.
std::vector<int> ordenado = {1, 3, 3, 5, 7};  // Deve estar ordenado

bool existe = busca_binaria(ordenado.begin(), ordenado.end(), 3);  // true

auto inferior = limite_inferior(ordenado.begin(), ordenado.end(), 3);
std::cout << "Posição inferior: " << inferior - ordenado.begin() << std::endl;  // Saída: 1

auto superior = limite_superior(ordenado.begin(), ordenado.end(), 3);
std::cout << "Posição superior: " << superior - ordenado.begin() << std::endl;  // Saída: 3

3.4 Mesclagem de Containers Ordenados

std::vector<int> conjunto_a = {1, 3, 5};
std::vector<int> conjunto_b = {2, 4, 6};
std::vector<int> mesclado(conjunto_a.size() + conjunto_b.size());

mesclar(conjunto_a.begin(), conjunto_a.end(), conjunto_b.begin(), conjunto_b.end(), mesclado.begin());
// mesclado: [1,2,3,4,5,6]

4. Algoritmos de Heap

STL oferece operações para treating ranges como heaps (árvores binárias completas).

std::vector<int> heap = {4, 1, 3, 2, 5};
construir_heap(heap.begin(), heap.end()); // Max-heap: {5, 4, 3, 2, 1}

heap.push_back(6);
empurrar_heap(heap.begin(), heap.end()); // Heap atualizado: {6, 4, 5, 2, 1, 3}

tirar_heap(heap.begin(), heap.end()); // Move max para o final: {5, 4, 3, 2, 1, 6}
int maximo = heap.back(); // 6
heap.pop_back(); // Remove máximo

ordenar_heap(heap.begin(), heap.end()); // Ordena em ascendente: {1, 2, 3, 4, 5}

5. Algoritmos de Mínimo e Máximo

5.1 Valores Directos

int x = 5, y = 3;
int minimo = std::min(x, y); // 3
int maximo = std::max(x, y); // 5

auto menor_lista = std::min({4, 2, 8, 5, 1}); // 1
auto maior_lista = std::max({4, 2, 8, 5, 1}); // 8

5.2 Localização de Extremos

std::vector<int> dados = {3, 1, 4, 2, 5};
auto it_min = elemento_min(dados.begin(), dados.end()); // Aponta para 1
auto it_max = elemento_max(dados.begin(), dados.end()); // Aponta para 5

5.3 Extremos Simultâneos (C++11)

std::vector<int> numeros = {3, 1, 4, 2, 5};
auto minmax = elemento_minmax(numeros.begin(), numeros.end());
// minmax.first → 1, minmax.second → 5

6. Algoritmos Numéricos (em <numeric>)

6.1 Acumulação

#include <numeric>

std::vector<int> valores = {1, 2, 3, 4, 5};
int soma = acumular(valores.begin(), valores.end(), 0); // 15
int produto = acumular(valores.begin(), valores.end(), 1, std::multiplies<int>()); // 120

6.2 Produto Escalar

std::vector<int> vetor_a = {1, 2, 3};
std::vector<int> vetor_b = {4, 5, 6};
int resultado = produto_interno(vetor_a.begin(), vetor_a.end(), vetor_b.begin(), 0);
// 1*4 + 2*5 + 3*6 = 32

6.3 Geração Sequencial

std::vector<int> sequencia(5);
iota(sequencia.begin(), sequencia.end(), 10); // 10, 11, 12, 13, 14

6.4 Somas Parciais

std::vector<int> origem = {1, 2, 3, 4, 5};
std::vector<int> destino(origem.size());
soma_parcial(origem.begin(), origem.end(), destino.begin()); // {1, 3, 6, 10, 15}

6.5 Diferenças Adjacentes

std::vector<int> entrada = {1, 2, 3, 4, 5};
std::vector<int> saida(entrada.size());
diferenca_adjacente(entrada.begin(), entrada.end(), saida.begin()); // {1, 1, 1, 1, 1}

7. Utilitários Diversos

7.1 Geração com Função

std::vector<int> gerado(5);
int contador = 0;
gerar(gerado.begin(), gerado.end(), [&contador]() { 
    return contador++; 
}); // 0, 1, 2, 3, 4

7.2 Geração Parcial

std::vector<int> vetor(5);
int inicio = 10;
gerar_n(vetor.begin(), 3, [&inicio]() { 
    return inicio++; 
}); // Primeiros 3: 10, 11, 12

7.3 Verificação de Subconjunto

std::vector<int> super = {1, 2, 3, 4, 5};
std::vector<int> sub = {2, 4};
bool contem = incluir(super.begin(), super.end(), sub.begin(), sub.end()); // true

7.4 Operações de Conjuntos

std::vector<int> conjunto_a = {1, 2, 3, 4, 5};
std::vector<int> conjunto_b = {3, 4, 5, 6, 7};
std::vector<int> resultado;

// União
uniao_conjuntos(conjunto_a.begin(), conjunto_a.end(), conjunto_b.begin(), conjunto_b.end(), inseridor_fundo(resultado));
// resultado: {1, 2, 3, 4, 5, 6, 7}

// Interseção
resultado.limpar();
interseccao_conjuntos(conjunto_a.begin(), conjunto_a.end(), conjunto_b.begin(), conjunto_b.end(), inseridor_fundo(resultado));
// resultado: {3, 4, 5}

// Diferença (A - B)
resultado.limpar();
diferenca_conjuntos(conjunto_a.begin(), conjunto_a.end(), conjunto_b.begin(), conjunto_b.end(), inseridor_fundo(resultado));
// resultado: {1, 2}

// Diferença Simétrica
resultado.limpar();
diferenca_simetrica(conjunto_a.begin(), conjunto_a.end(), conjunto_b.begin(), conjunto_b.end(), inseridor_fundo(resultado));
// resultado: {1, 2, 6, 7}

8. Considerações Importantes

1. Diferença entre ordenar e ordenar_estavel?

  • ordenar: Usa introsort (quicksort híbr), não presevra ordem de elementos iguais, O(n log n).
  • ordenar_estavel: Usa merge sort, mantém ordem relativa de elementos iguais, O(n log n) com maior consumo de memória.

2. Por que remover precisa de apagar?

O algoritmo remover apenas move os elementos válidos para frente, retornando um iterador lógico para o novo fim. O container mantém seu tamanho original. apagar remove fisicamente os elementos. A combinação correta é: container.apagar(remover(...), container.fim()).

3. Quais algoritmos requerem container ordenado?

Busca binária (busca_binaria, limite_inferior, limite_superior), operações de conjunto (interseccao, uniao, etc.) e mesclar dependem de dados ordenados para eficiência ideal (O(log n) para buscas).

Tags: cpp STL Algorithm c++-standard-library programming

Publicado em 6-18 02:06