Estruturas de Dados em CED em C · prova
XP 0 Sequência 0d Meta do dia

Do zero até a prova de Estruturas de Dados

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 listaAssuntos que você precisa dominarNa aula?Módulo
1. Ordenação e desempenhoBubble, Insertion, Selection, Quick, Merge; contar comparações e trocas; Big OBubble · 13/08 resto no material contadores: não1, 2, 3
2. Cadastro com buscastruct, vetor dinâmico, busca sequencial e binária, ordenar por RAstruct · 13/08 binária no material sequencial: não5
3. Ponteiros e memória&, *, malloc, realloc, free, inverter no lugar, remover valoresponteiro, malloc, free · 20/08 realloc: não4
4. Tabela hashfunção hash, colisão, encadeamento, endereçamento aberto, custo O(1) × O(n)hash e encadeamento no material endereç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?
TipoGuardaExemploNo printf/scanf
intinteiroint ra = 12345;%d
float / doublenúmero com vírgula (em C se escreve ponto)float nota = 7.5;%f (%.2f = 2 casas)
charuma letrachar letra = 'A';%c
char[50]texto (string) de até 49 letraschar 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 ONomeExemplo da matéria
Nenhum laço, só contas e acesso v[i]O(1)constanteacessar v[5]; hash no caso bom
Um laço que corta o problema pela metade a cada voltaO(log n)logarítmicobusca binária
Um laço que passa por todosO(n)linearbusca 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é nO(n²)quadráticoBubble, Selection, Insertion

Tá, mas por que isso importa? -> os números No material do professor

nlog₂ nn log₂ nn²
10333100
1.000109.9661.000.000
1.000.00020≈ 20 milhões1 trilhão

Rodei o programa da questão 1 pra montar este guia. Olha o que deu:

AlgoritmoComparações, N = 1.000Comparações, N = 10.000Cresceu
Bubble499.36449.992.789≈ 100× (n²: 10× mais dados, 100× mais trabalho)
Selection499.50049.995.000≈ 100×
Insertion248.21325.231.383≈ 100×
Quick11.417180.540≈ 16× (n log n)
Merge8.692120.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

IdeiaMelhorPiorTrocasEstável
Bubbletroca vizinhos; maior vai pro fimO(n)O(n²)muitassim
Selectionacha o menor; põe na frenteO(n²)O(n²)no máx. n−1não
Insertionencaixa cada carta na parte arrumadaO(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.

Intercalando [3, 7] com [2, 5] -> 3 × 2, pega 2. 3 × 5, pega 3. 7 × 5, pega 5. Sobrou o 7, copia. Deu [2, 3, 5, 7].

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 SortMerge Sort
Melhor / médioO(n log n)O(n log n)
Pior casoO(n²) (pivô ruim, ex.: vetor já ordenado)O(n log n)
Memória extrasó a pilha da recursão (log n)vetor auxiliar O(n)
Estávelnãosim
Onde tá o trabalhoantes 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):

Original:  467 103 223 436 266 753 489 370 124 226
Bubble     comparacoes:         44   trocas/mov.:         23
Insertion  comparacoes:         31   trocas/mov.:         23
Selection  comparacoes:         45   trocas/mov.:          7
Quick      comparacoes:         19   trocas/mov.:         13
Merge      comparacoes:         21   trocas/mov.:         34
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ímboloLê assimExemplo
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çãoO que fazDetalhe que cai
malloc(bytes)reserva memóriavem com lixo dentro; retorna NULL se falhar
calloc(qtd, tam) não vistoreserva e zeratudo começa com 0
realloc(p, bytes) não vistomuda o tamanhopode mudar o endereço; mantém os dados antigos
free(p)devolvedepois disso, não usa mais p
sizeof(tipo)tamanho em bytessizeof(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.

Rodando com 4 9 4 1 7 4 e removendo o 4:

Maior: 9 | Menor: 1 | Media: 4.83
Invertido: 4 7 1 4 9 4
Depois de remover (3 elementos): 7 1 9

Módulo 5Struct e buscas (sequencial e binária)

struct = ficha de cadastro Visto em aula · 13/08

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.

TamanhoSequencial (pior)Binária (pior)
1.0001.00010
1.000.0001.000.00020
1.000.000.0001.000.000.00030

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 sequencialTabela hash
Caso médioO(n): olha, em média, metade dos registrosO(1): calcula a gaveta e olha poucos itens
Pior casoO(n)O(n): todas as chaves colidiram na mesma gaveta
Precisa ordenar?nãonã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.
AlgoritmoMelhorPiorMemória extraEstável
InsertionO(n)O(n²)O(1)sim
BubbleO(n)O(n²)O(1)sim
SelectionO(n²)O(n²)O(1)não
QuickO(n log n)O(n²)O(log n) pilhanão
MergeO(n log n)O(n log n)O(n)sim
ShellO(n log n)O(n²)O(1)não
HeapO(n log n)*O(n log n)O(1)não
CountingO(n + k)O(n + k)O(n + k)sim
RadixO(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

PerguntaResposta 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.