1. Cotação de cada pergunta: 1. 30 / 2. 35 / 3. 35 (Total: 100 pontos) 2. Responda às questões de forma clara e concisa nas folhas de exame distribuídas. 1. (Valorização: 30%) Responda às seguintes questões: a) Suponha que existe uma classe abstracta com duas implementações diferentes: X e Y. Suponha que a classe inclui três métodos f, g e h. Os piores casos de comportamento assimptótico dos tempos de execução, para cada método, usando as duas implementações, são dados na tabela seguinte: f g h X O(1) O(n) O(log n) Y O(n) O(log n) O(1) Suponha que um algoritmo A faz O(n) chamadas a f, O(1) chamadas a g e O(n) chamadas a h. Indique qual das implementações terá um melhor comportamento assimptótico do tempo de execução para A? Explique a resposta. Para cada implementação temos de considerar o número de chamadas de cada função vezes o seu custo, assim temos: X: O(1) O(n)+O(n) O(1)+O(log n) O(n) =O(n + n + n log n) =O(n log n) Y: O(n) O(n)+O(log n) O(1)+O(1) O(n) =O(n 2 + n log n + n) =O(n 2 ) Como (n log n) < n 2, podemos dizer que X tem melhor comportamento assimptótico que Y (i.e. menor complexidade). b) Considere uma lista ligada ordenada de valores inteiros, com repetições, dada como atributo de uma classe, LinkedList<Integer> lista;. Escreva um método na mesma classe, public static int valormaisfrequente() que retorne o valor que ocorre mais frequentemente na lista ordenada. Por exemplo, considerando que a lista contém a seguinte sequência [1, 1, 2, 2, 2, 3, 5, 5, 5, 5, 6, 6, 6, 7, 9, 9, 10], então o método deverá devolver 5, pois o valor 5 aparece 4 vezes e é o que aparece mais vezes. Se dois ou mais valores ocorrerem o mesmo número de vezes e esse número for o máximo, retorna o primeiro valor mais frequente que aparece na lista. Pode assumir que a lista tem pelo menos um elemento, caso em que esse é o mais frequente. int valormaisfrequente() { int max, freqmax=0; int val, cur, freqval; Iterator i= lista.iterator(); val= max= i.next(); // primeiro da lista freqval= 1; while (i.hasnext()) { cur= i.next(); if (val==cur) freqval++; else { if (freqval>freqmax) { max= val; freqmax= freqval; val= cur;
Estruturas de dados (CC114) freqval=1; // verificar se a maior sequencia era a última da lista if (freqval>freqmax) max= val; return max; c) Considere uma heap representada pelo vector seguinte: c.1) Considere que se pretende introduzir o valor 41. Apresente o resultado final da heap, assim como todos os passos intermédios de reposição de condição de heap. Podemos representar o vector como uma árvore binária e depois aplicar as transformações, para isso temos de colocar os valores como se estivessem por níveis: 55 / \ 23 42 / \ / \ 21 15 28 35 / \ / 10 3 7 Quando introduzimos o valor 41, colocamo-lo na primeira posição livre do vector, i.e. como filho direito do nó 15 na árvore, e depois aplicamos a operação upheap() para posicionar correctamento o valor. O valor troca com o o valor do nó pai se tiver maior prioridade. 55 --> 55 --> 55 / \ / \ / \ 23 42 23 42 41 42 / \ / \ / \ / \ / \ / \ 21 15 28 35 21 41 28 35 21 23 28 35 / \ / \ / \ / \ / \ / \ 10 3 7 41 10 3 7 15 10 3 7 15 c.2) Considere novamente a heap no estado inicial dado. Apresente o resultado final da heap, assim como todos os passos intermédios de reposição de condição de heap, após remover o elemento com maior prioridade. Nota: pode responder a estas questões recorrendo a uma representação da heap como fila de prioridade, em árvore. Considerando a representação inicial: 55 / \ 23 42 / \ / \ 21 15 28 35 / \ / 10 3 7 Quando removemos a raíz, o valor 55, colocamos no seu lugar o valor mais à direita no último nível (ou o último no vector), i.e. colocamos o valor 7 no lugar do 55, e depois aplicamos a operação downheap() para fazer descer e repor a condição de heap. O valor troca com o valor de um dos nós filho, o de maior prioridade.
7 --> 42 --> 42 / \ / \ / \ 23 42 23 7 23 35 / \ / \ / \ / \ / \ / \ 21 15 28 35 21 15 28 35 21 15 28 7 / \ / \ / \ 10 3 10 3 10 3 2. (Valorização: 35%) Classes O DCC foi contratado pela RTP para desenvolver uma aplicação informática para analisar a actuação de uma equipa de futebol através de estatísticas rigorosas. Algo fundamental após a detecção de um jogador na imagem televisiva, é determinar a que equipa a que pertence. Isto será feito através da análise da cor da sua camisola. a) Crie uma classe Cor que permita guardar a informação sobre a cor de uma camisola, usando três números inteiros que podem variar entre 0 e 255: vermelho, verde, azul. Um outro atributo importante a reter é a luminosidade da cor que pode ser determinada pela média aritmética dos valores das três cores de base. Escreva dois construtores diferentes para a classe, sendo que um deles instancia a cor com um valor por defeito. class Cor { byte verm; byte verde; byte azul; float lum; // dado que o código vai até 255, chega um byte Cor() { verm=verde=azul= 0; lum= 0.0; Cor(byte vm, byte vd, byte az) { verm= vm; verde= vd; azul= az; lum= ((float)(vm+vd+az))/3; b) Ao desenvolver a aplicação identificou-se que era interessante poder comparar dois objectos que representam as cores de duas camisolas. Escreva o método compareto(cor c) da classe Cor que compara o objecto que invoca o método com o objecto c. O resultado desta comparação deverá ser: 0 significando que as cores das camisolas são iguais. Isto acontece quando todos os parâmetros (vermelho, verde, azul) têm uma diferença menor ou igual a 20 em relação aos mesmos parâmetros da cor da camisola comparada. -1 significando que a cor da camisola comparada é mais clara. Isto acontece se as cores não forem iguais e se a média dos 3 parâmetros for menor ou igual do que a média dos 3 parâmetros da cor da camisola comparada. 1 significando que a cor da camisola comparada é mais escura. Isto acontece se as cores não forem iguais e se a média dos 3 parâmetros for maior do que a média dos 3 parâmetros da cor comparada.
Estruturas de dados (CC114) int compareto(cor c) { // primeiro verifica se cores s~ao iguais if (Math.abs((c.verm+c.verde+c.azul)-(verm+verde+azul))<=20) return 0; // n~ao sendo, verifica luminosidade if (c.lum <= lum) return -1; return 1; c) Escreva um método que ordene um vector de cores com n objectos (deverá usar um dos métodos de ordenação dados). O critério de ordenação deverá ser a luminosidade da cor. Ordene da mais escura (luminosidade == 0) para a mais clara (luminosidade == 255). // metodo simples de ordenacao por selecç~ao de minimo void ordena(cor v[], int n) { int pmin; Cor tmp; for (int i= 0; i<n; i++) { pmin= i; for (int j= i+1; j<n; j++) if (v[j].compareto(v[pmin])<0) pmin= j; tmp= v[i]; v[i]= v[pmin]; v[pmin]= tmp; 3. (Valorização: 35%) Árvores Considere um sistema informático, associado ao Acelerador de Partículas do CERN, o LHC. Este sistema faz uma classificação sistemática das partículas detetadas sob três parâmetros que, para facilidade de representação, identificamos por: 1, 2 e 3. O sistema recebe regularmente indicação de como foram classificadas as partículas, cabendo-lhe a si fazer o armazenamento dessa informação, bem como uma posterior análise dos dados. Por exemplo, se o sistema receber para uma dada partícula a sequência: 2 20 3 35 3 15, esta significa que a partícula em causa foi decomposta primeiro segundo o parâmetro 2, com impacto 20, depois segundo o parâmetro 3, com impacto 35, e depois de novo segundo o parâmetro 3, com impacto 15. Um input típico do sistema é o seguinte: 3 1 10 3 5 1 5 1 5 3 5 3 5 2 5 2 10 1 5 3 5 Primeiro vem o número de particulas detectadas. Seguem-se as classificações de decomposição observadas de cada partícula, uma por linha. Este sistema de classificação é representado por uma árvore ternária que, depois de lida a classificação da 2 a partícula, fica:
Responda às seguintes questões: a) Implemente a(s) estrutura(s) de dados para representar o armazenamento das partículas e o seu impacto. Escreva ainda um método processaparticula(string linha) que processe uma linha do input, passada como argumento do método, com as classificações de decomposição. Sugestão: defina um novo Scanner cujo input é dado pela string. // considere-se a definicao de particula class Particula { int impacto; Particula seg[]; // usamos posiç~oes 1 a 3 Particula(int i) { impacto= i; seg= new Particula[4]; // linha de input com pares de pontos, e.g. 1 10 3 5 1 5 // path[] e imp[] tem o correspondente impacto // quando estiver no par i: // path[0], path[1],..., path[i-1] tem o caminho até um nó p // imp[i-1] tem o valor do impacto a guardar no nó p void processaparticula(string lin) { Scanner in2= new Scanner(lin); int path[]= new int[4]; // guarda tipo de decomposicao int imp[]= new int[4]; int start= 0; int i= 0; while (in2.hasnextint()) { path[i]= in2.nextint(); imp[i]= in2.nextint(); i++; root= inserir(root,start,i,path,imp); // valores das particulas Particula inserir(particula p, int start, int end, int path[], int imp[]) { if (start<end) p.seg[path[start]]= inserir(p.seg[path[start]], start+1, end, path,imp); else { // start==end if (p==null) p= new Particula(imp[start-1]); else
p.impacto += imp[start-1]; return p; b) Escreva um método em que dada um sequência do tipo 13 (String), retorne o impacto total (i.e. a soma dos impactos) das classificações da sub-árvore que tem como raiz o último nó do caminho: raiz, 1 o ramo, 3 o ramo. Por exemplo, a chamada do método com o argumento 13, para a árvore da direita na figura acima, retornaria 20 (= 10 + 5 + 5). int totalimpact(particula p, String path) { if (path.equals("")) return suminorder(p); int next= path.charat(0)- 0 ; return totalimpact(p.seg[next], path.substring(1)); int suminorder(particula p) { if (p==null) return 0; int soma=0; for (int i=0; i<4; i++) soma += suminorder(p.seg[i]); return p.impacto + soma; c) Escreva um método que percorra a árvore em largura e escreva os valores precedidos dos parâmetros 1, 2 ou 3, consoante o ramos onde se encontram. Por exemplo, o output que deverá ser produzido para a árvore da figura, no lado direito, é: 1 15 3 10 1 5 3 5 void visitbreadthfirst(particula p) { LinkedList<Particula> f= new LinkedList<Particula>(); f.addlast(p); while (f.size()>0) { Particula pn= f.removefirst(); for (int i= 1; i<4; i++) { if (pn.seg[i]!=null) { System.out.print(i+" "+(pn.seg[i]).impacto+" "); f.addlast(pn.seg[i]); System.out.println();