Gabarito da Prova I de MAC 314
10 de junho de 1999
1.
(i) Complex COMPLEXmult(Complex z, Complex w) { Complex r = (Complex) malloc(sizeof(*r));
r->Re = z->Re * w->Re - z->Im * w->Im;
r->Im = z->Re * w->Im + z->Im * w ->Re;
return r;
}
(ii) Complex COMPLEXsum(link h) {
Complex r = (Complex) malloc(sizeof(*r));
r->Re = r->Im = 0;
while (h) {
r->Re += h->Item->Re;
r->Im += h->Item->Im;
h = h->next;
}
return r;
}
k 2.
(i) link copia(link h) {
/* Recebe um apontador h para o in´ıcio */
/* de uma lista ligada e devolve um apontador */
/* para o in´ıcio de uma c´opia desta lista */
link p;
if (h == NULL) return NULL;
p = (link) malloc(sizeof(node));
p->item = h->item;
p->next = copia(h->next);
return p;
}
link copia(link h) {
/* Recebe um apontador h para o in´ıcio */
/* de uma lista ligada e devolve um apontador */
/* para o in´ıcio de uma c´opia desta lista */
node f;
link l = &f;
while (h) {
l->next = (link) malloc(sizeof(node));
l->next->item = h->item;
l = l->next;
h = h->next;
}
l->prox = NULL;
return f.prox;
}
(ii) link copia_reversa(link h) {
/* Denote por f o valor original de h */
link r = NULL, p = NULL;
while (h) {
p = (link) malloc(sizeof(node));
p->item = h->item;
p->next = r;
r = p;
/* r aponta para o in´ıcio de uma lista ligada */
/* que ´e uma c´opia reversa da sublista de f */
/* at´e h da lista original */
h = h->next;
} }
k 3.
link misterio(link *aa, link *bb, link c) { 1 link a, b;
2 *aa = *bb = z;
3 if (c->next != z) {
4 a = c; b = c->next->next->next;
5 while (b != z) {
6 c = c->next; b = b->next->next; } 7 b = c->next; c->next = z;
8 *aa = a; *bb = b;
9 }
10 return z;
11 }
Para facilitar o entendimento da fun¸c˜ao, suponha que se t ´e um n´o de uma lista ligada ent˜ao t->item cont´em a posi¸c˜ao de t na lista. Suponha, inicialmente, que a lista contenha mais de trˆes elementos. Neste caso, na linha 5 o ponteiro b estar´a apontando para o quarto elemento da lista. A cada execu¸c˜ao do la¸co das linhas 5 e 6 o ponteiro b avan¸ca de duas posi¸c˜oes enquanto quecavan¸ca uma ´unica posi¸c˜ao. Os valores deb->itemec->itemdurante a execu¸c˜ao do la¸co das linhas 5 e 6 s˜ao dados pela tabela abaixo:
c->item 1 2 3 4 5 6 ...
b->item 4 6 8 10 12 14 ...
E f´´ acil ver que a seq¨uˆencia acima ´e da forma si = 2i+ 2 parai≥1 ondei“representa” o valor dec->itemesi o valor deb->item. Denote porno n´umero de elementos da lista original. No final do la¸co das linhas 5 e 6 teremos quec aponta para ob(n−1)/2c-´esimo elemento da lista.
Portanto, na linha 8 o ponteirobaponta para ob(n+1)/2c-´esimo elemento da lista original. A atribui¸c˜aoc->next = z“quebra” a lista original em duas. Logo, a fun¸c˜aomisteriodivide a lista original em duas listas: a primeira cont´em os primeiros bn/2celementos da lista original e, a segunda cont´em os dn/2e elementos restantes.
Denote por n o n´umero de elementos da lista original,n1 e n2 o n´umero de elementos das listas apontadas por *aae *bbrespectivamente. Ent˜ao
n1 =n2 = 0 se 0 ≤n ≤1 n1 =bn/2c, n2 =dn/2e se n >1
E claro que o n´´ umero de elementos da lista apontada por z´e zero para todo n≥0.
k 4. Assuma, inicialmente, que o path length de uma ´arvore vazia seja 0 (perceba que com esta suposi¸c˜ao n˜ao seremos capazes de diferenciar atrav´es do path length uma ´arvore vazia de uma ´arvore que consta de um ´unico n´o, mas isto ser´a facilmente resolvido como veremos posteriormente). Seja T uma ´arvore bin´aria. Se T´e vazia ent˜ao seu path length ´e 0. Suponha que T n˜ao seja vazia e seja h a raiz de T. Suponha, por hip´otese de indu¸c˜ao, que sabemos calcular o path length das sub´arvores com raizes h->l e h->r. Denote tais valores por p e q respectivamente. Agora, se t ´e um n´o de profundidade d em uma destas sub´arvores ent˜ao sua profundidade emT´ed + 1. Portanto, o path length deT´ep + q + h->item - 1 pois a profundidade de h em T´e 0. Esta discuss˜ao ´e uma justificativa para a fun¸c˜ao pl.
Agora ´e f´acil resolver o problema citado acima. Basta garantirmos que a primeira chamada a pl n˜ao tenha h == NULL.
long path_length( link h ) { if (h == NULL) return -1;
return pl(h);
}
long pl( link h ) {
if (h == NULL) return 0;
return (pl(h->l) + pl(h->r) + h->item - 1);
}
A vers˜ao abaixo ´e baseada na mesma id´eia que a anterior, s´o que tudo numa ´unica fun¸c˜ao.
Aqui precisamos garantir que n˜ao chamaremos path length com parˆametro NULL. Observe que nesta vers˜ao a base da recurs˜ao tem dois casos.
long path_length(link h) { long c = 0;
if (h == NULL) return -1;
if (h->item == 1) return 0;
if (h->l) c = path_length(h->l);
if (h->r) c += path_length(h->r);
return c + h->item - 1;
}
Na vers˜ao abaixo n˜ao utilizaremos a hip´otese que se t aponta para um n´o da ´arvore, ent˜ao t->item´e o n´umero total de n´os que a sub´arvore de raiz t cont´em.
long path_length(link h) { if (h == NULL) return -1;
return pl(h, 0);
}
long pl(link h, int d) { long c = d;
if (h->l) c += pl(h->l, d + 1);
if (h->r) c += pl(h->r, d + 1);
return c;
}
Vamos tentar provar que a fun¸c˜ao path length´e correta.
Seja r a raiz de uma ´arvore bin´aria T e h um n´o de T. Se d ´e a profundidade de h em T, isto ´e, o caminho de r at´e h em T tem comprimento d, ent˜ao a fun¸c˜ao pl(h,d)devolve a soma das profundidades de todos os n´os da sub´arvore deTcuja raiz ´e h.
E claro que a afirma¸c˜´ ao acima garante a corretude de pl. A demonstra¸c˜ao ´e por indu¸c˜ao no n´umero de v´ertices de T. Suponha que h ´e um n´o de de profundidade d de uma ´arvore bin´aria T. Suponha, inicialmente, quehseja o ´unico n´o deT. Ent˜ao a soma das profundidades da sub´arvore de T com raiz h ´e d pois, por hip´otese, d ´e a profundidade de h em T e este ´e precisamente o valor devolvido por pl. Portanto, provamos que a fun¸c˜ao est´a correta no caso
“f´acil”. Suponha, agora, que h tenha dois filhos. Ent˜ao os filhos deh tˆem profundidade d+1.
Por hip´otese de indu¸c˜ao, as chamadas pl(h->l, d+1) e pl(h->r, d+1) devolvem a soma das profundidades das sub´arvores de T com raizes h->l e h->r. Denote estes n´umeros por p e q respectivamente. Ent˜ao a soma das profundidades da sub´arvore de T com raiz h´e p + q + d que ´e precisamente o valor de c na fun¸c˜aopl. O caso em que h possui um ´unico filho ´e similar e n˜ao provaremos aqui.
Considere a vers˜ao abaixo de path length.
long path_length(link h) { if (h == NULL) return -1;
return faux(h, 0);
}
long faux(link h, long d) { long ct;
if ((h->l == NULL) && (h->r == NULL)) return 0;
ct = d;
if (h->l) ct += faux(h->l, d + 1);
if (h->r) ct += faux(h->r, d + 1);
return ct;
}
Esta vers˜ao possui um probleminha (esta vers˜ao ´e obtida da anterior adicionando-se a linha if ((h->l == ...). Considere uma ´arvore bin´aria com dois n´os. Suponha que h seja a raiz de tal ´arvore e queh->lseja o filho n˜ao vazio. Claramente, o path length de tal ´arvore vale 1.
Teremos as seguintes chamadas a fun¸c˜aofaux:
faux(h, 0) // devolve 0 + faux(h->l, 1) = 0
faux(h->l, 1) // como h->l n~ao tem filhos devolve 0
k 5.
(i) void traverse(link h) {
1 STACKinit(); STACKpush(h);
2 while (!STACKempty()) {
3 printf("%c ", (h = STACKpop())->token);
4 if (h->r != NULL) STACKpush(h->r);
5 if (h->l != NULL) STACKpush(h->l); } } Considere a seguinte ´arvore bin´aria
+ / \
* c
/ \
a b
Denote por Pi o estado da pilha e Si a sa´ıda produzida no final da i-´esima itera¸c˜ao do la¸co das linhas 2 a 5. Assuma queP0 eS0 denotam o estado da pilha e a sa´ıda no in´ıcio da primeira itera¸c˜ao. Ent˜ao
P0 =h+i S0 =’ ’ P1 =hc,*i S1 =+ P2 =hc,b,ai S2 =+ * P3 =hc,bi S3 =+ * a P4 =hci S4 =+ * a b P5 =hi S5 =+ * a b c
O algoritmo acima realiza um percurso em pr´e-ordem da ´arvore bin´aria cuja raiz ´e h.
(ii) void traverse(link h) {
1 QUEUEinit(); QUEUEput(h);
2 while (!QUEUEempty()) {
3 printf("%c ", (h = QUEUEget())->token);
4 if (h->l != NULL) QUEUEput(h->l);
5 if (h->r != NULL) QUEUEput(h->r); } }
Denote por Fi o estado da fila e Si a sa´ıda produzida no final da i-´esima itera¸c˜ao do la¸co das linhas 2 a 5. Assuma que F0 e S0 denotam o estado da fila e a sa´ıda no in´ıcio da primeira itera¸c˜ao. Ent˜ao
F0 =h+i S0 =’ ’ F1 =h*,ci S1 =+ F2 =hc,a,bi S2 =+ * F3 =ha,bi S3 =+ * c F4 =hbi S4 =+ * c a F5 =hi S5 =+ * c a b
N˜ao ´e dif´ıcil ver que se F[1], F[2], . . . , F[n] denotam os n´os na fila utilizada pelo algo- ritmo e d[1], d[2], . . . , d[n] denotam as profundidades dos n´osF[1], F[2], . . . , F[n] ent˜ao d[1]≤d[2]≤ · · ·d[n]≤d[1] + 1. A observa¸c˜ao acima garante que primeiro s˜ao visitados todos os n´os de profundidade 0, em seguida, todos os n´os de profundidade 1, depois todos os n´os de profundidade 2 e assim suscessivamente, isto ´e, n˜ao ´e visitado um n´o de profundidade n sem que antes sejam visitados todos os n´os de profundidade n−1.
k