Fechando o bloco: O(log n) com busca binária, como excluir elementos e o programa completo integrado
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.
Lista ordenada: [1, 3, 4, 7, 9, 12, 15] — procurando ch = 7:
| Passo | ini | fim | meio | A[meio] | Decisão |
|---|---|---|---|---|---|
| 1 | 0 | 6 | 3 | 7 | ch == A[3] → achou! Retorna 3 |
Procurando ch = 12 na mesma lista:
| Passo | ini | fim | meio | A[meio] | Decisão |
|---|---|---|---|---|---|
| 1 | 0 | 6 | 3 | 7 | 12 > 7 → busca na metade direita: ini = 4 |
| 2 | 4 | 6 | 5 | 12 | ch == A[5] → achou! Retorna 5 |
Procurando ch = 6 (não existe):
| Passo | ini | fim | meio | A[meio] | Decisão |
|---|---|---|---|---|---|
| 1 | 0 | 6 | 3 | 7 | 6 < 7 → busca na metade esquerda: fim = 2 |
| 2 | 0 | 2 | 1 | 3 | 6 > 3 → busca na metade direita: ini = 2 |
| 3 | 2 | 2 | 2 | 4 | 6 > 4 → busca na metade direita: ini = 3 |
| 4 | 3 | 2 | — | — | ini > fim → não achou. Retorna -1 |
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 */
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 Sequencial | Busca com Sentinela | Busca Binária | |
|---|---|---|---|
| Complexidade | O(n) | O(n) | O(log n) |
| Lista ordenada? | Não exige | Não exige | Obrigatório |
| Para n=1.000.000 | Até 1.000.000 comp. | Até 1.000.000 comp. | Até 20 comp. |
| Comparações/loop | 2 | 1 | 2–3 |
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 inicial | 9 | 3 | 12 | 4 | 1 |
| j=2: A[2] ← A[3] | 9 | 3 | 4 | 4 | 1 |
| j=3: A[3] ← A[4] | 9 | 3 | 4 | 1 | 1 |
| nroElem-- (4) | 9 | 3 | 4 | 1 | (ignorado) |
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.
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 */
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).
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
| Aula | Conceito | Equivalente Java |
|---|---|---|
| 29 | struct — tipo composto de dados | class sem métodos |
| 30 | typedef — apelidos semânticos de tipo | Nome da classe / final int MAX |
| 31 | Ponteiros e passagem por referência | Referências (automáticas em Java) |
| 32 | Estrutura da lista, inicializar, exibir, tamanho | new ArrayList(), .size() |
| 33 | Inserção com deslocamento | ArrayList.add(index, value) |
| 34 | Busca sequencial e com sentinela — O(n) | ArrayList.indexOf() |
| 35 | Busca binária O(log n), exclusão, reinicializar | Collections.binarySearch(), .remove(), .clear() |