INF01203 — Estruturas de Dados — Simulado
Conteúdo: Ponteiros • Structs • Listas • Pilha • Fila • ABP
Universidade Federal do Rio Grande do Sul
Instituto de Informática
Prof. Dennis G. Balreira
Semestre 2026/2 — Turma C
Nome:   Cartão:  

Parte 1 — Questões Teóricas (20 questões)

Responda de forma breve e objetiva. Uma a três frases por questão são suficientes.

Q1. (TAD) O que é um Tipo Abstrato de Dados (TAD)? Qual a diferença entre a interface de um TAD e sua implementação? Cite um exemplo de TAD visto em aula.
Q2. (Alocação dinâmica) Por que a alocação dinâmica (malloc/free) é essencial para implementar TADs como listas encadeadas? O que acontece se o programador não chamar free ao final do uso de um nodo?
Q3. (Complexidade) O que significa dizer que um algoritmo tem complexidade O(n)? Cite um exemplo de operação O(1) e um de O(n) em listas encadeadas.
Q4. (Lista contígua) O que é uma lista linear com contiguidade física (baseada em vetor)? Qual a complexidade de inserção no início dessa lista? Por quê ela é pior do que em uma lista encadeada?
Q5. (Lista simples) Descreva a estrutura interna de uma lista simplesmente encadeada: o que é o nodo, o que é o descritor (ListaEnc) e qual o papel do ponteiro ini.
Q6. (Lista simples) Por que a função inserirInicio(ListaEnc *lista, Produto p) recebe o descritor por ponteiro em vez de por valor? O que ocorreria de errado se recebesse por valor?
Q7. (Lista simples) Descreva os passos para remover o primeiro nodo de uma lista simplesmente encadeada. O que deve ser feito com a memória do nodo removido?
Q8. (Lista dupla) O que é uma lista duplamente encadeada? Qual a vantagem do ponteiro ant (anterior) em relação à lista simplesmente encadeada, especialmente na remoção?
Q9. (Lista dupla) Ao remover um nodo do meio de uma lista duplamente encadeada, quais ponteiros devem ser atualizados? Descreva os passos, incluindo o que fazer com os vizinhos do nodo removido.
Q10. (Lista circular) O que é uma lista circular? Como se detecta que a varredura percorreu todos os elementos se não há NULL para indicar o fim?
Q11. (Insertion Sort) Descreva o funcionamento do Insertion Sort. Qual é sua complexidade no melhor caso e no pior caso? Quando ocorre cada um?
Q12. (Shell Sort) Qual é a ideia central do Shell Sort em relação ao Insertion Sort? Por que o Shell Sort tende a ser mais eficiente para vetores grandes?
Q13. (Pilha) O que é o princípio LIFO? Quais são as operações do TAD PilhaEnc? Onde (início ou fim da lista interna) ocorrem push e pop, e por quê?
Q14. (Pilha — aplicação) Cite uma aplicação prática de pilha na computação (além de “pilha de pratos”) e explique como a pilha é usada nesse contexto.
Q15. (Fila) O que é o princípio FIFO? No TAD FilaEnc, onde ocorre o enqueue e onde ocorre o dequeue? Por que manter um ponteiro fim é fundamental para a eficiência?
Q16. (Deque) O que é um deque (double-ended queue)? Em que sentido ele generaliza tanto a pilha quanto a fila? Cite as operações que ele oferece.
Q17. (Árvores) Defina: raiz, folha, altura de uma árvore e nível de um nodo. Qual é a altura de uma árvore com apenas um nodo (a própria raiz)?
Q18. (Árvores binárias) Quais são os três percursos clássicos de uma árvore binária? Descreva a ordem de visita de cada um (pré-ordem, in-ordem, pós-ordem).
Q19. (ABP) Enuncie a propriedade de uma Árvore Binária de Pesquisa. Por que o percurso in-ordem em uma ABP produz os elementos em ordem crescente?
Q20. (ABP) Quais são os três casos para remoção de um nodo em uma ABP? Descreva cada um e diga o que é o sucessor in-ordem, usado no caso mais complexo.

Parte 2 — Questões de Código (9 questões)

Para todas as questões abaixo, considere os tipos e TADs definidos em aula:

typedef struct {
    int cod; char nome[50]; float preco;
} Produto;

/* Lista simplesmente encadeada */
typedef struct str_Nodo Nodo;
struct str_Nodo { Nodo *prox; Produto dado; };
typedef struct { Nodo *ini; } ListaEnc;

/* TAD Pilha (implementada com lista encadeada) */
typedef struct str_NodoPilha NodoPilha;
struct str_NodoPilha { NodoPilha *prox; Produto dado; };
typedef struct { NodoPilha *topo; } PilhaEnc;
void    inicializaPilha(PilhaEnc *p);  /* p->topo = NULL        */
int     estaVaziaPilha(PilhaEnc *p);  /* retorna 1 se vazia    */
void    push(PilhaEnc *p, Produto d); /* insere no topo        */
Produto pop(PilhaEnc *p);             /* remove e retorna topo */

/* TAD Fila (implementada com lista encadeada) */
typedef struct str_NodoFila NodoFila;
struct str_NodoFila { NodoFila *prox; Produto dado; };
typedef struct { NodoFila *ini; NodoFila *fim; } FilaEnc;
void    inicializaFila(FilaEnc *f);   /* f->ini = f->fim = NULL */
int     estaVaziaFila(FilaEnc *f);   /* retorna 1 se vazia     */
void    enqueue(FilaEnc *f, Produto d); /* insere no fim       */
Produto dequeue(FilaEnc *f);          /* remove e retorna ini  */

/* Árvore Binária de Pesquisa */
typedef struct str_NodoArv NodoArv;
struct str_NodoArv { Produto dado; NodoArv *esq; NodoArv *dir; };

Grupo A — Lista Simplesmente Encadeada

A1. Implemente maiorPreco, que percorre a lista e retorna o produto de maior preco. Assuma lista não vazia.
Produto maiorPreco(const ListaEnc *lista);
A2. Implemente somarPrecosImpares, que percorre a lista e retorna a soma dos preco dos produtos cujo cod seja ímpar. Retorne 0.0 para lista vazia.
float somarPrecosImpares(const ListaEnc *lista);
A3. Implemente removerMenoresQuePreco, que remove da lista todos os produtos com preco estritamente menor que p. Libere cada nodo com free. Trate múltiplos consecutivos no início.
void removerMenoresQuePreco(ListaEnc *lista, float p);
A4. Implemente concatenarListas, que concatena b ao final de a sem criar novos nodos (apenas redirecione ponteiros). Trate os casos em que a ou b estejam vazias.
void concatenarListas(ListaEnc *a, const ListaEnc *b);
A5. Implemente inverterLista, que inverte a ordem dos nodos in-place, sem criar novos nodos. Use três ponteiros auxiliares para redirecionar os campos prox.
void inverterLista(ListaEnc *lista);
A6. Implemente moverPrimeiroParaFim, que move o primeiro nodo da lista para o final sem criar ou destruir nodos. Não faça nada se a lista for vazia ou unitária.
void moverPrimeiroParaFim(ListaEnc *lista);
A7. Implemente inserirAntesDeCod, que insere um novo produto imediatamente antes do primeiro nodo cujo cod seja igual ao parâmetro. Se o código não existir, insira o novo produto no final da lista.
void inserirAntesDeCod(ListaEnc *lista, Produto p, int cod);
A8. Implemente segundoMaiorPreco, que percorre a lista e retorna o segundo maior preço distinto. Assuma preços não-negativos; retorne -1.0 se não houver segundo preço distinto.
float segundoMaiorPreco(const ListaEnc *lista);

Grupo B — Pilha e Fila (usando apenas o TAD)

B1. Implemente maiorPrecoFila, que retorna o produto de maior preco da fila sem alterar a ordem dos elementos. Use apenas operações do TAD.
Produto maiorPrecoFila(FilaEnc *fila);
B2. Implemente maiorPrecoPilha, que retorna o produto de maior preco da pilha sem alterar a ordem dos elementos. Use apenas operações do TAD.
Produto maiorPrecoPilha(PilhaEnc *pilha);
B3. Implemente removerDaFilaPorCod, que remove da fila o primeiro produto cujo cod seja igual ao parâmetro, preservando a ordem dos demais. Use apenas operações do TAD.
void removerDaFilaPorCod(FilaEnc *fila, int cod);
B4. Implemente removerDaPilhaPorCod, que remove da pilha o primeiro produto (do topo para a base) cujo cod seja igual ao parâmetro, preservando a ordem dos demais. Use apenas operações do TAD.
void removerDaPilhaPorCod(PilhaEnc *pilha, int cod);
B5. Implemente separarParImpar, que separa os elementos de fila em duas novas filas: pares (cod par) e impares (cod ímpar), preservando a ordem original em cada fila. A fila original é consumida.
void separarParImpar(FilaEnc *fila, FilaEnc *pares, FilaEnc *impares);
B6. Implemente somarPrecosImparesPreservando, que retorna a soma dos preco dos produtos com cod ímpar, preservando o conteúdo e a ordem da fila. Use apenas operações do TAD.
float somarPrecosImparesPreservando(FilaEnc *fila);
B7. Implemente temConsecutivosIguais, que retorna 1 se existem dois produtos consecutivos com o mesmo cod na fila, e 0 caso contrário. A fila deve ser preservada. Use apenas operações do TAD.
int temConsecutivosIguais(FilaEnc *fila);
B8. Implemente mesmasElementos, que retorna 1 se a pilha e a fila possuem a mesma quantidade de elementos e os mesmos cod na mesma ordem (topo da pilha corresponde à frente da fila), e 0 caso contrário. Ambas devem ser preservadas. Use apenas operações do TAD.
int mesmasElementos(PilhaEnc *pilha, FilaEnc *fila);

Grupo C — Árvore Binária de Pesquisa (recursiva)

C1. Implemente recursivamente abpTodosPares, que retorna 1 se todos os produtos possuem cod par, e 0 se algum é ímpar. Retorne 1 para árvore vazia.
int abpTodosPares(const NodoArv *raiz);
C2. Implemente recursivamente abpContarComUmFilho, que retorna a quantidade de nodos com exatamente um filho (esquerdo ou direito, mas não ambos).
int abpContarComUmFilho(const NodoArv *raiz);
C3. Implemente recursivamente abpContarMenoresQue, que retorna a quantidade de produtos cujo cod é estritamente menor que x. Dica: use a propriedade da ABP para podar subárvores.
int abpContarMenoresQue(const NodoArv *raiz, int x);
C4. Implemente recursivamente abpMaiorCod, que retorna o maior cod aproveitando a propriedade da ABP (sem percorrer toda a árvore). Assuma árvore não vazia.
int abpMaiorCod(const NodoArv *raiz);
C5. Implemente recursivamente abpImprimirIntervalo, que imprime em ordem crescente todos os produtos com cod ∈ [minCod, maxCod]. Dica: use a propriedade da ABP para evitar subárvores fora do intervalo.
void abpImprimirIntervalo(const NodoArv *raiz, int minCod, int maxCod);
C6. Implemente recursivamente abpContarMaioresQue, que retorna a quantidade de produtos cujo cod é estritamente maior que x. Dica: use a propriedade da ABP para podar subárvores.
int abpContarMaioresQue(const NodoArv *raiz, int x);
C7. Implemente recursivamente abpExisteNoIntervalo, que retorna 1 se existir pelo menos um produto com cod ∈ [minCod, maxCod], e 0 caso contrário. Dica: use a propriedade da ABP para retornar antecipadamente.
int abpExisteNoIntervalo(const NodoArv *raiz, int minCod, int maxCod);
C8. Implemente recursivamente abpSomaCods, que retorna a soma de todos os cod armazenados na árvore. Retorne 0 para árvore vazia.
int abpSomaCods(const NodoArv *raiz);