Aula 35 – Busca Binária, Exclusão e Programa Completo

Fechando o bloco: O(log n) com busca binária, como excluir elementos e o programa completo integrado

Busca Binária — Dividindo para Conquistar

A busca sequencial examina até n elementos. A busca binária divide o problema pela metade a cada passo — examine apenas log₂(n) elementos. Para 1024 elementos: sequencial faz até 1024 comparações; binária faz no máximo 10.

Pré-requisito obrigatório: A lista deve estar ordenada pela chave. A busca binária não funciona em listas desordenadas — é esse o trade-off: manter a ordenação é custoso, mas as buscas ficam muito mais rápidas.
A busca binária funciona assim: compare a chave procurada com o elemento do meio da lista. Se for igual, achou. Se for menor, a resposta está na metade esquerda. Se for maior, está na metade direita. Repita até encontrar ou esgotar o intervalo.

Visualizando a Busca Binária — Procurando o 7

Lista ordenada: [1, 3, 4, 7, 9, 12, 15] — procurando ch = 7:

PassoinifimmeioA[meio]Decisão
10637 ch == A[3] → achou! Retorna 3

Procurando ch = 12 na mesma lista:

PassoinifimmeioA[meio]Decisão
1063712 > 7 → busca na metade direita: ini = 4
246512ch == A[5] → achou! Retorna 5

Procurando ch = 6 (não existe):

PassoinifimmeioA[meio]Decisão
106376 < 7 → busca na metade esquerda: fim = 2
202136 > 3 → busca na metade direita: ini = 2
322246 > 4 → busca na metade direita: ini = 3
432——ini > fim → não achou. Retorna -1

A Função buscaBinaria

int buscaBinaria(LISTA* l, TIPOCHAVE ch) {
    int ini  = 0;
    int fim  = l->nroElem - 1;

    while (ini <= fim) {
        int meio = (ini + fim) / 2;

        if (ch == l->A[meio].chave) return meio;  // achou!

        if (ch < l->A[meio].chave)
            fim = meio - 1;  // chave está na metade esquerda
        else
            ini = meio + 1;  // chave está na metade direita
    }
    return -1;  // ini > fim: chave não existe na lista
} /* buscaBinaria */
Equivalente Java: Collections.binarySearch(lista, 7) — funciona em listas que implementam Comparable e também retorna um índice negativo quando não encontra. A lógica interna é exatamente a mesma.
Busca SequencialBusca com SentinelaBusca Binária
ComplexidadeO(n)O(n)O(log n)
Lista ordenada?Não exigeNão exigeObrigatório
Para n=1.000.000Até 1.000.000 comp.Até 1.000.000 comp.Até 20 comp.
Comparações/loop212–3

Exclusão de Elemento

Excluir um elemento é o inverso da inserção: encontramos o índice do elemento, depois deslocamos todos os elementos subsequentes uma posição para a esquerda, sobrescrevendo o elemento excluído, e decrementamos nroElem.

bool excluirElemLista(LISTA* l, TIPOCHAVE ch) {
    int pos = buscaSequencial(l, ch);  // encontra a posição
    if (pos == ERRO) return false;      // elemento não existe

    int j;
    for (j = pos; j < l->nroElem - 1; j++)
        l->A[j] = l->A[j + 1];        // desloca para a esquerda

    l->nroElem--;
    return true;
} /* excluirElemLista */

Visualizando: excluir o elemento com chave 12 da lista [9, 3, 12, 4, 1]:

PassoÍndice 0Índice 1Índice 2Índice 3Índice 4
Lista inicial931241
j=2: A[2] ← A[3]93441
j=3: A[3] ← A[4]93411
nroElem-- (4)9341(ignorado)
Em Java: lista.remove(Integer.valueOf(12)) faz exatamente isso — busca o elemento e desloca os subsequentes. O método remove(int index) exclui pela posição, enquanto remove(Object o) busca pelo valor.

Reinicializar a Lista

Zerar a lista sem liberar memória (afinal, em lista estática não há memória dinâmica para liberar):

void reinicializarLista(LISTA* l) {
    l->nroElem = 0;  // apenas zera o contador — os dados no array são "esquecidos"
} /* reinicializarLista */
Equivalente Java: lista.clear() — zera o ArrayList. A diferença é que em Java o garbage collector libera a memória dos objetos removidos. Em C estático, os dados ainda existem no array mas são ignorados (nroElem=0).

Programa Completo — Tudo Integrado

O main() completo que exercita todas as funções que aprendemos nas aulas 29 a 35:

int main() {
    LISTA lista;
    inicializarLista(&lista);
    exibirLista(&lista);

    printf("Nro elementos: %d\n", tamanho(&lista));
    printf("Tamanho bytes: %d\n", tamanhoEmBytes(&lista));

    REGISTRO reg;
    reg.chave = 9;  inserirElemLista(&lista, reg, 0);
    exibirLista(&lista);   // Lista: " 9 "

    reg.chave = 3;  inserirElemLista(&lista, reg, 1);
    reg.chave = 4;  inserirElemLista(&lista, reg, 2);
    reg.chave = 1;  inserirElemLista(&lista, reg, 3);
    reg.chave = 12; inserirElemLista(&lista, reg, 2);
    exibirLista(&lista);   // Lista: " 9 3 12 4 1 "

    printf("Nro elementos: %d\n", tamanho(&lista));
    printf("Tamanho bytes: %d\n", tamanhoEmBytes(&lista));

    printf("Chave 4, pos: %d\n", buscaSequencial(&lista, 4));
    printf("Chave 4, pos: %d\n", buscaSentinela(&lista, 4));

    if (excluirElemLista(&lista, 4))  printf("Excluído: 4\n");
    if (excluirElemLista(&lista, 8))  printf("Excluído: 8\n");  // não existe
    if (excluirElemLista(&lista, 9))  printf("Excluído: 9\n");
    exibirLista(&lista);   // Lista: " 3 12 1 "

    printf("Nro elementos: %d\n", tamanho(&lista));

    reinicializarLista(&lista);
    exibirLista(&lista);   // Lista: ""
    printf("Nro elementos: %d\n", tamanho(&lista));

    return 0;
}

Lista: " "

Nro elementos: 0

Tamanho bytes: 204

Lista: " 9 "

Lista: " 9 3 12 4 1 "

Nro elementos: 5

Tamanho bytes: 204

Chave 4, pos: 3

Chave 4, pos: 3

Excluído: 4

Excluído: 9

Lista: " 3 12 1 "

Nro elementos: 3

Lista: " "

Nro elementos: 0

Resumo do Bloco — Aulas 29 a 35

AulaConceitoEquivalente Java
29struct — tipo composto de dadosclass sem métodos
30typedef — apelidos semânticos de tipoNome da classe / final int MAX
31Ponteiros e passagem por referênciaReferências (automáticas em Java)
32Estrutura da lista, inicializar, exibir, tamanhonew ArrayList(), .size()
33Inserção com deslocamentoArrayList.add(index, value)
34Busca sequencial e com sentinela — O(n)ArrayList.indexOf()
35Busca binária O(log n), exclusão, reinicializarCollections.binarySearch(), .remove(), .clear()

Exercícios — Aula 35

1. Por que a busca binária exige que a lista esteja ordenada?

2. Quantas comparações a busca binária faz no pior caso para uma lista de 1024 elementos?

3. Na exclusão, os elementos são deslocados para qual direção, e por quê?

4. O que acontece com reinicializarLista — os dados do array são apagados?

5. Na busca binária, o que significa ini > fim ao final do while?

6. Para excluir o elemento com chave 8 de uma lista que não contém esse valor, excluirElemLista retorna:

7. Qual é o equivalente Java mais próximo de buscaBinaria(&lista, 7)?

8. Comparando inserção e exclusão em lista sequencial: qual é a complexidade de ambas no pior caso?