📚 Estrutura de Dados — Notas de Aula

Módulo 1 — Hash e Heap
Unidade 1 · Tabelas de Dispersão (Hash)

Uma tabela hash transforma uma chave em um índice de vetor usando uma função de dispersão. O objetivo é buscar, inserir e remover elementos em tempo médio O(1), sem percorrer toda a estrutura.

A função mais comum é h(k) = k mod m, onde k é a chave e m é o tamanho da tabela. O resultado é sempre um número entre 0 e m−1.

Exemplo
h(23) com m=7 → 7×3=21, 23−21=2 → posição 2
Exemplo
h(58) com m=11 → 11×5=55, 58−55=3 → posição 3
Exemplo
Dicionário Python (dict) é internamente uma tabela hash: {nome: Ana, idade: 20}. Acesso direto por chave em O(1).

⚠ Importante
Colisão ocorre quando duas chaves diferentes geram o mesmo índice. Ex: h(7)=0 e h(14)=0 com m=7 — ambas vão para posição 0.
⚠ Importante
Encadeamento: cada posição vira uma lista ligada. Elementos que colidem ficam na mesma posição: [7→14→21].
⚠ Importante
Sondagem linear: quando há colisão, procura a próxima posição livre. Fórmula: h(k,i) = (h(k)+i) mod m, onde i=0,1,2…
⚠ Importante
Fator de carga λ = n/m. Ideal manter λ < 0,75 para evitar muitas colisões.
⚠ Importante
Se a tabela encher com sondagem linear, o algoritmo entra em loop infinito. Por isso λ < 0,75 é crítico!

Exercícios Resolvidos — Hash
Exercício 1: Com h(k)=k mod 11, em qual posição a chave k=58 é armazenada?
→ Posição 3
11×5=55. 58−55=3. Portanto h(58)=3.
Exercício 2: Tabela tam 7, posições 0 e 1 ocupadas. Inserindo k=14 com sondagem linear. Onde vai?
→ Posição 2
h(14)=0 (ocup) → i=1: pos 1 (ocup) → i=2: pos 2 (livre!).
Exercício 3: m=5, posições 0,1,2,4 ocupadas. Inserindo k=10 com sondagem linear. Onde vai?
→ Posição 3
h(10)=0 (ocup) → 1 (ocup) → 2 (ocup) → 3 (livre!).
Exercício 4: Com m=7, as chaves 7, 14 e 21 colidem. Como o encadeamento resolve?
→ Todas vão para posição 0. Lista [7→14→21].
7 mod 7=0, 14 mod 7=0, 21 mod 7=0. Todos múltiplos de 7 têm resto 0.
Módulo 1 — Hash e Heap
Unidade 2 · Lista de Prioridades (Heap)

Um heap é uma árvore binária quase completa (todos os níveis cheios exceto o último, preenchido da esquerda para a direita). É armazenado como vetor — não precisa de ponteiros.

Max-Heap
Todo pai é maior ou igual aos filhos. O maior elemento sempre fica na raiz — disponível em O(1).
Min-Heap
Todo pai é menor ou igual aos filhos. O menor elemento sempre fica na raiz — disponível em O(1).

Índices no array (começa em 1): Pai = ⌊i/2⌋ | Filho esq = 2×i | Filho dir = 2×i+1

Exemplo
Max-heap [90,75,60,40,55,30,25]: índice 3 (valor 60) → pai=índice 1 (90), filho esq=índice 6 (30), filho dir=índice 7 (25).
Exemplo
Inserindo 80 no max-heap: coloca no índice 8. Pai=índice 4 (40). 80>40, troca. Pai=índice 2 (75). 80>75, troca. Pai=índice 1 (90). 80<90, para. 80 fica no índice 2.
Exemplo
Fila de prioridade em hospitais: o paciente mais grave (maior prioridade) sempre sai primeiro — está sempre na raiz do max-heap.
⚠ Importante
Após inserir ou remover, é preciso restaurar com heapify (sobe ou desce o elemento). Custo: O(log n).
⚠ Importante
Heap NÃO é uma árvore binária de busca. A propriedade esquerda < raiz < direita não vale aqui.
⚠ Importante
Heap é a estrutura ideal para fila de prioridade: acesso ao maior/menor em O(1) e inserção/remoção em O(log n).

Exercícios Resolvidos — Heap
Exercício 5: No max-heap [90,75,60,40,55,30,25], qual é o pai e os filhos do índice 3 (valor 60)?
→ Pai=índice 1 (valor 90). Filho esq=índice 6 (valor 30). Filho dir=índice 7 (valor 25).
Pai = ⌊3/2⌋ = 1. Filho esq = 2×3 = 6. Filho dir = 2×3+1 = 7.
Exercício 6: V ou F — no max-heap, o menor elemento está sempre na raiz.
Falso
No MAX-heap o MAIOR elemento está na raiz. O menor está em alguma folha.
Exercício 7: Por que heap é melhor que array simples para fila de prioridade?
→ Heap: acesso ao maior em O(1), inserção/remoção em O(log n). Array: O(n) para achar o maior.
No array simples é preciso varrer todos os elementos. No max-heap, o maior está SEMPRE na raiz.
Módulo 2 — Árvore Binária de Busca (BST)
Unidade 1 · Inserção e Busca

Regra fundamental: todos os valores à ESQUERDA de um nó são MENORES que ele. Todos à DIREITA são MAIORES.

Busca: começa na raiz. Se o valor é menor → esquerda. Se maior → direita. Repete até encontrar ou posição vazia.

Inserção: segue o mesmo caminho da busca até posição vazia. Insere como folha.

Complexidade: O(h). Árvore balanceada: O(log n). Árvore degenerada: O(n).

Exemplo — Inserindo 5,3,8,1,4
raiz=5. 3<5 → esq. 8>5 → dir. 1<5<3 → esq de 3. 4>3<5 → dir de 3.
Exemplo — Buscando 4
Compara com 5 (4<5, esq) → compara com 3 (4>3, dir) → compara com 4 (encontrado!). Apenas 3 comparações.
Exemplo — Inserindo 45 na BST raiz=50
45<50 → esq. 45>30 → dir. 45>40 → dir de 40. O 45 vira filho direito do 40.
⚠ Importante
Se inserir elementos já ordenados (1,2,3,4,5…) a árvore vira uma lista ligada → O(n) — caso degenerado!
⚠ Importante
O caso degenerado é o maior problema da BST simples. A AVL resolve isso com balanceamento automático.

Exercícios Resolvidos — Inserção e Busca
Exercício 8: Na BST raiz=50 (filhos 30,70; netos 20,40,60,80), onde é inserido o valor 45?
→ Filho direito do nó 40.
45<50 → esq. 45>30 → dir. 45>40 → dir do 40.
Exercício 9: Na mesma BST, quantas comparações para buscar o valor 20?
3 comparações.
Compara com 50 (esq) → compara com 30 (esq) → compara com 20 (encontrado!).
Módulo 2 — Árvore Binária de Busca (BST)
Unidade 2 · Percurso e Remoção
Pré-ordem
raiz → esq → dir
Útil para copiar ou serializar a árvore.
Em-ordem
esq → raiz → dir
Sempre resulta em ordem crescente em BST!
Pós-ordem
esq → dir → raiz
Útil para deletar a árvore (filhos antes do pai).
Remoção — 3 casos
Caso 1: folha → remove direto.
Caso 2: 1 filho → pai aponta pro filho.
Caso 3: 2 filhos → substitui pelo sucessor em-ordem.
Exemplo — Em-ordem raiz=50
Resultado: 20, 30, 40, 50, 60, 70, 80 — sempre ordem crescente!
Exemplo — Pré-ordem raiz=50
Resultado: 50, 30, 20, 40, 70, 60, 80 — raiz primeiro.
Exemplo — Removendo 30 (filhos 20 e 40)
Caso 3: dois filhos. Sucessor em-ordem = menor da subárvore direita = 40. O 40 sobe. O 20 fica como filho esq do 40.
Exemplo — Removendo 70 (filho dir=80)
Caso 2: um filho. O 80 sobe e ocupa o lugar do 70.
⚠ Importante
Sucessor em-ordem = menor elemento da subárvore direita do nó removido.
⚠ Importante
O percurso em-ordem em BST SEMPRE gera a sequência em ordem crescente. Propriedade fundamental!

Exercícios Resolvidos — Percurso e Remoção
Exercício 10: Resultado do percurso em-ordem na BST raiz=50 (filhos 30,70; netos 20,40,60,80)?
20, 30, 40, 50, 60, 70, 80
Em-ordem (esq→raiz→dir) em BST SEMPRE resulta em ordem crescente.
Exercício 11: Resultado do percurso pré-ordem na mesma BST?
50, 30, 20, 40, 70, 60, 80
Pré-ordem (raiz→esq→dir): começa pela raiz 50, percorre subárvore esq, depois dir.
Exercício 12: Removendo o nó 30 (filhos 20 e 40). Quem ocupa o lugar?
→ O nó 40 ocupa o lugar do 30. O 20 fica como filho esquerdo do 40.
Caso 3 — dois filhos. Sucessor em-ordem = menor da subárvore direita = 40.
Módulo 3 — Árvores Balanceadas (AVL)
Unidade 1 · Conceitos e Rotações

A AVL é uma BST que se auto-balanceia após cada inserção ou remoção. Resolve o problema da BST degenerada.

Fator de Balanceamento (FB) = altura(esq) − altura(dir). Folha = 0. Nó vazio = −1.

A AVL mantém FB ∈ {−1, 0, +1} em todos os nós. FB = ±2 → desbalanceado → rotação!

Como identificar a rotação:

Pergunta 1: FB do nó desbalanceado é +2 (desequilíbrio à esquerda) ou −2 (à direita)?

Pergunta 2: FB do filho tem o mesmo sinal → 1 rotação (caminho reto). Sinal oposto → 2 rotações (zigue-zague).

CasoFB do nóFB do filhoTipoRotaçãoQtd
LL+2+1Reto (esq-esq)Simples à direita1
RR−2−1Reto (dir-dir)Simples à esquerda1
LR+2−1Zigue-zagueEsq no filho + Dir na raiz2
RL−2+1Zigue-zagueDir no filho + Esq na raiz2
Exemplo — LL: inserindo 30,20,10
Nó 30: FB=+2. Filho 20: FB=+1. Mesmo sinal → LL → 1 rotação à direita. O 20 vira raiz: [10 ← 20 → 30].
Exemplo — RR: inserindo 10,20,30
Nó 10: FB=−2. Filho 20: FB=−1. Mesmo sinal → RR → 1 rotação à esquerda. O 20 vira raiz: [10 ← 20 → 30].
Exemplo — LR: inserindo 30,10,20
Nó 30: FB=+2. Filho 10: FB=−1. Sinais opostos → LR → 2 rotações. O 20 vira raiz: [10 ← 20 → 30].
Exemplo — RL: inserindo 10,30,20
Nó 10: FB=−2. Filho 30: FB=+1. Sinais opostos → RL → 2 rotações. O 20 vira raiz: [10 ← 20 → 30].
⚠ Importante
O nó do MEIO sempre vira a nova raiz em qualquer rotação AVL (LL, RR, LR, RL)!
⚠ Importante
LR e RL precisam de 2 rotações (zigue-zague). LL e RR precisam de apenas 1 rotação (caminho reto).

Exercícios Resolvidos — Rotações
Exercício 13: Inserindo 50,30,10. Nó 50 tem FB=+2, filho 30 com FB=+1. Qual caso, quantas rotações e quem vira raiz?
→ Caso LL — 1 rotação à direita — o 30 vira raiz: [10 ← 30 → 50].
FB=+2 → esquerda. Filho esq (30) FB=+1 → mesmo sinal → caminho reto → LL.
Exercício 14: Inserindo 10,30,20. Nó 10 tem FB=−2, filho 30 com FB=+1. Qual caso e resultado?
→ Caso RL — 2 rotações — o 20 vira raiz: [10 ← 20 → 30].
FB=−2 → direita. Filho dir (30) FB=+1 → sinal oposto → zigue-zague → RL.
Exercício 15: Inserindo 30,10,20. Nó 30 tem FB=+2, filho 10 com FB=−1. Qual caso e resultado?
→ Caso LR — 2 rotações — o 20 vira raiz: [10 ← 20 → 30].
FB=+2 → esquerda. Filho esq (10) FB=−1 → sinal oposto → zigue-zague → LR.
Módulo 3 — Árvores Balanceadas (AVL)
Unidade 2 · Inserção e Remoção na AVL

Inserção na AVL: insere igual à BST normal. Depois sobe pelo caminho verificando o FB de cada nó. Se algum tiver FB=±2, aplica a rotação adequada.

Remoção na AVL: remove igual à BST normal (3 casos). Depois sobe verificando FB e aplicando rotações. Pode precisar de rotações em mais de um nó.

Exemplo — inserindo 10,20,30
Após inserir 30: FB da raiz 10 = −2, filho 20 FB=−1 → RR → rotação esquerda → 20 vira raiz, 10 esq, 30 dir.
Exemplo — inserindo 10,20,30,40,50
Após inserir 40: nó 20 FB=−2, filho 30 FB=−1 → RR → rotação. Após inserir 50: nó 30 FB=−2 → nova rotação. AVL rebalanceia a cada inserção.
⚠ Importante
AVL garante SEMPRE O(log n) para busca, inserção e remoção — ao contrário da BST simples, nunca degenera em lista.
⚠ Importante
O custo extra das rotações é O(log n) no pior caso — vale a pena para garantir buscas rápidas em qualquer situação.
⚠ Importante
A altura de uma AVL com n nós é sempre O(log n). Isso garante que todas as operações sejam eficientes.

Exercícios Resolvidos
Exercício 16: Um nó tem subárvore esq altura 2 e subárvore dir altura 0. FB=? Está balanceado?
→ FB = 2−0 = +2, DESBALANCEADO. Filho esq FB=+1 → LL (1 rotação). Filho esq FB=−1 → LR (2 rotações).
FB = altura(esq)−altura(dir) = 2−0 = +2. Como |FB| > 1, está desbalanceado.
Exercício 17: V ou F — após cada inserção em AVL, todos os nós precisam ser rotacionados.
Falso
Apenas os nós com FB=±2 são rotacionados. Na maioria dos casos, apenas 1 ou 2 rotações bastam para rebalancear.
Módulo 4 — Desafios de Programação
Unidade 1 · Aplicações com Hash

Tabela hash é ideal quando precisamos de acesso rápido por uma chave e a ordem dos elementos não importa.

Hash criptográfico é diferente do hash de tabela: foi projetado para ser irreversível e resistente a colisões intencionais.

Exemplo
Contar frequência de palavras: cada palavra é a chave, a contagem é o valor. Atualização em O(1).
Exemplo
Verificar duplicatas: inserir cada elemento no hash. Se já existe → é duplicata. O(n) total, vs O(n²) da força bruta.
Exemplo
Hash criptográfico SHA-256: "senha123" → sempre o mesmo hash. Dado o hash, impossível descobrir "senha123". Usado em bancos de dados de senhas.
⚠ Importante
Hash criptográfico é IRREVERSÍVEL — dado o hash, não dá para descobrir o valor original. Por isso é usado para proteger senhas.
⚠ Importante
Escolha hash quando: busca por chave em O(1), não precisa de ordem, chave única por elemento.

Unidade 2 · Aplicações com Árvores

Código de Huffman: compressão sem perda. Símbolos mais frequentes ficam mais perto da raiz e recebem códigos menores.

Exemplo — Huffman: "aabbbcccc"
Frequências: a=2, b=3, c=4. Na árvore: c → código menor (ex: 0), b → médio (ex: 10), a → maior (ex: 11). Letras frequentes usam menos bits.
Exemplo — Dijkstra
Usa min-heap: sempre processa o vértice com menor distância acumulada. Heap garante O(log n) por operação.
⚠ Importante
Huffman: compressão ótima quando a frequência dos símbolos é conhecida. Muito usado em JPEG, MP3 e ZIP.
⚠ Importante
Use BST/AVL para buscar qualquer elemento. Use Heap para maior/menor. Use Hash para buscar por chave específica.

Exercícios Resolvidos — Aplicações
Exercício 18: Por que sistemas guardam o HASH da senha e não a senha em texto puro?
→ Hash criptográfico é irreversível — protege usuários mesmo se o banco de dados vazar.
O sistema calcula h(senha) no login e compara com o hash armazenado. Quem rouba o banco só vê hashes — não consegue reverter para descobrir as senhas originais.
Exercício 19: No código de Huffman, a letra que aparece 100 vezes terá código binário maior ou menor que a que aparece 2 vezes?
Menor — letras mais frequentes têm códigos MENORES (menos bits).
Huffman coloca símbolos mais frequentes mais perto da raiz. Quanto mais perto da raiz, menor o caminho e menor o código binário gerado.
Exercício 20: Por que o heap é melhor que a BST simples para fila de prioridade?
→ Heap garante O(log n) sempre. BST simples pode degenerar para O(n) com inserções ordenadas.
No heap a estrutura NUNCA degenera — sempre árvore quase completa com altura O(log n). Na BST simples, inserir em ordem cria lista ligada.
🗒 Cola de Prova — Resumo Geral
Comparativo de Estruturas
EstruturaPara que serveComplexidade principal
HashBusca rápida por chave específicaO(1) médio, O(n) pior caso
HeapAchar maior/menor sempre disponívelO(1) raiz, O(log n) ins/rem
BSTBusca ordenada e dinâmicaO(h): O(log n) bal., O(n) deg.
AVLBST sempre balanceadaO(log n) garantido sempre
Tabela de Rotações AVL
CasoFB nóFB filhoTipoRotações
LL → direita+2+1Reto1
RR → esquerda−2−1Reto1
LR → esq+dir+2−1Zigue-zague2
RL → dir+esq−2+1Zigue-zague2
Percursos BST
PercursoOrdem de visitaResultado (raiz=50)
Em-ordemesq → raiz → dir20,30,40,50,60,70,80 (SEMPRE crescente!)
Pré-ordemraiz → esq → dir50,30,20,40,70,60,80
Pós-ordemesq → dir → raiz20,40,30,60,80,70,50
Remoção BST — 3 Casos
CasoQuando ocorreComo resolver
Caso 1: folhaNó sem filhosRemove direto, sem reorganizar
Caso 2: 1 filhoNó com apenas um filhoConecta o pai diretamente ao filho do removido
Caso 3: 2 filhosNó com filho esq e dirSubstitui pelo sucessor em-ordem (menor da subárvore dir)
⭐ Regras de Ouro para a Prova
Regra
O nó do MEIO sempre vira a nova raiz nas rotações AVL (LL, RR, LR e RL)
Regra
Em-ordem em BST = SEMPRE ordem crescente (propriedade fundamental)
Regra
λ = n/m ideal < 0,75 para tabela hash (evita colisões excessivas)
Regra
Heap: pai ≥ filhos (max-heap) | pai ≤ filhos (min-heap)
Regra
Hash criptográfico é IRREVERSÍVEL — hash de tabela não necessariamente
Regra
BST pode degenerar em lista O(n) — AVL resolve com rotações automáticas
Regra
Mesmo sinal nos FBs = 1 rotação (caminho reto). Sinais opostos = 2 rotações (zigue-zague)