UNIVERSIDADE DA BEIRA INTERIOR Programação II 2º Semestre Exame Época Norma (15 val) Resolução 22/06/2018 1 [1,00 val + 1,00 val] - Memória Dinâmica Considere as seguintes declarações de variáveis: int *V, **W, N; e que sizeof(int) = 4 a) Preencha o esquema dado na folha de resposta (em anexo) tendo em conta a execução do seguinte bloco de instruções em linguagem C: V = (int *) malloc (2*sizeof(int)); *V = 900; N = 1; do { V[N] = N * 500; N = N + 1; V = (int *) realloc(v, (N+3) * sizeof(int)); while (N < 4); *(*W + 1) = 600; *(V + 6) = 400; (*W)[0] = 50; 1
1 a) 1 b) (esquema de um bloco de memória) 100500 110180 100700 110164 V 100800 1 2 3-4 N 110160 100 110164 900 110168 500 EXPRESSÃO VALOR *(V + 3) 1500 V - 3 110152 &V[6] 110188 &(*V + 2) Indefinido **W 50 (*W)[2] 400 *(*W + 3) 200 *W - 2 110172 NOTA: Caso o valor da expressão não seja conhecido deve responder com Indefinido ; ou seja, deve colocar na coluna VALOR na respetiva posição a palavra Indefinido 110172 1000 110176 1500 110180 50 110184 600 110188 400 110192 200 110500 100500 W
2 [2,00 val] - Memória Dinâmica Considere a seguinte definição de um tipo estrutura associada aos dados de uma pessoa: typedef struct { int numcc; // Número de Cartão de Cidadão int datanasc[3]; // Data de nascimento no formato dia/mês/ano (ex: 21/5/1985) float altura; // Altura em metros (exemplo: 185) int peso; // Peso em Kg (exemplo: 76) PESSOA; em que para a data 21/5/1985, datanasc[0] = 21, datanasc[1] = 5 e datanasc[2] = 1985 Usando memória dinâmica na manipulação de vetores, implemente uma função em C que a partir dos dados contidos no ficheiro de texto Pessoastxt, construa um vetor do tipo PESSOA apenas com os dados das pessoas nascidas na década de 1990 (ou seja, entre 1990 e 1999, inclusive) PESSOA *LerVetorPessoasNascidasDecada1990 (int *tam) { PESSOA *V, aux; FILE *fin; fin = fopen( Pessoastxt, r ); if (fin == NUUL) return NULL; *tam = 0; V = (PESSOA *) malloc(0 * sizeof(pessoa)); while (fscanf(fin, %d %d %d %d %f %d, &auxnumcc, &auxdatanasc[0], &auxdatanasc[1], &auxdatanasc[2], &auxaltura, &auxpeso) == 6) if (auxdatanasc[2] >= 1990 && auxdatanasc[2] <= 1999) { *tam = *tam + 1; V = (PESSOA *) realloc(v, (*tam) * sizeof(pessoa)); V[*tam 1] = aux; fclose(fin); return V; 3 [2,50 val] Ficheiros Binários Considere a seguinte definição de um tipo estrutura associada aos dados de uma pessoa: typedef struct { int numcc; // Número de Cartão de Cidadão int datanasc[3]; // Data de nascimento no formato dia/mês/ano (ex: 21/5/1985) float altura; // Altura em metros (exemplo: 185) int peso; // Peso em Kg (exemplo: 76) PESSOA; em que para a data 21/5/1985, datanasc[0] = 21, datanasc[1] = 5 e datanasc[2] = 1985 3
Construa um programa em C que a partir dos dados contidos no ficheiro binário Pessoasbin construa um ficheiro binário de nome Decada1990bin com os dados de todas as pessoas nascidas na década de 1990 (ou seja, nascidas entre 1990 e 1999, inclusive) Para tal não pode usar vetores e deve usar a função fseek (para além de outras que achar necessárias) A estratégia é a seguinte: ler do ficheiro Pessoasbin apenas o ano de nascimento de cada pessoa (não pode ler toda a informação de cada pessoa) e, caso esta pessoa tenha nascido na década de 1990, toda a informação que lhe está associada (numcc, datanasc, altura e peso) deve ser guardada no ficheiro Decada1990bin #include <stdioh> typedef struct { int numcc; int datanasc[3]; float altura; int peso; PESSOA; int main(){ int ano; PESSOA aux; FILE *fin, *fout; fin = fopen( Pessoasbin, rb ); if (fin == NULL) return 0; fout = fopen( Decada1990bin, wb ); if (fout == NULL) return 0; fseek(fin, 3 * sizeof(int), SEEK_SET); // salta 3 int e coloca-se em datanasc[2] do 1º registo while (fread(&ano, sizeof(int), 1, fin) == 1) { // lê o ano do registo atual if (ano >= 1990 && ano <= 1999) { fseek(fin, -4 * sizeof(int), SEEK_CUR); // coloca no início do registo atual fread(&aux, sizeof(pessoa), 1, fin); // lê todo o registo atual do ficheiro de entrada fwrite(&aux, sizeof(pessoa), 1, fout); // acrescentar o registo lido ao ficheiro de saída fseek(fin, 3 * sizeof(int), SEEK_SET); // salta 3 int e coloca em datanasc[2] do próx reg else // salta 1 float e 1 int (altura e peso) do reg Atual, e 3 int e coloca datanasc[2] do próx reg fseek(fin, sizeof(float) + 4*sizeof(int), SEEK_CUR); fclose(fin); fclose(fout); return 1;
4 [1,50 val] - Recursividade Implemente uma função recursiva para verificar se uma dada password (sequência de carateres) existe ou não num dado vetor de passwords NOTA: Considere definido o tipo de dados PASSWORD que é uma string com 30 carateres int ExistePassword (PASSWORD pass, PASSWORD VP[], int N) { if (N == 0) return -1; // não existe if (strcmp(pass, VP[N-1]) == 0) // pass == VP[N-1] return 1; // existe return ExistePassword(pass, VP, N-1); 5 [1,00 val] - Análise de Complexidade dos Algoritmos Considere o seguinte bloco de instruções em linguagem C: fim = N % 10; for (k = 0; k < fim; k++) for (j = k; j < N; j++) printf( %d\n, j); Determine a ordem de complexidade do algoritmo (considerando o pior caso) associado à função dada Justifique, apresentando todos os cálculos efetuados Pode incidir os cálculos sobre a operação que achar que mais vezes é executada Basta determinar o número de vezes que a instrução de comparação j < N (A) é executada, pois é a mais vezes executada Fim 9 (pior caso) k = 0 (k < fim verdadeiro) j = 0, 1,, N-1 (j < N verdadeiro), N (j < N falso) ==> A é executada N+1 vezes k = 1 (k < fim verdadeiro) j = 1, 2,, N-1 (j < N verdadeiro), N (j < N falso) ==> A é executada N vezes k = 2 (k < fim verdadeiro) j = 2, 3,, N-1 (j < N verdadeiro), N (j < N falso) ==> A é executada N-1 vezes k = 8 (k < fim verdadeiro) j = 8, 9,, N-1 (j < N verdadeiro), N (j < N falso) ==> A é executada N-7 vezes k = 9 (k < fim falso) j não assume qualquer valor ==> A é executado 0 vezes 5
Assim sendo, A é executada as seguintes vezes: (N+1) + N + (N-1) + + (N-7) = ( ((N+1) + (N-7)) / 2) x 9 = (2 x N 6) / 2 x 9 = 9 x (N 3) = 9 x N - 27 Logo, a função dada tem ordem de complexidade O(N) ==> Ordem linear 6 [2,0 val + 2,0 val] - Algoritmos de Ordenação e de Pesquisa Considere a seguinte definição de um tipo estrutura associada aos dados de uma pessoa: typedef struct { int numcc; // Número de Cartão de Cidadão int datanasc[3]; // Data de nascimento no formato dia/mês/ano (ex: 21/5/1985) float altura; // Altura em metros (exemplo: 185) int peso; // Peso em Kg (exemplo: 76) PESSOA; em que para a data 21/5/1985, datanasc[0] = 21, datanasc[1] = 5 e datanasc[2] = 1985 Considere as seguintes funções (já implementadas): void OrdenarCC (PESSOA V[], int tam); // ordena crescentemente o vetor V pelo campo numcc int PesquisaBinariaCC (int CC, PESSOA V[], int tam); // devolve o índice de um elemento de V cujo campo numcc é igual a CC ou -1 (se não existe qualquer elemento) NOTA: As funções de ordenar e de pesquisar devem ser usadas de tal forma que sejam fundamentais na resolução dos problemas e que otimizem os algoritmos (com o menor número de operações possível) a) Implemente uma função em C que dados um vetor V do tipo PESSOA, ordenado por ordem crescente pelo campo peso, com N elementos e um elemento E do tipo PESSOA, insira no vetor V o elemento E de forma que o vetor V se mantenha ordenado pelo campo peso, mas sem usar qualquer algoritmo de ordenação PESSOA *InserirElementoVetorOrdenadoPeso (PESSOA Elem, PESSOA V[], int *N) { int k, j; k = 0; while (k < *N && V[k]peso < Elempeso) k = k + 1; // Inserir no índice k do vetor V o (novo) elemento Elem *N = *N + 1; V = (PESSOA *) realloc(v, (*N)*sizeof(PESSOA)); if (V == NULL) return NULL; for (j = *N-1; j > k; j--) V[j] = V[j-1]; V[k] = Elem; return V;
b) Usando as funções OrdenarCC e PesquisaBinariaCC, implemente uma função em C que dados um vetor V do tipo PESSOA com N elementos (parâmetros da função), permita ao utilizador inserir um valor inteiro num e imprima no ecrã toda a informação associada à pessoa com o numcc igual a num, fazendo isto as vezes que o utilizador desejar void MostrarElementosVetor (PESSOA V[], int N) { int k, num; OrdenarCC(V, N); // ordem crescente pelo campo NotaF printf( Insira um número CC (-1 para terminar): ); scanf( %d, &num); while (num!= -1) { k = PesquisaBinariaCC(num, V, N); if (k >= 0) { printf( Número de CC: %d\n, V[k]numCC); printf( Nascimento: %d/%d/%d\n, V[k]dataNasc[0], V[k]dataNasc[1],V[k]dataNasc[2]); printf( Altura: %f\n, V[k]altura); printf( Peso: %d\n, V[k]peso); printf( Insira um número CC (-1 para terminar): ); scanf( %d, &num); 7 [1,25 val + 0,25 val + 0,50 val] - Tabelas de Dispersão/Hash Considere uma tabela de hash T de tamanho 10 inicialmente vazia e a seguinte função de hash: h(k, i) = ( (k + 5) % 5 + i 2 ) % 10, i = 0, 1, 2, Responda às questões que se seguem, justificando com os cálculos efetuados a) Considerando que as colisões são tratadas com hashing fechado, desenhe uma tabela de hash após a inserção das seguintes chaves (pela ordem apresentada): 55, 60, 45, 64 e 79 h(55, 0) = ((55+5) % 5 + 0 2 ) % 10 = (60 % 5 + 0) % 10 = (0 + 0) % 10 = 0 h(60, 0) = ((60+5) % 5 + 0 2 ) % 10 = (65 % 5 + 0) % 10 = (0 + 0) % 10 = 0 (colisão com 55) h(60, 1) = ((60+5) % 5 + 1 2 ) % 10 = (65 % 5 + 1) % 10 = (0 + 1) % 10 = 1 h(45, 0) = ((45+5) % 5 + 0 2 ) % 10 = (50 % 5 + 0) % 10 = (0 + 0) % 10 = 0 (colisão com 55) h(45, 1) = ((45+5) % 5 + 1 2 ) % 10 = (50 % 5 + 1) % 10 = (0 + 1) % 10 = 1 (colisão com 60) h(45, 2) = ((45+5) % 5 + 2 2 ) % 10 = (50 % 5 + 4) % 10 = (0 + 4) % 10 = 4 h(64, 0) = ((64+5) % 5 + 0 2 ) % 10 = (69 % 5 + 0) % 10 = (4 + 0) % 10 = 4 (colisão com 45) h(64, 1) = ((64+5) % 5 + 1 2 ) % 10 = (69 % 5 + 1) % 10 = (4 + 1) % 10 = 5 7
h(79, 0) = ((79+5) % 5 + 0 2 ) % 10 = (84 % 5 + 0) % 10 = (4 + 0) % 10 = 4 (colisão com 45) h(79, 1) = ((79+5) % 5 + 1 2 ) % 10 = (84 % 5 + 1) % 10 = (4 + 1) % 10 = 5 (colisão com 64) h(79, 2) = ((79+5) % 5 + 2 2 ) % 10 = (84 % 5 + 4) % 10 = (4 + 4) % 10 = 8 A tabela fica da seguinte forma: 0 1 2 3 4 5 6 7 8 9 T 55 60 45 64 79 b) Redesenhe a tabela anterior após a remoção da chave 45 h(45, 0) = ((45+5) % 5 + 0 2 ) % 10 = (50 % 5 + 0) % 10 = (0 + 0) % 10 = 0 Como T[0] = 55 45, então continuar a procura do 45 para possível remoção h(45, 1) = ((45+5) % 5 + 1 2 ) % 10 = (50 % 5 + 1) % 10 = (0 + 1) % 10 = 1 Como T[1] = 60 45, então continuar a procura do 45 para possível remoção h(45, 2) = ((45+5) % 5 + 2 2 ) % 10 = (50 % 5 + 4) % 10 = (0 + 4) % 10 = 4 Como T[4] = 45 = 45, então remover 45 da tabela T, mas T[6] fica com o rótulo de Removido (livre mas já ocupado) A tabela T fica então da seguinte forma: 0 1 2 3 4 5 6 7 8 9 T 55 60 45 64 79 c) Redesenhe a tabela anterior após a inserção das chaves 70 h(70, 0) = ((70+5) % 5 + 0 2 ) % 10 = (75 % 5 + 0) % 10 = (0 + 0) % 10 = 0 (colisão com 55) h(70, 1) = ((70+5) % 5 + 1 2 ) % 10 = (75 % 5 + 1) % 10 = (0 + 1) % 10 = 1 (colisão com 60) h(70, 2) = ((70+5) % 5 + 2 2 ) % 10 = (75 % 5 + 4) % 10 = (0 + 4) % 10 = 4 Como a posição T[4] está livre, inserir a chave 70 nesta posição da tabela A tabela fica da seguinte forma: 0 1 2 3 4 5 6 7 8 9 T 55 60 70 64 79