Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Este um resumo de parte das sees 5.4 (Trees, p.216), 5.6 (Tree traversal, p.230), 5.7 (Recursive binarytree algorithms, p.235) e 12.5 (Binary Search Trees) do livro do Sedgewick.
Definio
Uma rvore binria (= binary tree) formada de ns; cada n tem um certo contedo (por exemplo, um nmero inteiro) e os endereos (das razes) de duas subrvores: uma esquerda e uma direita. Eis um exemplo de n:
typedef struct node *link; struct node { int item; link l, r; } ; // contedo do n // 'l' de "left" e 'r' de "right"
Termos tcnicos importantes: raiz de uma rvore, filho de um n, pai de um n, folha de uma rvore, n interno de uma rvore, nvel de um n. Em geral, quando dizemos "um n x" devemos entender que x o endereo de um n. Nesses termos, o filho esquerdo de um n x x->l e o filho direito x->r. Um n x uma folha se no tem filhos, ou seja, se x->l e x->r valem NULL. Para ilustrar o conceito de rvore, eis uma pequena funo (veja programa 5.17, p.236, do Sedgewick) que calcula o nmero de ns de uma rvore binria.
// Esta funo devolve o nmero de ns // da rvore binria cuja raiz h. int count(link h) { if (h == NULL) return 0; return count(h->l) + count(h->r) + 1; }
Exerccios
1. [Sedg 5.59, p.225] Escreva uma funo recursiva que receba uma rvore binria ab e um nmero x e remova da rvore todas as folhas que tenham item igual a x.
// Devolve o altura de um n h em uma rvore binria. int height(link h) { int u, v; if (h == NULL) return -1; u = height(h->l); v = height(h->r); if (u > v) return u+1; else return v+1; }
A altura de uma rvore a altura de sua raiz. A altura de uma rvore com N ns pode variar de lg(N) at N-1. (Como de hbito, lg uma abreviatura de log2.)
Exerccios
2. Escreva uma funo no-recursiva que calcule o nmero de ns de uma rvore binria. 3. Mostre que toda rvore binria com N ns tem altura maior ou igual ao piso de lg(N). 4. [Profundidade] A profundidade (= depth) de um n x em uma rvore binria com raiz h a "distncia" x e h. Mais precisamente, a profundidade de x o comprimento do (nico) caminho que vai de h at x. Por exemplo, a profundidade de h 0 e a profundidade de h->l 1. Escreva uma funo que determine a profundidade de um n dado em relao raiz da rvore. 5. Suponha que cada n da rvore tem um campo depth do tipo int. Preencha o campo de cada n com a altura do n. 6. [Cdigos de ns] Um caminho que vai da raiz de uma rvore at um n pode ser representado por uma seqncia de 0s e 1s: toda vez que o caminho "desce para a esquerda" temos um 0; toda vez que "desce para a direita" temos um 1. Diremos que essa seqncia de 0s e 1s o cdigo do n. Suponha agora que todo n de nossa rvore tem um campo adicional cod capaz de armazenar uma cadeia de caracteres. Escreva uma funo que preencha o campo cod de cada n com o cdigo do n. 7. [Reconstruo] Suponha dados os cdigos de todas as folhas de uma rvore binria. Escreva uma funo que reconstrua a rvore a partir desses cdigos das folhas.
Essa funo percorre a rvore em ordem raiz-esquerda-direita (= preorder). Se as trs ltimas instrues forem trocadas por
imprime(h->l); printf("%d\n", h->item); imprime(h->r);
a rvore ser percorrida em ordem esquerda-raiz-direita (= inorder). Se as trs ltimas instrues forem trocadas por
imprime(h->l); imprime(h->r); printf("%d\n", h->item);
Exerccios
8. Escreva uma funo que encontre o n de uma rvore binria cujo item tem um dado valor. 9. [Sedg 5.86] Escreva uma funo que calcule o nmero de folhas de uma rvore binria. Faa trs verses: uma que percorra a rvore em inorder, outra que percorra a rvore em preorder e outra que percorra a rvore em postorder.
Esta funo percorre a rvore na ordem raiz-esquerda-direita, ou seja, em preorder. Ela usa uma pilha de ns (todos diferentes de NULL) para gerenciar o andamento do algoritmo. Todo n x na pilha representa o comando "imprima os ns da rvore cuja raiz x". No cdigo abaixo, a pilha implementada em um vetor pilha[0..t], sendo t o ndice do topo da pilha:
// Imprime o item de cada n de uma rvore binria h. // A funo supe que h != NULL. void imprime_red (link h) { link *pilha; int t; pilha = malloc((1+height(h)) * sizeof (link)) pilha[t=0] = h; while (t >= 0) { h = pilha[t--]; printf("%d\n", h->item); if (h->r != NULL) pilha[++t] = h->r; if (h->l != NULL) pilha[++t] = h->l; } free(pilha); }
Exerccios
10. [Inorder no-recursivo. Sedg 5.82, p.235] Escreva uma verso iterativa do imprime que percorra a rvore na ordem esquerda-raiz-direita (= inorder). 11. [Postorder no-recursivo. Sedg 5.83, p.235] Escreva uma verso iterativa do imprime que percorra a rvore na ordem esquerda-direita-raiz (= postorder). (Cuidado!) 12. Escreva uma funo que calcule a soma dos contedos (campos item) dos ns de uma rvore binria. Percorra a rvore em ordem esquerda-raiz-direita (= inorder).
A funo usa uma fila implementada em um vetor fila[i..f-1]: o ndice do primeiro da fila i e o ndice do ltimo f-1. Todos os elementos da fila so diferentes de NULL.
// A funo auxiliar printnode imprime o caracter // c precedido de 3b espaos e seguido de uma mudana // de linha. void printnode(char c, int b) { int i; for (i = 0; i < b; i++) printf(" printf("%c\n", c); }
");
Eis uma amostra do resultado de show(x,0). Troquei os espaos em branco por "-" para facilitar a leitura.
------* ---H ------------* ---------G ------------* ------F ---------* E ------* ---D ------------* ---------C ------------* ------B ------------* ---------A ------------*
Eis o resultado da impresso da mesma rvore em ordem raiz-esquerda-direita. Troquei os espaos em branco por "-" para facilitar a leitura.
E ---D ------B ---------A ------------* ------------* ---------C ------------* ------------* ------* ---H ------F ---------* ---------G ------------* ------------* ------*
Construo de um torneio
O programa 5.19, p.238, de Sedgewick ilustra a construo de uma rvore binria. Diremos que uma rvore binria um torneio se cada n que no seja uma folha contm uma cpia do maior dos items de seus dois filhos.
// A funo max recebe um vetor no-vazio a[p..q] // (portanto p <= q) e constroi um torneio cujas folhas // so a[p],..,a[q]. A funo devolve a raiz do torneio. link max(int a[], int p, int q) { int m, u, v; link x; m = (p + q) / 2; x = malloc(sizeof *x); if (p == q) { x->l = x->r = NULL; x->item = a[m]; return x; } x->l = max(a, p, m); x->r = max(a, m+1, q); u = x->l->item; v = x->r->item; if (u > v) x->item = u; else x->item = v; return x; }
Exerccios
13. Aplique a funo max acima ao vetor 1 2 3 4 5 . 14. [Sedg 5.91, p.241] Escreva uma funo recursiva que remova de um torneio todas as folhas que contenham uma dada chave. (Veja acima o exerccio Sedg 5.59.) 15. [Busca binria] Escreva uma funo que contrua a rvore binria que representa todas as possveis buscas binrias em um vetor crescente a[p..r]. Cada n da rvore dever conter o ndice do vetor envolvido em uma comparao com a chave procurada. 16. Escreva uma funo que construa uma rvore binria aleatria com n ns e chaves aleatrias.
Se a rvore for lida em ordem esquerda-raiz-direita, teremos a expresso em notao infixa. Se for lida em ordem esquerda-direita-raiz, teremos a expresso em notao posfixa. Se for lida em ordem raiz-esquerdadireita-raiz, teremos a expresso em notao prefixa. infixa
(a+(b*c)*(d+e))*f
posfixa abc*de+*+f* prefixa *+a**bc+def O programa 5.20, p.240, de Sedgewick, faz o servio inverso: transforma a expresso prefixa (no-vazia, claro) em uma rvore binria. Se a expresso consiste em um nica letra, a rvore ter um nico n; se a expresso for algo como *ab, a rvore ter uma raiz e duas folhas. Suponha que a expresso prefixa est armazenada em um vetor global de caracteres a[i..] , sendo i uma varivel global.
typedef struct Tnode *link; struct Tnode { char token; link l, r; } ; char *a; int i; // // // // // A funo parse atua sobre a expresso prefixa a[i..]. Os operadores so '+' e '*', cada varivel tem um s caracter, e no h espaos entre os caracteres. A funo transforma a expresso em uma rvore binria e devolve a raiz da rvore.
link parse() { char t; link x; t = a[i++]; x = malloc(sizeof *x); x->token = t; if (t == '+' || t == '*') { x->l = parse(); x->r = parse(); } else x->l = x->r = NULL; return x; }
Exerccios
17. Escreva uma funo que calcule o valor da expresso aritmtica representada por uma rvore sendo dodos os valores das variveis. Suponha que os valores das variveis so dados em um vetor do tipo int indexado por letras. 18. Escreva uma funo que receba uma expresso aritmtica em notao infixa e construa a correspondente rvore. Suponha que a expresso s envolve os operadores '+' e '*' e operandos que consistem em uma s letra. A pgina sobre pilhas pode ser til.
Para representar os ns da rvore vamos usar uma estrutura que envolve um union:
// Type: node // ---------// An expression is represented as tree. The contents of each // node consists of a tagged union that allows the node to // have multiple representations [interpretations?]. // struct node { exptype type; union { int constRep; // a constant string varRep; // name of a variable struct { char op; // '=' or '+' or '-' or '*' ou '/' expression lhs; // left subexpression expression rhs; // right subexpression } subexpRep; } contents; } ;
// Returns the value of the subexpression exp. (The values // of all variables must have been already loaded into the // appropriate table.) // static int EvalSubExp(expression exp) { char op; expression leftexp, rightexp; int leftval, rightval; op = exp->contents.subexpRep.op; leftexp = exp->contents.subexpRep.lhs; rightexp = exp->contents.subexpRep.rhs; if (op = '=') { rightval = EvalExp(rightexp); SetVariableValue(leftexp->contents.varRep, rightval); return rightval; } leftval = EvalExp(leftexp); rightval = EvalExp(rightexp); switch (op) { case '+': return leftval + rightval; case '-': return leftval - rightval; case '*': return leftval * rightval; case '/': return leftval / rightval; } } // Prototypes of auxiliary functions: // Returns the value of variable var. int GetVariableValue(string var) ; // Sets the value of variable var to val. int SetVariableValue(string var, int val) ;
Mais exerccios
19. Escreva uma funo que receba um vetor a[1..n], interprete esse vetor como um heap, e construa a correspondente rvore binria. 20. [Sedg 12.54, p.511] O comprimento interno de uma rvore binria a soma dos comprimentos dos caminhos que levam da raiz a cada uma das folhas. Escreva um programa recursivo que calcule o comprimento interno de uma rvore binria dada. 21. [Sedg 12.63, p.514, ndices no lugar de ponteitos] rvores binrias podem ser implementadas com ndices no lugar de ponteiros, da seguinte maneira: teremos trs vetores "paralelos", item[1..N], l[1..N] e r[1..N]; para cada ndice i, l[i] o ndice do filho esquerdo de i e r[i] o ndice do filho direito. Exerccio: escreva todas as funes desse captulo para a implementao que acabamos de sugerir. [Essa implementao tem suas vantagens porque reduz o tempo consumido pelas sucessivas chamadas de malloc durante a construo da rvore. Mas exige que o nmero total de ns seja conhecidono antes que a rvore comece a ser construda.]
Compresso de arquivos
[Esse material est no capitulo 22 da 2-a edio do livro de Sedgewick.] Suponha dada uma cadeia de caracteres, digamos
bafeabacaadefa
Cada caracter representado por 8 bits: smbolo caracter grfico ASCII a 97 b 98 c 99 d 100 e 101 f 102 bits
01100001 01100010 01100011 01100100 01100101 01100110
Suponha agora que adotemos uma codificao com nmero varivel de bits poucos bits para as letras mais freqentes e muitos bits para letras raras: smbolo bits grfico
a b c d e f 0 101 100 111 1101 1100
Agora podemos representar a cadeia bafeabacaadefa por uma cadeia de bits bastante curta:
1010110011010101010000111110111000
Note que no temos separadores entre as subcadeias de bits que representam os vrios caracteres. Apesar disso, a cadeia de bits pode ser decodificada sem ambigidades. Essa uma propriedade interessante e valiosa de nossa tabela de cdigos. A propriedade decorre do seguinte fato: o cdigo pode ser representado por uma rvore cujas folhas so os caracteres:
. / a / . / \ c b f \ . \ . / \ . / \ e d
Para determinar o cdigo de um caracter x, comece na raiz e caminhe at x; toda vez que descer para a esquerda, acrescente um 0 ao cdigo de x; toda vez que descer para a direita, acrescente um 1. [Veja exerccio sobre cdigos de ns]. PROBLEMA: Dada uma cadeia de caracteres, construir uma tabela de codificao que codifique a cadeia de caracteres usando o menor nmero possvel de bits. Eis um algoritmo que resolve o problema. Suponha que cada caracter x ocorre f(x) vezes na cadeia de caracteres. Ento o seguinte algoritmo produz uma codificao tima: construa uma rvore binria cujas chaves so nmeros inteiros; comece com um n para cada caracter x, sendo f(x) a chave do n; seja x um n que tem chave mnima; seja y um n que tem a segunda menor chave; faa com que x e y sejam os filhos de um novo n z; a chave do novo n ser f(x)+f(y); os ns x e y "saem do jogo" e o n z "entra no jogo"; repita o processo at que todas as subrvores se juntem. A rvore resultante conhecida como rvore de Huffman da cadeia de caracteres original. Exemplo: Suponha que nossa cadeia s contm os caracteres a, b, c, d, e, f. Suponha que o nmero de ocorrncias de cada caracter dado pela tabela: x f(x)
a b c d e f
45 13 12 16 9 5
Aplique o algoritmo. Verifique que a rvore exatamente aquela da figura acima. Exerccio: Escreva uma funo que receba uma cadeia de caracteres e construa uma rvore de Huffman para essa cadeia. Veja tambm o exerccio sobre reconstruo da rvore de cdigos. Esse material sobre codificao e compresso de arquivos pode ser encontrado no captulo 22 da 2-a edio do livro do Sedgewick. Tambm pode ser encontrado no livro Introduction to Algorithms de Cormen, Leiserson, Rivest e Stein (h uma edio do livro em portugus).