Parte do zero: nunca viu C? Sem problema. Cada assunto começa com uma comparação do dia a dia -> depois o código -> depois você treina. Os selos dizem o que foi visto nas aulas de 13/08 e 20/08 e o que a lista cobra sem ninguém ter explicado.
Isto é um vetor: uma fileira de caixinhas numeradas a partir do 0.
O que cai (segundo a lista avaliativa)
Questão da lista
Assuntos que você precisa dominar
Na aula?
Módulo
1. Ordenação e desempenho
Bubble, Insertion, Selection, Quick, Merge; contar comparações e trocas; Big O
Bubble · 13/08resto no materialcontadores: não
1, 2, 3
2. Cadastro com busca
struct, vetor dinâmico, busca sequencial e binária, ordenar por RA
struct · 13/08binária no materialsequencial: não
5
3. Ponteiros e memória
&, *, malloc, realloc, free, inverter no lugar, remover valores
hash e encadeamento no materialendereçamento aberto: não (opcional)
6
Base de tudoO Módulo 0 (o mínimo de C pra ler código) aparece em todas as questões. Não pula.
Onde tá o riscoO que a lista cobra e ninguém explicou em aula: realloc (módulo 4), contar comparações e trocas (módulo 2) e busca sequencial (módulo 5, essa é fácil). Reserva 40 min pra eles.
O que cada selo quer dizer
Visto em aulatá nas suas aulas de 13/08 ou 20/08 (código ou anotação). Base garantida.No material do professortá nos .c, nas imagens ou na apostila que ele passou, mas não nas suas anotações.Não visto em aula · a lista cobraninguém explicou em aula, mas a lista pede. Estude com calma, pode cair.Só na apostilanão tá na lista. Se sobrar tempo.
Módulo 0Kit de sobrevivência em C
Antes de ordenar ou buscar qualquer coisa, tem que conseguir ler um programa em C. Aqui vai só o mínimo, sem enrolação.
Programa = receita de bolo Visto em aula · 13/08
Explicando pro macacoO computador é um cozinheiro obediente e burro. Lê a receita linha por linha, de cima pra baixo, não pula nada e não adivinha nada. Mandou "coloca 3 ovos" -> coloca 3 ovos. Esqueceu de escrever "liga o forno"? -> bolo cru, e a culpa é sua, não dele.
Toda instrução termina com ;. Bloco fica entre { }. O que vem depois de // ou entre /* */ é comentário -> o compilador ignora, é só pra gente.
Variável = caixa com etiqueta Visto em aula · 13/08
Explicando pro macacoVariável é uma caixa. Etiqueta = nome (idade). Formato da caixa = tipo: só cabe inteiro, ou número com vírgula, ou uma letra. O que tá dentro = valor. Caixa de ovo não guarda pizza, blz?
Tipo
Guarda
Exemplo
No printf/scanf
int
inteiro
int ra = 12345;
%d
float / double
número com vírgula (em C se escreve ponto)
float nota = 7.5;
%f (%.2f = 2 casas)
char
uma letra
char letra = 'A';
%c
char[50]
texto (string) de até 49 letras
char nome[50] = "Ana";
%s
Contas que pegam iniciante Visto em aula · 13/08
7 / 2 dá 3, não 3.5. Inteiro dividido por inteiro -> corta a parte quebrada e pronto. Quer 3.5? -> (double) 7 / 2.
7 % 2 dá 1 -> é o resto da divisão. 137 % 10 = 7, ou seja, o último dígito. É isso que tá por trás do rand() % 100 da aula e da tabela hash.
= guarda. == pergunta "é igual?". Trocar um pelo outro dentro do if é o erro nº 1 de C.
&& = E (as duas verdadeiras). || = OU (pelo menos uma). ! = NÃO.
i++ soma 1. i-- tira 1. soma += x é só atalho de soma = soma + x.
Decidir: if / else Visto em aula · 13/08
Repetir: for e while Visto em aula · 13/08
Explicando pro macacofor = treinador na pista: "começa na volta 0; enquanto for menor que 5, corre; no fim de cada volta, soma 1". while = "enquanto tiver fome, come banana". O while não sabe quantas voltas vai dar, só fica testando a condição.
Na aula o i foi declarado fora do for (int i = 0; lá em cima), que é o jeito do C mais antigo. Os dois jeitos funcionam.
E um for dentro de outro for? -> laço aninhado. Se cada um dá n voltas, o de dentro roda n × n vezes. Guarda isso: é o motivo do Bubble Sort ser lento (Módulo 1).
Vetor = fileira de armários numerados Visto em aula · 13/08
Explicando pro macacoVetor é corredor de armário de escola: todos iguais, grudados, cada um com número na porta. O primeiro é o 0, não o 1. Corredor com 4 armários -> números de 0 a 3.
Pegadinha clássicav[4] num vetor de 4 posições -> não existe. O último é v[n-1]. E o C não avisa, fio: lê lixo ou dá pau. Por isso o laço é i < n e não i <= n.
Função = máquina com entrada e saída Visto em aula · 13/08
Explicando pro macacoFunção é liquidificador: joga as coisas (parâmetros), ele faz o serviço e devolve o resultado (return). void é máquina que faz algo mas não devolve nada, tipo impressora.
Passou um int pra função? -> ela recebe uma fotocópia. Mexeu na cópia, o original nem fica sabendo. Com vetor é diferente: a função mexe no vetor original. Por quê? -> Módulo 4 (spoiler: ponteiro, que a gente usa sem saber).
Números aleatórios e constantes Visto em aula · 13/08
Na aula foi #define tamanho 100 e srand(time(0)). Mesma coisa: time(0) e time(NULL) são iguais.
Textos (strings) Visto em aula · 13/08
Texto em C = vetor de char que termina com um caractere invisível, o '\0'. Logo, char nome[50] cabe 49 letras (uma vaga é do '\0').
Módulo 1Big O: medir o esforço de um algoritmo
Explicando pro macacoPerdeu a banana numa fileira de caixas. Abrindo uma por vez: 10 caixas -> até 10 aberturas; 1000 caixas -> até 1000. O trabalho cresce junto com a quantidade -> isso é O(n). Big O não mede segundo nenhum. Mede quanto o trabalho cresce quando a entrada cresce.
Como descobrir o Big O olhando o código No material do professor
Olha pros laços. É quase sempre isso:
Se o código tem…
Big O
Nome
Exemplo da matéria
Nenhum laço, só contas e acesso v[i]
O(1)
constante
acessar v[5]; hash no caso bom
Um laço que corta o problema pela metade a cada volta
O(log n)
logarítmico
busca binária
Um laço que passa por todos
O(n)
linear
busca sequencial, achar maior, média
Divide pela metade (log n níveis) e em cada nível passa por todos (n)
O(n log n)
"n log n"
Merge Sort; Quick Sort no caso médio
Um laço dentro de outro, ambos até n
O(n²)
quadrático
Bubble, Selection, Insertion
Tá, mas por que isso importa? -> os números No material do professor
n
log₂ n
n log₂ n
n²
10
3
33
100
1.000
10
9.966
1.000.000
1.000.000
20
≈ 20 milhões
1 trilhão
Rodei o programa da questão 1 pra montar este guia. Olha o que deu:
Algoritmo
Comparações, N = 1.000
Comparações, N = 10.000
Cresceu
Bubble
499.364
49.992.789
≈ 100× (n²: 10× mais dados, 100× mais trabalho)
Selection
499.500
49.995.000
≈ 100×
Insertion
248.213
25.231.383
≈ 100×
Quick
11.417
180.540
≈ 16× (n log n)
Merge
8.692
120.390
≈ 14× (n log n)
Resposta pronta pra "relacione com a complexidade"Ao multiplicar N por 10, Bubble, Selection e Insertion fizeram cerca de 100 vezes mais comparações, o que confirma O(n²). Quick e Merge cresceram cerca de 14 a 16 vezes, compatível com O(n log n). Por isso Quick e Merge tiveram o melhor comportamento nos testes, e a diferença aumenta conforme N cresce.
Melhor, médio e pior caso No material do professor
O mesmo algoritmo trabalha mais ou menos dependendo de como os dados chegam. Banana na primeira caixa -> melhor caso, 1 passo. Banana na última, ou nem existe -> pior caso, n passos. Prova falou só "a complexidade de X"? -> quase sempre quer o pior caso.
Três palavras que caem em prova No material do professor
Estável -> se dois têm a mesma chave, continuam na ordem em que chegaram. Ana e Bia com nota 8, Ana antes -> estável mantém Ana antes de Bia.
In-place (no lugar) -> ordena dentro do próprio vetor, sem criar outro do tamanho n. Merge Sort não é: precisa de vetor auxiliar.
Comparação × troca -> comparar é perguntar "esse é maior que aquele?"; trocar é mudar os dois de lugar. São contadores diferentes na questão 1.
Módulo 2Ordenações simples: Bubble, Selection e Insertion
Ordenar = deixar crescente: v[0] ≤ v[1] ≤ … ≤ v[n-1]. Os três daqui são fáceis de escrever e lentos pra muito dado (O(n²)). Vou usar sempre o mesmo vetor [7, 3, 5, 2] pra dar pra comparar.
comparandotrocoulugar definitivo
Bubble Sort: as bolhas sobem Visto em aula · 13/08
Explicando pro macacoMacacos em fila. Você anda pela fila olhando só dois vizinhos por vez. O da esquerda é mais alto? -> troca os dois. No fim da primeira passada, o mais alto de todos foi empurrado até o final, igual bolha subindo na água. Repete a passada, agora ignorando o último, que já tá certo.
No exemplo -> 6 comparações e 5 trocas. Já ordenado? -> uma passada sem troca e para: O(n). Ordem decrescente? -> O(n²). E é estável.
Olha o teste.c da aula de 13/08Tem quatro pegadinhas lá que valem ouro pra prova:
1) A função boubleSort foi escrita dentro do main. C padrão não deixa -> ela vai fora, antes ou depois do main.
2) Ela recebe v, mas mexe em vet, que é variável do main. Tem que usar o parâmetro v.
3) Preenche só 6 posições, mas ordena tamanho (100). As outras 94 são lixo -> o lixo entra na ordenação. Passe o n de verdade.
4) Usa rand e srand sem #include <stdlib.h>. Compilador novo reclama.
Selection Sort: escolher o menor No material do professor
Explicando pro macacoEducação física, escolhendo time. Você olha todo mundo que sobrou, aponta o mais baixinho e põe ele na primeira posição livre. Olha o resto de novo, aponta o próximo mais baixinho, e vai. Muita olhada (comparação), pouca troca: no máximo uma por rodada.
No exemplo -> 6 comparações e só 1 troca. Faz sempre n(n−1)/2 comparações, tando ordenado ou não -> O(n²) no melhor, médio e pior. Não é estável: a troca "de longe" pode passar um igual na frente do outro.
Insertion Sort: arrumar carta na mão No material do professor
Explicando pro macacoVocê recebe carta de baralho uma por vez. A mão já tá arrumada. Pega a carta nova e vai empurrando pra direita as maiores que ela, até achar o buraco certo. Encaixa. Pega a próxima.
No exemplo -> 6 comparações e 5 movimentações. Já ordenado? -> O(n), uma comparação por carta. Pior caso -> O(n²). É estável e é o melhor dos três quando o vetor já tá quase ordenado.
No AulaOrdenacao.c do professor tá no jeito compacto: while (j >= 0 && v[j] > chave). A ordem importa -> primeiro testa j >= 0, senão acessaria v[-1] e dá pau.
Resumo dos três
Ideia
Melhor
Pior
Trocas
Estável
Bubble
troca vizinhos; maior vai pro fim
O(n)
O(n²)
muitas
sim
Selection
acha o menor; põe na frente
O(n²)
O(n²)
no máx. n−1
não
Insertion
encaixa cada carta na parte arrumada
O(n)
O(n²)
muitas (deslocamentos)
sim
Como contar comparações e trocas (questão 1) Não visto em aula · a lista cobraCria duas variáveis long comparacoes, trocas;. Põe comparacoes++logo antes de cada if que compara dois elementos do vetor, e trocas++ dentro da função trocar (ou em cada deslocamento). Zera as duas antes de rodar cada algoritmo. Só isso.
Módulo 3Recursão, Quick Sort e Merge Sort
Recursão: função que chama ela mesma No material do professor
Explicando pro macacoFila do cinema, escuro. Quer saber sua posição? -> pergunta pro da frente. Ele pergunta pro da frente dele, que pergunta pro da frente… até chegar no primeiro, que sabe sem perguntar: "sou o 1". Esse é o caso base. A resposta volta de mão em mão, cada um somando 1.
Sem caso base = explodeA função nunca para de se chamar -> a memória de chamadas (a "pilha") enche -> stack overflow, dá pau. Toda recursão precisa de duas coisas: 1) caso base, 2) chamada com um problema menor.
Quick Sort: escolhe um pivô e separa No material do professor
Explicando pro macacoO professor escolhe um macaco da fila: o pivô (aqui é sempre o último). Quem é menor ou igual a ele -> vai pra esquerda. Quem é maior -> direita. O pivô fica no meio e pronto: ele já tá no lugar definitivo. Agora faz a mesma coisa só com o grupo da esquerda, depois só com o da direita. Grupo de 0 ou 1 macaco já tá ordenado, nem mexe.
Imagina o seguinte -> particionamento (Lomuto) em [7, 3, 5, 2, 6, 4], pivô = 4:
Agora ordena [3, 2] e [7, 6, 5] do mesmo jeito. O 4 nunca mais se mexe.
Custo: pivô caindo perto do meio -> log n níveis com n comparações cada -> O(n log n). Mas se o pivô for sempre o maior ou o menor (vetor já ordenado, com pivô no último) -> um lado fica vazio -> O(n²). Não usa vetor auxiliar, mas a recursão gasta memória de pilha. Não é estável.
Merge Sort: divide e intercala No material do professor
Explicando pro macacoBaralho bagunçado. Divide ao meio e entrega pra dois amigos. Eles dividem de novo, e de novo, até cada um ter uma carta só (carta sozinha já tá ordenada, né). Na volta, cada dupla junta suas duas pilhas: olha a carta de cima de cada pilha, pega a menor, repete. Juntar duas pilhas já ordenadas é fácil e rápido.
Custo: O(n log n) sempre (melhor, médio e pior), porque divide sempre ao meio, não importa o valor de ninguém. O preço -> vetor auxiliar de tamanho n, O(n) de memória extra. É estável.
Quick × Merge
Quick Sort
Merge Sort
Melhor / médio
O(n log n)
O(n log n)
Pior caso
O(n²) (pivô ruim, ex.: vetor já ordenado)
O(n log n)
Memória extra
só a pilha da recursão (log n)
vetor auxiliar O(n)
Estável
não
sim
Onde tá o trabalho
antes de chamar a recursão (particionar)
depois da recursão (intercalar)
Programa completo da questão 1
Junta os 5 algoritmos (com as funções aí de cima), gera N números aleatórios com malloc, roda cada um numa cópia do mesmo vetor original e mostra os contadores. Sem compilador? -> cola no OnlineGDB.
Saída real com N = 10 (os seus números mudam, o vetor é aleatório):
ReparaBubble e Insertion fizeram exatamente as mesmas 23 trocas. Coincidência? Não -> os dois resolvem uma "inversão" (par fora de ordem) por troca ou deslocamento. Selection faz pouca troca mas o máximo de comparação: 10·9/2 = 45.
Módulo 4Ponteiros e memória dinâmica
Memória estática × dinâmica Visto em aula · 20/08
Estática -> tamanho decidido quando você escreve o código: int v[100];. Compilou, é 100 e acabou. Precisou de 101? Ferrou.
Dinâmica -> alocada em tempo de execução. O programa pergunta pro usuário quanto precisa e pede pro SO na hora. A lista inteira gira em torno disso ("o programa não poderá utilizar um vetor de tamanho fixo").
Ponteiro: o número da casa Visto em aula · 20/08
Explicando pro macacoA memória é uma rua gigante de casinhas numeradas. Cada variável mora numa casa. O número da casa = endereço (aquele hexadecimal tipo 0x61882a21). Ponteiro = uma posição de memória que guarda o endereço de outra -> um papelzinho com o número da casa anotado. Com o papel na mão, você vai lá e mexe no que tem dentro, sem nem saber o nome de quem mora.
Aí em cima -> x mora na casa 1000 e guarda 10. p mora na casa 2000 e guarda o número 1000, ou seja, "aponta pra" x.
Símbolo
Lê assim
Exemplo
int *p;
"p é um papelzinho que guarda endereço de um int"
declaração
&x
"o endereço de x" (qual é a casa?)
p = &x;
*p
"o conteúdo da casa anotada em p" (vai lá)
*p = 20; muda o x
Por que declaro com tipo (int *, float *, double *)? -> porque o ponteiro precisa saber o que tem lá na casa: quantos bytes ler e quanto andar no p++. Mas o ponteiro em si não gasta o mesmo que o que ele aponta. Ele é só endereço -> tamanho fixo, 8 bytes num PC 64 bits (4 bytes em 32 bits), aponte ele pra char ou pra double.
Agora faz sentido o & do scanfscanf("%d", &idade) entrega pro scanf o endereço de idade. Sem isso, o scanf não sabe em qual casa guardar o número digitado. Ou seja, a gente já usava ponteiro sem saber.
Por que a função trocar precisa de ponteiro No material do professor
Passou variável comum pra função? -> ela recebe fotocópia. Troca as cópias, os originais ficam iguaizinhos. Passou o endereço? -> a função vai até a casa original e troca lá. É o swap(int* a, int* b) do AulaOrdenacao.c.
E é assim que uma função "devolve" mais de um resultado: void maior_menor(int *v, int n, int *maior, int *menor) escreve nos dois endereços que recebeu.
Vetor é deslocamento de ponteiro: p[i] é *(p + i) Visto em aula · 20/08
Os colchetes parecem um negócio separado, mas não são -> p[i] é só escrita bonita pra *(p + i): o + calcula o endereço do elemento i, o * vai lá e pega o conteúdo. O nome do vetor é o endereço do primeiro armário. Por isso, passou vetor pra função -> ela mexe no original.
p++ anda um pra frente. Posso voltar? -> pode, p--. O que não pode é perder a referência do começo do bloco -> aí depende do dev, não da linguagem.
Pegadinha: p + i não é p = p + i*(p + 3) só calcula o endereço 3 casas pra frente e lê. O p continua onde tava. Já p = p + 3 (ou p++) move o p. Se você mover o único ponteiro que aponta pro começo de um bloco do malloc, perdeu o endereço que o free precisa.
malloc e free: pedir e devolver memória Visto em aula · 20/08
Explicando pro macacoComo pede memória pro SO? -> malloc (Memory Allocation): "fio, eu quero X de memória, blz?". O SO devolve o endereço do começo do bloco. Não tem mais memória? -> devolve NULL, e se você não checar, dá pau. Terminou de usar? -> free, devolve pro SO. Toda vez que der um malloc, tem que dar um free.
Lixo de memória (vazamento): se o p passar a apontar pra outro lugar antes do free, aquele bloco fica reservado e ninguém mais acha ele -> não dá pra limpar. Faz isso num laço e o programa vai comendo memória (o Chrome que o diga).
Ajuste fino na anotação de 20/08Não precisa desligar o PC pra memória voltar: quando o programa fecha, o SO pega de volta tudo que ele alocou. O vazamento dói em programa que fica aberto muito tempo (navegador, servidor), porque ele vai acumulando lixo enquanto roda.
realloc: aumentar ou encolher o bloco Não visto em aula · a lista cobra (questão 3)
A questão 3 pede remover valores "redimensionando a região de memória quando necessário". Isso é o realloc. Imagina o seguinte -> você tem um bloco de n ints e agora só precisa de k. realloc(v, k * sizeof(int)) -> "SO, muda meu terreno pra esse tamanho e leva a mobília junto". Ele mantém os dados que cabem e devolve o endereço novo (pode mudar de lugar!).
Por que o ponteiro novo, e não v = realloc(v, …) direto? -> se o realloc falhar, ele devolve NULL. Aí você sobrescreveu o v com NULL e perdeu o único endereço do bloco antigo -> lixo de memória, olha ele de novo.
Função
O que faz
Detalhe que cai
malloc(bytes)
reserva memória
vem com lixo dentro; retorna NULL se falhar
calloc(qtd, tam)não visto
reserva e zera
tudo começa com 0
realloc(p, bytes)não visto
muda o tamanho
pode mudar o endereço; mantém os dados antigos
free(p)
devolve
depois disso, não usa mais p
sizeof(tipo)
tamanho em bytes
sizeof(int) costuma ser 4
Os 4 erros que o professor procura
1) Esquecer o free -> vazamento de memória.
2) Usar o ponteiro depois do free -> ponteiro pendurado, lixo.
3) Esquecer o sizeof -> malloc(n) reserva n bytes, não n inteiros.
4) v = realloc(v, …) direto -> se falhar, perdeu o bloco antigo.
As operações da questão 3, uma por uma Ponteiros: 20/08Remover com realloc: não visto
Maior e menor: começa supondo que o primeiro é o maior e o menor, percorre o resto atualizando. Média: soma tudo e divide por n, convertendo pra double antes (senão divisão inteira corta as casas).
Inverter sem outro vetor: um ponteiro no começo, outro no fim. Troca, anda os dois pro meio, para quando se cruzarem.
Remover todas as ocorrências de um valor: dois índices. O i lê todo mundo; o k marca onde escrever o próximo que fica. No fim, k é o tamanho novo -> realloc encolhe o bloco.
E se eu quiser juntar as variáveis num tipo só? -> struct -> registro -> conjunto de variáveis de tipos diferentes. É um modelo: cria uma vez, usa quantas fichas quiser.
Explicando pro macacoUm int guarda um número solto. Mas aluno tem nome, idade e nota, e isso tem que andar grudado. A struct é a ficha de papel com os campos. Faz o molde da ficha uma vez e imprime quantas quiser.
Do jeito que foi na aula (struct.c, 13/08):
Com typedef (jeito do material do professor) você dá um apelido e para de escrever struct toda hora. Os dois jeitos valem na prova:
Vetor de fichas com malloc Visto em aula · struct 13/08 + malloc 20/08
A aula fez aluno[10] fixo. A lista quer alocado dinamicamente -> junta as duas aulas:
Busca sequencial: lista de chamada Não visto em aula · a lista cobra
Explicando pro macacoO professor procura seu nome na lista de chamada lendo de cima pra baixo, um por um, até achar ou a lista acabar. Funciona com a lista em qualquer ordem. 1000 nomes -> até 1000 olhadas. O(n).
Busca binária: adivinha o número No material do professor
Explicando pro macaco"Pensei num número de 1 a 100." Chuta 50. "É maior." -> jogou fora 50 números de uma vez. Chuta 75, depois 62… Em no máximo 7 chutes acerta. Só funciona porque os números tão em ordem: o "é maior" diz pra que lado ir.
Procurando o RA 5030 no vetor ordenado:
3050 < 5030 -> tudo à esquerda do meio vai pro lixo. Na 2ª olhada, achou. A sequencial ia precisar de 6.
Tamanho
Sequencial (pior)
Binária (pior)
1.000
1.000
10
1.000.000
1.000.000
20
1.000.000.000
1.000.000.000
30
Por que a binária exige vetor ordenado (a lista pede pra demonstrar) No material do professor
Mesmo RA 5030, agora no vetor desordenado. Ele tá na posição 0.
O meio é 2040. Como 2040 < 5030, a busca conclui que o 5030 "só pode estar à direita" -> joga fora a esquerda, onde ele tava de verdade. Resultado: "não encontrado", com o aluno cadastrado lá. Logo, descartar metade só vale se a ordem garantir que tudo à esquerda é menor.
Ordenar os alunos pelo RA No material do professor
Qualquer ordenação serve; aqui foi Insertion. O ponto crítico -> mover a struct inteira. Trocou só os RAs? -> o RA de um aluno fica com o nome e a nota de outro. Bagunça total.
Programa completo da questão 2
--- Vetor DESORDENADO, buscando RA 5030 ---
[sequencial] encontrado com 1 comparacoes: RA 5030 | Ana | nota 8.5
[binaria] NAO encontrado (3 comparacoes)
--- Vetor ORDENADO por RA ---
[sequencial] encontrado com 6 comparacoes: RA 5030 | Ana | nota 8.5
[binaria] encontrado com 2 comparacoes: RA 5030 | Ana | nota 8.5
Módulo 6Tabela hash
Explicando pro macacoGuarda-volumes com 10 escaninhos, de 0 a 9. Regra da casa -> "seu escaninho é o último dígito do seu código". Código 137 -> escaninho 7. Pra achar o 137 depois, ninguém procura em todos: vai direto no 7. Essa regra é a função hash. Achar algo vira quase 1 passo, não importa quantos itens tenha.
Função hash No material do professor
No hash_vetor.c e no tabela_hash_encadeamento.c do professor é exatamente isso: chave % 10. Boa função hash -> rápida e espalha as chaves. Tamanho primo (11, 13) costuma espalhar melhor que 10.
Colisão: dois na mesma gaveta No material do professor
101 e 111 dão resto 1 -> os dois querem o escaninho 1. Isso é colisão, e não tem como fugir: tem muito mais código possível do que gaveta. A lista exige tratar, com uma de duas estratégias:
Estratégia 1: encadeamento (lista em cada gaveta) No material do professor
Explicando pro macacoCada escaninho tem um gancho. Chegou alguém pra gaveta ocupada? -> pendura no gancho, vira uma corrente. Cada elo sabe quem é o próximo. O último aponta pra "ninguém" (NULL).
[0]NULL
[1]121 Café→111 Açúcar→101 Arroz→ NULL
[5]205 Feijão→ NULL
[7]37 Leite→ NULL
Lembra da aula de 20/08, "um ponteiro que aponta pra outro ponteiro numa lista"? -> é isso aqui: lista encadeada. Cada nó é uma struct com os dados e um ponteiro pro próximo nó. Vantagem -> guarda valores sem precisar de um bloco grande contínuo, e percorre a lista seguindo os ponteiros.
Pra percorrer uma gaveta -> começa em tabela[g] e segue atual = atual->proximo até dar NULL. Pra remover um nó do meio -> o anterior "pula" o removido: anterior->proximo = atual->proximo, depois free(atual).
E qual o rolê de uma lista que volta? -> ponteiro pra frente e ponteiro pra trás (proximo e anterior no mesmo nó) -> lista duplamente encadeada. Pra hash não precisa, a simples resolve.
Estratégia 2: endereçamento aberto (sondagem linear) Não visto em aula · opcional: a lista aceita encadeamento
Explicando pro macacoEstacionamento. Sua vaga é a 1, mas tem carro? -> tenta a 2. Ocupada? -> tenta a 3. Para na primeira livre. Pra achar seu carro depois, faz o mesmo caminho: começa na 1 e anda até achar, ou até topar com uma vaga que nunca foi usada.
[1]101vaga dele
[2]111queria a 1, andou 1 passo
[3]121queria a 1, andou 2 passos
Tudo fica no próprio vetor, sem ponteiro. Detalhe que cai -> ao remover, marca a vaga como "removida" em vez de esvaziar. Esvaziou a vaga 2? -> a busca pelo 121 para nela achando que ele não existe.
Programa completo da questão 4 (encadeamento)
Inserir, pesquisar pelo código, alterar estoque, remover, exibir, com colisão provocada de propósito (101, 111 e 121 caem todos na gaveta 1).
COLISAO: codigo 111 caiu na gaveta 1, que ja tinha itens
COLISAO: codigo 121 caiu na gaveta 1, que ja tinha itens
[0] -> NULL
[1] -> {121, Cafe, 15} -> {111, Acucar, 20} -> {101, Arroz, 50} -> NULL
[5] -> {205, Feijao, 30} -> NULL
[7] -> {37, Leite, 40} -> NULL
Busca 101: Arroz, estoque 50 (3 comparacoes)
Custo: procurar em todos × usar hash No material do professor
Busca sequencial
Tabela hash
Caso médio
O(n): olha, em média, metade dos registros
O(1): calcula a gaveta e olha poucos itens
Pior caso
O(n)
O(n): todas as chaves colidiram na mesma gaveta
Precisa ordenar?
não
não
Fator de carga α = elementos ÷ gavetas -> tamanho médio de cada corrente. 5 produtos em 10 gavetas -> α = 0,5. Quanto menor o α, mais perto de O(1).
Resposta pronta pra "explique a diferença de custo"Na busca sequencial, o programa compara a chave com cada registro até encontrá-lo; com n registros, são até n comparações, custo O(n). Na tabela hash, a função hash calcula diretamente a posição da chave, então a busca examina apenas os poucos elementos daquela posição. Com uma boa função hash e fator de carga baixo, o custo médio é O(1), independente de n. O pior caso da hash é O(n), quando todas as chaves colidem na mesma posição.
Módulo 7Bônus: os outros quatro da apostila Só na apostila
A lista não pede, mas a apostila do professor traz. Sobrou tempo? -> saiba pelo menos a ideia e o Big O de cada um.
Shell Sort -> Insertion com "saltos". Primeiro ordena elementos distantes (intervalo n/2, depois n/4…) e termina com intervalo 1, que é um Insertion normal. Pior caso O(n²) com intervalos pela metade. Não é estável.
Heap Sort -> monta um heap máximo (árvore guardada no vetor em que todo pai ≥ filhos; filhos de i ficam em 2i+1 e 2i+2). Tira o maior da raiz, joga no fim, conserta o heap, repete. O(n log n) no pior caso e memória O(1). Não é estável.
Counting Sort -> não compara ninguém. Conta quantas vezes cada valor aparece (vetor de contagens de 0 até k−1) e usa as somas acumuladas pra posicionar. O(n + k). Só serve pra inteiro num intervalo pequeno. Estável.
Radix Sort -> ordena dígito por dígito, das unidades pras centenas, com uma ordenação estável (Counting) em cada passada. O(d·(n + b)), com d dígitos e base b. Estável.
Algoritmo
Melhor
Pior
Memória extra
Estável
Insertion
O(n)
O(n²)
O(1)
sim
Bubble
O(n)
O(n²)
O(1)
sim
Selection
O(n²)
O(n²)
O(1)
não
Quick
O(n log n)
O(n²)
O(log n) pilha
não
Merge
O(n log n)
O(n log n)
O(n)
sim
Shell
O(n log n)
O(n²)
O(1)
não
Heap
O(n log n)*
O(n log n)
O(1)
não
Counting
O(n + k)
O(n + k)
O(n + k)
sim
Radix
O(d(n + b))
O(d(n + b))
O(n + b)
sim
* A apostila observa que, com todas as chaves iguais, a versão dela do Heap Sort termina em O(n).
Módulo 8Na hora da prova
Esqueleto pra começar qualquer questão
Checklist antes de entregar
Todo malloc tem teste de NULL e um free no fim.
Função fora do main, e mexendo no parâmetro dela (não na variável do main). Foi o erro do teste.c.
Laço de 0 até < n. Nada de v[n].
Cada ordenação roda numa cópia do vetor original, com os contadores zerados antes.
Quick e Merge -> chamada inicial com 0 e n - 1. Caso base inicio < fim.
Busca binária -> vetor ordenado antes. meio = inicio + (fim - inicio) / 2. Laço while (inicio <= fim).
Texto -> strcpy pra copiar, strcmp pra comparar. Nada de = ou == em texto.
Struct por ponteiro usa ->; struct direta usa ..
Hash -> trata código negativo; remove com o ponteiro anterior; libera cada nó.
Perguntas teóricas prováveis, com resposta curta
Pergunta
Resposta em uma frase
Por que a busca binária exige ordenação?
Porque ela descarta metade do vetor comparando com o meio, e isso só é correto se tudo à esquerda for menor e tudo à direita for maior.
Qual ordenação foi melhor nos testes?
Quick e Merge, com muito menos comparações, porque são O(n log n) contra O(n²) dos simples; a diferença cresce com N.
Quando o Quick Sort fica O(n²)?
Quando o pivô é sempre o menor ou o maior elemento, por exemplo com vetor já ordenado e pivô no último.
Por que o Merge usa mais memória?
Porque intercala as metades num vetor auxiliar de tamanho n.
Memória estática × dinâmica?
Estática tem tamanho fixo definido no código; dinâmica é pedida ao SO em tempo de execução com malloc e devolvida com free.
O que é colisão?
Duas chaves diferentes que a função hash manda para a mesma posição.
O que acontece sem free?
Vazamento de memória: o bloco fica reservado até o programa acabar.
Diferença entre *p e &x?
&x pega o endereço de x; *p acessa o valor guardado no endereço que p contém.
O que p[i] significa?
É o mesmo que *(p + i): desloca i elementos a partir de p e acessa o conteúdo.
Algoritmo estável?
Mantém a ordem original entre elementos de chave igual. Bubble, Insertion e Merge são; Selection e Quick não.
Treino
Cada bolinha é uma lição curta. Errou? A questão volta no fim da lição. Bolinha verde = feita; amarela = feita sem errar nada.
Laboratório
Veja os algoritmos rodando passo a passo. Aperte "Próximo passo" e leia a narração: é exatamente o que o código em C está fazendo naquele momento.
Ordenação passo a passo
comparandotrocando / movendopivôlugar definitivo
Passo 0/0Comparações 0Trocas/movimentações 0
Escolha um algoritmo e aperte "Próximo passo".
Busca binária
Comparações 0
Digite um número e aperte "Próximo passo".
Tabela hash (10 gavetas)
A função hash é codigo % 10. Insira 101, depois 111: os dois caem na gaveta 1.
Elementos 0Fator de carga 0.00Colisões 0
Simulado
20 questões sorteadas de todos os módulos, sem ver a resposta durante a prova. No fim você recebe a nota de 0 a 10, a nota por assunto e a correção comentada de cada questão.
Histórico
Nenhum simulado feito ainda.
Vídeos
Todos os links foram conferidos. A maioria é do Prof. André Backes (Programação Descomplicada), que tem a sequência mais completa sobre esses assuntos em português. As danças húngaras (AlgoRythmics) não têm fala: dá para assistir no ônibus.
Resumão
Uma página para reler antes de entrar na sala.
C básico
v[0] é o primeiro; v[n-1] o último. 7/2 = 3; 7%2 = 1. = guarda, == compara. Texto: strcpy, strcmp(a,b)==0.
Big O
O(1) acesso direto e hash média · O(log n) busca binária · O(n) sequencial · O(n log n) Merge, Quick médio · O(n²) Bubble, Selection, Insertion, Quick pior. N×10: n² faz ×100; n log n faz ≈ ×13.
Bubble
Troca vizinhos; maior vai para o fim. Parada antecipada se não trocou. Melhor O(n), pior O(n²). Estável.
Selection
Acha o menor do resto, troca com a posição i. Sempre n(n−1)/2 comparações, O(n²). Até n−1 trocas. Não estável.
Insertion
Guarda chave = v[i], empurra maiores para a direita, encaixa em v[j+1]. Melhor O(n), pior O(n²). Estável.
Quick
Pivô = último. i = inicio-1; se v[j] <= pivo: i++, troca. No fim troca v[i+1] com v[fim]. Recursão em (inicio, p-1) e (p+1, fim). O(n log n) médio, O(n²) pior. Não estável.
Merge
meio = inicio + (fim-inicio)/2; ordena metades; intercala com <= num auxiliar. O(n log n) sempre, O(n) memória. Estável.
Ponteiros
&x endereço · *p conteúdo · v[i] == *(v+i) · p->campo == (*p).campo. Função que altera a variável de fora recebe o endereço.
Memória
int *v = malloc(n * sizeof(int)); if (v == NULL) … · novo = realloc(v, k * sizeof(int)); if (novo) v = novo; · free(v); v = NULL; · malloc tem lixo, calloc zera.
Buscas
Sequencial: qualquer ordem, O(n). Binária: só ordenado, while (inicio <= fim), descarta metade, O(log n). 1000 itens: 10 olhadas.
Hash
h = cod % TAM; if (h < 0) h += TAM; Colisão = chaves diferentes, mesma posição. Encadeamento: lista por gaveta (proximo, NULL). Aberto: tenta (h+t) % TAM; remoção marca "removido". Busca O(1) médio, O(n) pior. α = n / TAM.
Frases prontas
Binária exige ordem porque decide o lado comparando com o meio. Quick/Merge venceram por serem O(n log n). Hash calcula a posição em vez de percorrer tudo.