AED - Algoritmos e Estruturas de Dados
Licenciatura em Engenharia Electrónica
Exame de 17 de Junho de 2015 - 1a Época - Resolução
Prova escrita, individual e sem consulta. 20 valores.
NÚMERO:
NOME:
PARTE I - Questões de Escolha Múltipla
Preencha as respostas na tabela (usando apenas letras maiúsculas). Se nenhuma opção servir, escreva NENHUMA.
Se pretender alterar a sua resposta, risque e escreva ao lado a sua nova opção. Todas as questões de escolha múltipla
valem 1 valor. As questões de escolha múltipla não respondidas são cotadas com 0 valores, mas por cada resposta
errada são descontados 1/4=0.25 valores.
Questão
Resposta
1
A
2
C
3
B
4
C
5
D
6
B
7
B
8
C
1. Considere o seguinte extracto de programa em C:
#include <stdio.h>
#define N 10
int k,j,n=0;
for(k = 0 ; k < N; k++)
for(j = k + 1; j < N; j++)
n++;
printf("%d\n",n);
Indique qual a saı́da deste programa:
A.
45
B.
55
C.
60
D.
66
R. Há várias formas de chegar a este resultado. Uma forma simples é verificar que os dois ciclos
correspondem a varrer os elementos da sub-matriz triangular superior (ou inferior) de uma matrix
quadrada de N × N (ou seja, excluindo os elementos da diagonal). É simples perceber que o
resultado deve ser então (N 2 − N )/2. Note que N 2 − N são todos os elementos da matriz excepto
a diagonal, a divisão por 2 corresponde à sub-matriz indicada.
Uma forma alternativa é verificar que para k = 0 o ciclo interior é executado N − 1 vezes, para
k = 1, N − 2, etc. No total, o número de vezes que o ciclo interior é executado é dado por
(N − 1) + (N − 2) + (N − 3) + ... + 2 + 1
Isto não é mais que a soma dos elementos de uma progressão aritmética. Recordando que, no caso
deste tipo de progressão, a soma dos elementos é dada por,
M
X
k=1
ak =
a1 + aM
×M
2
E que neste caso M = N − 1, a1 = N − 1, aM = 1, resulta
M
X
k=1
ak =
(N − 1) + 1
N2 − N
× (N − 1) =
2
2
2. Considere o seguinte extracto de programa em C:
struct s {
int n;
struct s *p;
} *b = NULL,*a;
int j;
for(j=0;j<3;j++){
a = malloc(sizeof(struct s));
a->n = j-3;
a->p=b; b=a;
}
while(b!=NULL) {
printf("%d ",b ->n);
b = b -> p;
}
Indique qual a saı́da deste programa:
A.
123
B.
-3 -2 -1
C.
-1 -2 -3
D.
321
R. Os elementos são inseridos na base da lista na sequência -3, -2, -1, ficando p último elemento
na base. A listagem corresponde obviamente a −1, −2, −3.
3. Considere que uma árvore é suportada em C com a estrutura:
typedef struct tree {
char c;
struct tree*l,*r;
} Tree;
é usada para uma representação de uma árvore binária , a qual após inicializada, pode ser representada desta forma:
Admita que esta árvore é listada a partir da raiz usando a seguinte função:
void list(Tree *t){
if(t){
list(t->r);
list(t->l);
printf("%c",t->c);
}
}
Indique qual a saı́da deste programa:
A. ABCDEFG
B. ACBEGFD
C. GFEDCBA
D. DBACFEG
R. É fácil de ver que se trata de uma listagem da árvore da direita para a esquerda em ordem
pós-fixada (ou post-order). Basta olhar para um elemento para saber qual a resposta correcta...
4. Indique qual das afirmações seguintes é falsa:
√
A. N (N
+ 3)5 ∈ O(N 7 / N + 1)
√
C. N 2 N + 1 ∈ O(N 2 lg(N ))
B.
D.
√
N 2 N + 3 ∈ O(N 5/2 )
log(N 3 ) ∈ O(log N 2 )
R. . É suficiente verificar que em A. a função é de ordem O(N 6 ) e a da direita de O(N 6+1/2 ),
pelo que a afirmação é verdadeira. Em B., a função à esquerda é da ordem de O(N 2+1/2 ) =
O(N 5/2 ), pelo que a afirmação é igualmente verdadeira. Em D., basta notar que O(log(N α )) =
O(α log(N )) = O(log(N )), pelo que ambos os membros √
são da ordem de O(log(N )). Em C, descontando o factor multiplicativo N 2 , basta recordar que N cresce mais depressa que lg(N ), pelo
que a afirmação é falsa.
5. Considere uma árvore ordenada e perfeitamente balanceada de N inteiros. Considere um algoritmo
que percorre todos os nós da árvore e, em cada nó da árvore, efectua N operações.
A complexidade deste algoritmo é (deverá indicar a ordem de complexidade correspondente ao
menor dos majorantes):
A.
C.
O(N 2 log2 N )
O(N log2 N )
B.
D.
O(N )
O(N 2 )
R. É fácil verificar que se são percorridos N nós e em cada nó efectuadas N operações elementares,
a complexidade tem que ser O(N 2 ).
6. Indique qual das tabelas seguintes não pode ser um acervo:
A.
20
15
13
12
11
10
9
B.
20
15
15
13
12
16
11
C.
9
3
8
1
2
8
7
D.
9
8
3
7
6
1
2
R. . Basta representar as tabelas em forma de árvore e verificar a condição de acervo.
7. Considere uma árvore binária ordenada totalmente balanceada com 31 nós. No pior caso, o número
de nós que é preciso percorrer para saber se um determinado item se encontra na árvore é
A.
31
B.
5
C.
10
D.
1
R
R. Sendo a árvore balanceada, a altura é dada por h = (log2 (N ) + 1 = 5. O pior caso corresponde
ao nó procurado estar nas folhas terminais, em que será necessário percorrer um número de nós
igual à altura da árvore.
8. Admita que tem uma lista ordenada de inteiros, representados por uma lista simplesmente ligada.
O número médio de operações necessárias para determinar qual o nó em que se encontra um inteiro
que faz parte da lista é:
A.
N
B.
log2 N
C.
N/2
D.
log2 (N/2)
R. Se a lista tem N elementos, e a procura é feita sequencialmente, o melhor caso será 1 e e o pior
N . Em média será N/2. Nota: se fosse omitida a indicação de que o elemento faz parte da lista,
poderia haver muitas procuras de elementos inexistentes, em que o número de operações seria N .
Nestas condições não seria possı́vel estimar o valor médio.
PARTE II - Questões de Desenvolvimento
Responda às questões de desenvolvimento em folhas de exame devidamente identificadas com nome e número.
[3.0V]
9. Considere uma lista simplesmente ligada de reais suportada num tipo L definido por
typedef struct list {
float x;
struct list *next;
} L;
Escreva uma função com protótipo
float average(L *base);
que receber um apontador para a base da lista e que retorna o valor médio de todos os elementos
da lista. Nota: se a lista estiver vazia, o valor retornado deve ser 0.
R.
float average(L *b){
int n=0; float sum = 0;
if(b == NULL) return 0;
while(b != NULL){
sum += b -> x; n++;
}
return sum / n;
}
[3.0V]
10. Considere uma árvore suportada num tipo T definido por
typedef struct tree {
float x;
struct tree *left,*right;
} T;
[2.0V] a) Escreva uma função com protótipo
int
count(T *r);
que recebe como argumento um apontador para a raı́z da árvore r e que retorna o número
total de elementos na árvore.
R.
int count(Tree *t){
if(t == NULL)
return 0;
else
return count(t->l) + count(t->r) + 1;
}
[1.0V] b) Indique a complexidade do algoritmo, admitindo que a árvore tem N nós.
R. Dado que a cada nó da árvore é percorrido apenas uma vez, a complexidade é obviamente O(N ).
[3.0V]
11. O algoritmo quick sort baseia-se na noção de partição e função de partição. Considere uma função
de partição aplicada a uma tabela de inteiros com o protótipo
int partition(int a[],int l, int r)
em que l e r são os ı́ndices que indicam o primeiro e último elemento do subconjunto da tabela
que se pretende particionar.
Admita que aplicamos o algoritmo de partição a uma tabela de 12 inteiros x[] e que, após a
chamada a esta função na forma
i = partition(x,0,11);
a tabela fica organizada como se segue:
28
7
41
8
1
50
82
78
57
75
60
94
[1.0V] a) Indique o valor de i retornado pela função partition.
R. A partição divide a tabela em duas partes, em que a primeira corresponde a todos os valores
menores que um elemento pivot, e a segunda todos os valores superiores. O valor retornado
pela função é o ı́ndice pivot. É fácil verificar que i=5
[2.0V] b) Escreva uma função de ordenação baseada no algoritmo quick sort com protótipo
void quicksort(int x[],int l, int r)
e que utiliza a função partition() definida anteriormente. Nota: não é necessário escrever o
código da função partition().
R.
void quicksort(int x[],int l, int r){
int i;
if(r <= l) return;
i = partition(x,l,r);
quicksort(x,l,i-1);
quicksort(x,i+1,r);
}
[1.0]
12. Considere o seguinte algoritmos recursivo:
int xpto{int *tab, int N){
if(N<=1)
return 0;
else
return xpto(tab,N/2) + xpto(tab+N/2,N/2);
}
Admita para simplificar que N é uma potência de 2. Determine a equação de recorrência e a
complexidade da função xpto().
R.
CN = CN/2 + CN/2 = 2CN/2
com condição terminal C1 = 1. Fazendo por conveniência, N = 2k ,
C2k
=
2C2k /2 = 2C2k−1
C2k−1
=
2C2k−2
ou seja
C2k
=
4C2k−2
C2k
=
8C2k−3
C2k
=
2j C2k−j
Generalizando
Para j = k,
C2k
=
2k C20 = 2k C1 = 2k
ou seja
CN
pelo que a complexidade é O(N ).
= N
[2.0V]
13. Considere o seguinte grafo:
Determine os caminhos mı́nimos de A a todos os nós da árvore, usando o algoritmo de Dijkstra. Não
se pretende o código, apenas a explicitação da sequência de operações e testes. Nota: a resposta
só será valorizada se forem indicados todos os passos do algoritmo de Dijkstra. R.
Passo 1, Nó A=0, resta {B, C, D, E, F }
B = 2, A → B
C = 5, A → C
F = 2, A → F
Passo 2, Nó B=2 (podia rb ser o F), resta {C, D, E, F }:
B = 2, A → B
C = 3, B → C
D = 5, B → D
F = 2, A → F
Passo 3, Nó F=2, resta {C, D, E}:
B = 2, A → B
C = 3, B → C
D = 3, F → D
E = 3, F → E
F = 2, A → F
Passo 4, Nó C=3 (podia tb ser o D ou E), resta {D, E}:
B = 2, A → B
C = 3, B → C
D = 3, F → D
E = 3, F → E
F = 2, A → F
(nenhum caminho actualizado)
Passo 5, Nó D=3 (podia tb ser o E), resta {E}:
B = 2, A → B
C = 3, B → C
D = 3, F → D
E = 3, F → E
F = 2, A → F
(nenhum caminho actualizado)
Passo 6, Nó E=3, resta {}:
B = 2, A → B
C = 3, B → C
D = 3, F → D
E = 3, F → E
F = 2, A → F
(nenhum caminho actualizado)
logo
A → B, Custo = 2
A → B → C, Custo = 3
A → F → D, Custo = 3
A → F → E, Custo = 3,
A → F , Custo = 2
Download

Enunciado e soluções