Você está na página 1de 146

Estrutura de Dados

Prof. Evandro Preuss


evandro.preuss@gmail.com

2011

Sumrio
1. INTRODUO ..................................................................................................................................................... 5 1.1 1.2 2. TIPOS ABSTRATOS DE DADOS ......................................................................................................................... 5 OBJETIVOS DA ESTRUTURA DE DADOS............................................................................................................ 5

ESTRUTURAS DE DADOS HOMOGNEAS .................................................................................................. 6 2.1 MATRIZES DE UMA DIMENSO OU VETORES .................................................................................................. 6 2.1.1 Operaes Bsicas com Matrizes do Tipo Vetor ....................................................................................... 6
2.1.1.1 2.1.1.2 2.1.1.3 Atribuio de Uma Matriz do Tipo Vetor ............................................................................................................ 6 Leitura de Dados de Uma Matriz do Tipo Vetor .................................................................................................. 7 Escrita de Dados de Uma Matriz do Tipo Vetor .................................................................................................. 7 O Mtodo da Bolha de Classificao ................................................................................................................... 8

2.1.2

Exemplos de Aplicao de Vetores ............................................................................................................ 8

2.1.2.1

2.2 MATRIZES COM MAIS DE UMA DIMENSO ...................................................................................................... 9 2.2.1 Operaes Bsicas com Matrizes de Duas Dimenses ............................................................................ 10
2.2.1.1 2.2.1.2 2.2.1.3 Atribuio de Uma Matriz de Duas Dimenses ................................................................................................. 10 Leitura de Dados de Uma Matriz de Duas Dimenses ....................................................................................... 11 Escrita de Dados de Uma Matriz de Duas Dimenses ....................................................................................... 11

2.3 2.4 3.

MATRIZES E VETORES EM PASCAL ................................................................................................................ 12 MATRIZES EM C ............................................................................................................................................ 13

ESTRUTURAS E CONJUNTOS ....................................................................................................................... 15 3.1 3.2 REGISTRO .................................................................................................................................................. 15 CONJUNTOS - SET .......................................................................................................................................... 17

4.

ARQUIVOS ......................................................................................................................................................... 20 4.1 ARQUIVO TIPADO .......................................................................................................................................... 20 4.1.1 Declarao de arquivos ........................................................................................................................... 20 4.1.2 Funes de abertura e fechamento de arquivos ...................................................................................... 21 4.1.3 Funes de escrita e gravao ................................................................................................................ 23
4.1.3.1 4.1.3.2 4.1.3.3 4.1.3.4 4.1.3.5 4.1.3.6 4.1.3.7 WRITE ............................................................................................................................................................... 23 READ ................................................................................................................................................................. 24 EOF .................................................................................................................................................................... 24 SEEK.................................................................................................................................................................. 25 FILEPOS ............................................................................................................................................................ 26 FILESIZE ........................................................................................................................................................... 26 WITH ................................................................................................................................................................. 27

4.2 ARQUIVOS TEXTO ......................................................................................................................................... 27 4.2.1 Funes para manipulao de arquivos texto ......................................................................................... 29
4.2.1.1 4.2.1.2 4.2.1.3 4.2.1.4 4.2.1.5 4.2.1.6 APPEND ............................................................................................................................................................ 29 EOF(TEXT) ....................................................................................................................................................... 29 SEEKEOF .......................................................................................................................................................... 29 SEEKEOLN ....................................................................................................................................................... 29 FLUSH ............................................................................................................................................................... 30 SETTEXTBUF ................................................................................................................................................... 30

4.3 ARQUIVOS SEM TIPOS ............................................................................................................................ 30 4.3.1 Funes para manipulao de arquivos sem tipos .................................................................................. 30
4.3.1.1 4.3.1.2 4.3.1.3 BLOKREAD ...................................................................................................................................................... 30 BLOCKWRITE .................................................................................................................................................. 31 TRUNCATE ...................................................................................................................................................... 31

4.4 5. 6.

RESUMO DO USO DE ARQUIVOS TIPADOS ..................................................................................................... 31

ALOCAO DINMICA DE MEMRIA ..................................................................................................... 39 LISTAS LINEARES ........................................................................................................................................... 42 6.1 FUNDAMENTOS ............................................................................................................................................. 42 6.2 FORMAS DE ARMAZENAMENTO..................................................................................................................... 43 6.2.1 Alocao Esttica versus Dinmica ........................................................................................................ 43 6.2.2 Alocao Seqencial ................................................................................................................................ 43 6.2.3 Alocao Encadeada ............................................................................................................................... 43

7.

PILHAS ................................................................................................................................................................ 45 7.1 ORGANIZAO DE DADOS NA PILHA ............................................................................................................. 46 7.1.1 Declarando uma pilha ............................................................................................................................. 46

7.1.2 Inicializando uma pilha ........................................................................................................................... 46 7.1.3 Verificando limites da pilha ..................................................................................................................... 46 7.1.4 Inserindo elementos na pilha ................................................................................................................... 47 7.1.5 Removendo elementos na pilha ................................................................................................................ 47 7.1.6 Verificando o elemento do topo da pilha ................................................................................................. 48 7.2 ESTRUTURAS DO TIPO PILHA ........................................................................................................................ 48 7.3 PILHAS USANDO A BIBLIOTECA STL DO C++ ................................................................................................ 50 7.4 EXERCCIOS................................................................................................................................................... 51 8. FILAS ................................................................................................................................................................... 53 8.1 IMPLEMENTAO SEQENCIAL DE FILAS ...................................................................................................... 54 8.1.1 Declarando uma fila ................................................................................................................................ 54 8.1.2 Inicializando uma fila .............................................................................................................................. 54 8.1.3 Verificando limites da fila ........................................................................................................................ 54 8.1.4 Inserindo elementos na fila ...................................................................................................................... 55 8.1.5 Removendo elementos da fila................................................................................................................... 55 8.1.6 Problemas na Implementao Seqencial de Filas ................................................................................. 56 8.2 SOLUCIONANDO OS PROBLEMAS DA IMPLEMENTAO SEQENCIAL ............................................................ 56 8.3 IMPLEMENTAO CIRCULAR PARA FILAS ..................................................................................................... 57 8.3.1 Declarando uma fila ................................................................................................................................ 57 8.3.2 Inicializando uma fila .............................................................................................................................. 57 8.3.3 Verificando limites da fila ........................................................................................................................ 58 8.3.4 Inserindo elementos na fila ...................................................................................................................... 58 8.3.5 Removendo elementos da fila................................................................................................................... 59 8.4 MANIPULAO DE PILHAS E FILAS COM PROCEDIMENTOS E FUNES ......................................................... 60 8.5 FILAS USANDO A BIBLIOTECA STL DO C++................................................................................................... 66 9. LISTAS ORDENADAS ...................................................................................................................................... 68 9.1 ORGANIZAO DE DADOS NA LISTA ORDENADA .......................................................................................... 68 9.1.1 Declarando uma lista ordenada .............................................................................................................. 69 9.1.2 Inicializando uma Lista Ordenada .......................................................................................................... 69 9.1.3 Inserindo elementos em uma Lista Ordenada .......................................................................................... 70 9.1.4 Removendo elementos de uma Lista Ordenada ....................................................................................... 71 9.1.5 Imprimindo uma Lista Ordenada............................................................................................................. 72 9.1.6 Pesquisando na Lista Ordenada .............................................................................................................. 73 9.1.7 Exemplo completo de uma Lista Ordenada com procedimentos ............................................................. 74 9.2 LISTA ENCADEADA COM ALOCAO DINMICA DE MEMRIA: ...................................................................... 77 9.3 LISTAS ENCADEADAS PARA LISTAGEM EM ORDEM DE DADOS ARMAZENADOS EM ARQUIVOS ........................ 82 9.3.1 Lista encadeada com alocao dinmica de memria para exibir os dados de um Arquivo .................. 82 9.3.2 Usando um arquivo de ndice com lista ordenada para exibir os dados de um Arquivo ........................ 89 9.4 LISTA DUPLAMENTE ENCADEADAS............................................................................................................... 95 10. 10.1 10.2 10.3 11. CLASSIFICAO (ORDENAO) DE DADOS .................................................................................... 104 BUBBLE SORT ............................................................................................................................................. 104 MERGESORT ................................................................................................................................................ 105 QUICKSORT ................................................................................................................................................. 108 RVORES ..................................................................................................................................................... 114

11.1 FORMAS DE REPRESENTAO DE RVORES ................................................................................................. 114 11.1.1 Forma natural (cima-baixo) ................................................................................................................ 115 11.1.2 Forma Hierrquica .............................................................................................................................. 115 11.1.3 Forma linear ........................................................................................................................................ 115 11.1.4 Forma Grfica ..................................................................................................................................... 115 11.1.5 Forma em ndices ................................................................................................................................ 115 11.2 RVORE BINRIA........................................................................................................................................ 117 11.2.1 Implementao de uma rvore binria ................................................................................................ 117 11.2.2 Caminhamento sobre rvore binria ................................................................................................... 119 11.3 APLICAES QUE USAM RVORES E RVORE BINRIAS .............................................................................. 120 11.4 RVORE BINRIA DE BUSCA ........................................................................................................................ 120 11.4.1 rvore binria de busca tima ............................................................................................................ 122 11.4.2 rvores binrias de busca balanceadas .............................................................................................. 122 11.4.3 B-Trees (B-rvores) ............................................................................................................................. 123 11.4.4 rvore Balanceadas ABP e AVL ....................................................................................................... 125

11.4.5 11.4.6 12. 13. 13.1 13.2 14. 14.1 14.2 15. 15.1 15.2 15.3 15.4 15.5 16.

rvores B+........................................................................................................................................... 128 rvores B* ........................................................................................................................................... 129

GRAFOS........................................................................................................................................................ 130 HASHING ..................................................................................................................................................... 135 FUNES DE TRANSFORMAO DE CHAVES ............................................................................................... 135 TRATAMENTO DE COLISES............................................................................................................... 135 TABELAS...................................................................................................................................................... 137 PESQUISA EM TABELAS ............................................................................................................................... 137 INDEXAO................................................................................................................................................. 138 COMPONENTES DO DELPHI PARA USO COMO ESTRUTURAS DE DADOS .............................. 139 PILHAS COM DELPHI ................................................................................................................................... 139 FILAS COM DELPHI ...................................................................................................................................... 139 LISTAS COM DELPHI .................................................................................................................................... 141 RVORES COM DELPHI................................................................................................................................ 141 EXERCCIOS................................................................................................................................................. 144 BIBLIOGRAFIA .......................................................................................................................................... 146

1. INTRODUO
1.1 Tipos Abstratos de Dados
Um tipo abstrato de dados formado por um conjunto de valores e por uma srie de operaes que podem ser aplicadas sobre ele. Operaes e valores, em conjunto, constituem um modelo matemtico que pode ser empregado para modelar e solucionar problemas do mundo real. Para que possamos realmente aplicar um modelo matemtico na resoluo de problemas por computador, preciso antes transform-lo num tipo de dados concreto, ou simplesmente tipo de dados. A transformao de um tipo de dados abstrato em um tipo de dados concreto chamada implementao. durante o processo de implementao que a estrutura de armazenamento dos valores especificada, e que os algoritmos que desempenharo o papel das operaes so projetados.

1.2 Objetivos da Estrutura de Dados


O estudo das estruturas de dados envolve dois objetivos complementares: Terico: identificar e desenvolver modelos matemticos, determinando que classes de problemas podem ser resolvidos com o uso deles. Prtico: criar representaes concretas de objetos e desenvolver rotinas capazes de atuar sobre estas representaes, de acordo com o modelo considerado. O primeiro objetivo considera um tipo abstrato de dados como um recurso a ser empregado durante a resoluo de problemas em geral; e o segundo considera a implementao deste tipo abstrato de dados como um problema em si, que pode ser resolvido atravs do uso de outros tipos de dados j disponveis, possivelmente predefinidos na linguagem escolhida para a implementao.

2. ESTRUTURAS DE DADOS HOMOGNEAS


As estruturas de dados homogneas permitem agrupar diversas informaes dentro de uma mesma varivel. Este agrupamento ocorrer obedecendo sempre ao mesmo tipo de dado, e por esta razo que estas estruturas so chamadas homogneas. A utilizao deste tipo de estrutura de dados recebe diversos nomes, como: variveis indexadas, variveis compostas, variveis subscritas, arranjos, vetores, matrizes, tabelas em memria ou arrays. Os nomes mais usados e que utilizaremos para estruturas homogneas so: matrizes (genrico) e vetores (matriz de uma linha e vrias colunas).

2.1 Matrizes de Uma Dimenso ou Vetores


Este tipo de estrutura em particular tambm denominado por profissionais da rea como matrizes unidimensionais. Sua utilizao mais comum est vinculada criao de tabelas. Caracteriza-se por ser definida uma nica varivel vinculada dimensionada com um determinado tamanho. A dimenso de uma matriz constituda por constantes inteiras e positivas. Os nomes dados s matrizes seguem as mesmas regras de nomes utilizados para indicar as variveis simples. A sintaxe do comando de definio de vetores a seguinte: Var <nome_da_varivel> : MATRIZ [ <coluna_inicial> .. <coluna_final> ] DE <tipo_de_dado> Ex.: VAR M : MATRIZ [1 .. 10] DE INTEIRO

2.1.1 Operaes Bsicas com Matrizes do Tipo Vetor Do mesmo modo que acontece com variveis simples, tambm possvel operar com variveis indexadas (matrizes). Contudo no possvel operar diretamente com o conjunto completo, mas com cada um de seus componentes isoladamente. O acesso individual a cada componente de um vetor realizado pela especificao de sua posio na mesma por meio do seu ndice. No exemplo anterior foi definida uma varivel M capaz de armazenar 10 nmero inteiros. Para acessar um elemento deste vetor deve-se fornecer o nome do mesmo e o ndice do componente desejado do vetor (um nmero de 1 a 10, neste caso). Por exemplo, M[1] indica o primeiro elemento do vetor, M[2] indica o segundo elemento do vetor e M[10] indica o ltimo elemento do vetor. Portanto, no possvel operar diretamente sobre vetores como um todo, mas apenas sobre seus componentes, um por vez. Por exemplo, para somar dois vetores necessrio somar cada um de seus componentes dois a dois. Da mesma forma as operaes de atribuio, leitura e escrita de vetores devem ser feitas elemento a elemento. 2.1.1.1 Atribuio de Uma Matriz do Tipo Vetor No captulo sobre as instrues primitivas, o comando de atribuio foi definido como: <nome_da_varivel> := <expresso> No caso de vetores (variveis indexadas), alm do nome da varivel deve-se necessariamente fornecer tambm o ndice do componente do vetor onde ser armazenado o resultado da avaliao da expresso. 6

Ex.:

M[1] := 15 M[2] := 150 M[5] := 10 M[10] := 35

2.1.1.2 Leitura de Dados de Uma Matriz do Tipo Vetor A leitura de um vetor feita passo a passo, um de seus componentes por vez, usando a mesma sintaxe da instruo primitiva da entrada de dados, onde alm do nome da varivel, deve ser explicitada a posio do componente lido: LEIA <nome_da_varivel> [ <ndice> ] Uma observao importante a ser feita a utilizao da construo Para a fim de efetuar a operao de leitura repetidas vezes, em cada uma delas lendo um determinado componente do vetor. De fato esta construo muito comum quando se opera com vetores, devido necessidade de se realizar uma mesma operao com os diversos componentes dos mesmos. Na verdade, so raras as situaes que se deseja operar isoladamente com um nico componente do vetor. O algoritmo a seguir exemplifica a operao de leitura de um vetor:
Algoritmo exemplo_leitura_de_vetor Var numeros : matriz[1..10] de inteiro i : inteiro Incio Para i de 1 at 10 faa Incio Leia numeros[i] Fim Fim.

2.1.1.3 Escrita de Dados de Uma Matriz do Tipo Vetor A escrita de um vetor obedece mesma sintaxe da instruo primitiva de sada de dados e tambm vale lembrar que, alm do nome do vetor, deve-se tambm especificar por meio do ndice o componente a ser escrito: ESCREVA <nome_da_varivel> [ <ndice> ] O algoritmo a seguir exemplifica a operao de leitura e escrita de um vetor, utilizando a construo Para:
Algoritmo exemplo_escrita_de_vetor Var numeros : matriz[1..10] de inteiro i : inteiro Incio Para i de 1 at 10 faa Incio Leia numeros[i] Fim Para i de 1 at 10 faa Incio Escreva numeros[i] Fim Fim.

Um exemplo mais interessante mostrado a seguir, onde um vetor de dez nmeros lido e guardado no vetor numeros. Paralelamente, a soma destes nmeros calculada e mantida na varivel soma, que posteriormente escrita. 7

Algoritmo exemplo_escrita_de_vetor_com_soma Var numeros : matriz[1..10] de inteiro i : inteiro soma : inteiro Incio soma := 0 Para i de 1 at 10 faa Incio Leia numeros[i] soma := soma + numeros[i] Fim Para i de 1 at 10 faa Incio Escreva numeros[i] Fim Escrever Soma = , soma Fim.

2.1.2 Exemplos de Aplicao de Vetores O espectro de aplicao de vetores em algoritmos muito extenso, mas normalmente os vetores so usados em duas tarefas muito importantes no processamento de dados: pesquisa e classificao. A pesquisa consiste na verificao da existncia de um valor dentro de um vetor. Trocando em midos, pesquisar um vetor consiste em procurar dentre seus componentes um determinado valor. A classificao de um vetor consiste em arranjar seus componentes numa determinada ordem, segundo um critrio especfico. Por exemplo, este critrio pode ser a ordem alfabtica de um vetor de dados caracter, ou ento a ordem crescente ou decrescente para um vetor de dados numricos. H vrios mtodos de classificao, mas o mais conhecido o mtodo da bolha de classificao (Bubble Sort).

2.1.2.1 O Mtodo da Bolha de Classificao Este mtodo no o mais eficiente, mas um dos mais populares devido sua simplicidade. A filosofia bsica deste mtodo consiste em varrer o vetor, comparando os elementos vizinhos entre si. Caso estejam fora de ordem, os mesmos trocam de posio entre si. Procede-se assim at o final do vetor. Na primeira varredura verifica-se que o ltimo elemento do vetor j est no seu devido lugar (no caso de ordenao crescente, ele o maior de todos). A segunda varredura anloga primeira e vai at o penltimo elemento. Este processo repetido at que seja feito um nmero de varreduras igual ao nmero de elementos a serem ordenados menos um. Ao final do processo o vetor est classificado segundo o critrio escolhido. O exemplo a seguir ilustra o algoritmo bubble sort para ordenar 50 nmero inteiros em ordem crescente:
Algoritmo Bubble_Sort Var numeros : matriz [1..50] de inteiros aux, i, j: inteiro Incio Para i de 1 at 50 faa Incio Ler numeros[i] Fim j := 50 Enquanto j > 1 faa Incio Para i de 1 at j-1 faa Incio Se numeros[i] > numeros[i+1] Ento Incio aux := numeros[i];

numeros[i] := numeros[j]; numeros[j] := aux; Fim Fim j:=j-1; Fim Escreva vetor ordenado: Para i de 1 at 50 faa Incio Escrever numeros[i] Fim Fim.

Uma outra variao deste algoritmo, com as mesmas caractersticas utiliza duas construes Para, fazendo a comparao iniciando do primeiro elemento at o penltimo, comparando com o imediatamente seguinte at o ltimo elemento:
Algoritmo Variao_do_Bubble_Sort Var numeros : matriz [1..50] de inteiros aux, i, j: inteiro Incio Para i de 1 at 50 faa Incio Ler numeros[i] Fim Para i de 1 at 49 faa Incio Para j de i + 1 at 50 faa Incio Se numeros[i] > numeros[j] Ento Incio aux := numeros[i]; numeros[i] := numeros[j]; numeros[j] := aux; Fim Fim Fim Escreva vetor ordenado: Para i de 1 at 50 faa Incio Escrever numeros[i] Fim Fim.

Podemos observar tambm que para ordenar o vetor em ordem decrescente basta inverter o sinal de comparao no teste da condio lgica Se numeros[i] > numeros[j], para: Se numeros[i] < numeros[j]

2.2 Matrizes com Mais de Uma Dimenso


Este tipo de estrutura tambm tem sua principal utilizao vinculada criao de tabelas. Caracteriza-se por ser definida uma nica varivel vinculada dimensionada com um determinado tamanho. A dimenso de uma matriz constituda por constantes inteiras e positivas. Os nomes dados s matrizes seguem as mesmas regras de nomes utilizados para indicar as variveis simples. A sintaxe do comando de definio de matrizes de duas dimenses a seguinte: Var <nome_da_varivel> : MATRIZ [<linha_inicial> .. <linha_final> , <coluna_inicial> .. <coluna_final> ] DE <tipo_de_dado> Ex.: VAR M : MATRIZ [1 .. 5 , 1 .. 10] DE INTEIRO 9

Tambm possvel definir matrizes com vrias dimenses, por exemplo: Ex.: VAR N : MATRIZ O : MATRIZ P : MATRIZ Q : MATRIZ R : MATRIZ S : MATRIZ [1 .. 4] DE INTEIRO [1 .. 50 , 1 .. 4] DE INTEIRO [1 .. 5, 1 .. 50 , 1 .. 4] DE INTEIRO [1 .. 3 , 1 .. 5, 1 .. 50 , 1 .. 4] DE INTEIRO [1 .. 2 , 1 .. 3 , 1 .. 5, 1 .. 50 , 1 .. 4] DE INTEIRO [1 .. 2 , 1 .. 2 , 1 .. 3 , 1 .. 5, 1 .. 50 , 1 .. 4] DE INTEIRO

A utilidade de matrizes desta forma muito grande. No exemplo acima, cada matriz pode ser utilizada para armazenar uma quantidade maior de informaes: a matriz N pode ser utilizada para armazenar 4 notas de um aluno a matriz O pode ser utilizada para armazenar 4 notas de 50 alunos. a matriz P pode ser utilizada para armazenar 4 notas de 50 alunos de 5 disciplinas. a matriz Q pode ser utilizada para armazenar 4 notas de 50 alunos de 5 disciplinas, de 3 turmas. a matriz R pode ser utilizada para armazenar 4 notas de 50 alunos de 5 disciplinas, de 3 turmas, de 2 colgios. a matriz S pode ser utilizada para armazenar 4 notas de 50 alunos de 5 disciplinas, de 3 turmas, de 2 colgios, de 2 cidades 2.2.1 Operaes Bsicas com Matrizes de Duas Dimenses Do mesmo modo que acontece com os vetores, no possvel operar diretamente com o conjunto completo, mas com cada um de seus componentes isoladamente. O acesso individual a cada componente de uma matriz realizado pela especificao de sua posio na mesma por meio do seu ndice. No exemplo anterior foi definida uma varivel M capaz de armazenar 10 nmero inteiros em cada uma das 5 linhas. Para acessar um elemento desta matriz deve-se fornecer o nome da mesma e o ndice da linha e da coluna do componente desejado da matriz (um nmero de 1 a 5 para a linha e um nmero de 1 a 10 para a coluna, neste caso). Por exemplo, M[1,1] indica o primeiro elemento da primeira linha da matriz, M[1,2] indica o segundo elemento da primeira linha da matriz, M[1,10] indica o ltimo elemento da primeira linha da matriz e M[5,10] indica o ltimo elemento da ltima linha da matriz Da mesma forma como vetores, no possvel operar diretamente sobre matrizes como um todo, mas apenas sobre seus componentes, um por vez. Por exemplo, para somar duas matrizes necessrio somar cada um de seus componentes dois a dois. Da mesma forma as operaes de atribuio, leitura e escrita de matrizes devem ser feitas elemento a elemento. 2.2.1.1 Atribuio de Uma Matriz de Duas Dimenses Na atribuio de matrizes, da mesma forma que nos vetores, alm do nome da varivel deve-se necessariamente fornecer tambm o ndice do componente da matriz onde ser armazenado o resultado da avaliao da expresso. O ndice referente ao elemento composto por tantas informaes quanto o nmero de dimenses da matriz. No caso de ter duas dimenses, o primeiro nmero se refere linha e o segundo nmero se refere coluna da matriz em que se encontra a informao Ex.:
M[1,1] := 15 M[1,10] := 10

10

M[3,5] := 20 M[5,10] := 35

2.2.1.2 Leitura de Dados de Uma Matriz de Duas Dimenses A leitura de uma matriz feita passo a passo, um de seus componentes por vez, usando a mesma sintaxe da instruo primitiva da entrada de dados, onde alm do nome da varivel, deve ser explicitada a posio do componente lido: LEIA <nome_da_varivel> [ <ndice_1>, ... , < ndice_n> ] Uma observao importante a ser feita a utilizao de construes Para aninhadas ou encadeada a fim de efetuar a operao de leitura repetidas vezes, em cada uma delas lendo um determinado componente da matriz. Esta construo muito comum quando se opera com matrizes, devido necessidade de se realizar uma mesma operao com os diversos componentes das mesmas. O algoritmo a seguir exemplifica a operao de leitura de uma matriz:
Algoritmo exemplo_leitura_de_matriz Var numeros : matriz[1..5, 1..10] de inteiro i, j : inteiro Incio Para i de 1 at 5 faa Incio Para j de 1 at 10 faa Incio Leia numeros[i,j] Fim Fim Fim.

2.2.1.3 Escrita de Dados de Uma Matriz de Duas Dimenses A escrita de uma matriz obedece mesma sintaxe da instruo primitiva de sada de dados e tambm vale lembrar que, da mesma forma que com vetores, alm do nome da matriz, deve-se tambm especificar por meio do ndice o componente a ser escrito: ESCREVA <nome_da_varivel> [ <ndice_1> , ... , <ndice_n> ] O algoritmo a seguir exemplifica a operao de leitura e escrita de uma matriz, utilizando as construes Para aninhadas ou encadeadas:
Algoritmo exemplo_escrita_de_matriz Var numeros : matriz[1..5,1..10] de inteiro i, j : inteiro Incio Para i de 1 at 5 faa Incio Para i de 1 at 10 faa Incio Leia numeros[i,j] Fim Fim Para i de 1 at 5 faa Incio Para j de 1 at 10 faa Incio Escreva numeros[i,j] Fim Fim Fim.

11

Um exemplo mais interessante mostrado a seguir, onde uma matriz de 5 linhas por 10 colunas lida e guardada na matriz numeros. A seguir efetuada e escrita a soma dos elementos da 2 linha e tambm a soma dos elementos da 3 coluna
Algoritmo exemplo_escrita_de_matriz_com_soma Var numeros : matriz[1..5,1..10] de inteiro i, j : inteiro somal2, somac3 : inteiro Incio Para i de 1 at 5 faa Incio Para i de 1 at 10 faa Incio Leia numeros[i,j] Fim Fim Para i de 1 at 5 faa Incio Para i de 1 at 10 faa Incio Escreva numeros[i,j] Fim Fim somal2 := 0 somac3 := 0 Para j de 1 at 10 faa Incio somal2 := somal2 + numeros[2,j] Fim Para i de 1 at 5 faa Incio somac3 := somac3 + numeros[i,3] Fim Escrever Soma Linha 2 = , somal2 Escrever Soma Coluna 3 = , somac3 Fim.

2.3 Matrizes e Vetores em Pascal


Uma matriz unidimensional ou vetor em Pascal definido com o uso da palavra reservada array, seguida de seus limites inferior e superior entre colchetes, da palavra reservada of e do tipo de componente do vetor. <nomeVetor> : ARRAY [<lim_inf> .. <lim_sup>] of <TipoComponente> Ex: Alunos: Array[1..40] of integer; Uma matriz multidimensional pode ser comparada a uma tabela de duas ou mais dimenses, sendo necessrios tantos ndices quantas forem as dimenses. Uma matriz multidimensional em Pascal definida tambm com o uso da palavra reservada array, seguida de seus limites inferior e superior para cada dimenso, separada por vrgulas, entre colchetes, da palavra reservada of e do tipo de componente do vetor. <nomeVetor> : ARRAY [<lim_inf> .. <lim_sup>,<lim_inf> .. <lim_sup>, <lim_inf> .. <lim_sup>] of <TipoComponente>

Ex: 12

Notas: Array[1..40,1..3] of integer; Uma matriz pode ser declarada e inicializada declarando-se no bloco de constantes: const num1: array [1..5] of integer = (2,4,6,8,10); num2: array [1..2,1..5] of integer = ((10,20,30,40,50),(15,25,35,45,55));

Observe que no se pode usar uma varivel na definio da matriz para dimensionar o seu tamanho, pois o compilador precisa alocar a memria necessria no momento da compilao.
Exemplo: Program Arrays; Uses CRT; Var Vetor: array[1..10] of integer; Matriz: array[1..5,1..10] of integer; i, j:integer; begin clrscr; Writeln('Lendo Valores do Vetor: '); for i:= 1 to 10 do begin write('Elemento ',i,': '); readln(Vetor[i]); end; Writeln('Lendo Valores da Matriz: '); for i:= 1 to 5 do begin for j:= 1 to 10 do begin write('Elemento [', i, ',' , j, ']: '); readln(Matriz[i,j]); end; end; Writeln('Escrevendo Valores do Vetor: '); for i:= 1 to 10 do begin writeln('Elemento ',i,': ',Vetor[i]); end; Writeln('Escrevendo Valores da Matriz: '); for i:= 1 to 5 do begin for j:= 1 to 10 do begin writeln('Elemento [', i, ',' , j, ']: ',Matriz[i,j]); end; end; readln; end.

2.4 Matrizes em C
Uma matriz em C definida escrevendo-se o tipo da matriz, seguida de um nome, e um par de colchetes contendo uma expresso constante que define o tamanho da matriz. <tipo> <nomematriz> [<tamanho>] 13

Ex: int alunos[40]; Uma matriz multidimensional definida escrevendo-se o tipo da matriz, seguida de um nome, e tantos pares de colchetes quantas forem a dimenses, contendo uma expresso constante que define o tamanho da matriz em cada dimenso. Ex: int Notas[40][3]; Em C o primeiro elemento sempre ser o elemento de ndice 0. Uma matriz pode ser inicializada explicitamente quando declarada: Ex.:
#include <conio.h> #include <stdio.h> int numeros[3] = {2,4,6}; char vogais[5] = {'a','b','c','d','e'}; int num[2][5]={{2,4,6,8,10},{1,3,5,7,9}}; int m[3][5]; void main() { int i,j; for (i=0;i<3;i++) { for (j=0;j<5;j++) { printf("Informe um nmero: "); scanf(%d,&m[i][j]); } } for (i=0;i<2;i++) { printf("\n"); for (j=0;j<5;j++) { printf(" %d",num[i][j]); } } }

14

3. ESTRUTURAS E CONJUNTOS
Os tipos estruturados diferem do tipo simples pelo fato de poderem agregar mais de um componente (informao). Cada componente de um tipo estruturado pode ser uma varivel de um tipo simples ou estruturado. A linguagem Pascal agrupa os tipos estruturados de acordo com os tipos de componentes que podem agregar: os Tipos Estruturados Homogneos, que s podem agregar componentes do mesmo tipo e os Heterogneos, que podem agregar tipos diferentes. Como Tipos Homognetos, o Pascal nos oferece os Vetores (ARRAY), os Arquivos (FILE) e o Conjunto (SET). Como Tipo Heterogneo o Pascal nos oferece o Registro (RECORD).

3.1 REGISTRO
A declarao RECORD permite-nos definir um registro: <identificador> = RECORD <campo1> : tipo; <campo2> : tipo; ... <campon> : tipo; END;

REGISTRO um grupo de informaes relativas a uma mesma entidade. Estas informaes podem ter caractersticas diferentes, como em um R.G., onde temos diversos campos com nome, nome do pai, nome da me, nmero do registro, etc. O registro tem uma particularidade em relao a uma matriz, enquanto em uma matriz podemos ter vrios elementos de um nico tipo, no registro podemos ter elementos com tipos diferentes. A definio de um registro no Turbo Pascal s pode ser feita na rea de tipos (TYPE) e assim sendo, quando definimos um registro, na verdade, estamos criando um tipo novo. Para podermos utilizar este tipo, temos que declar-lo na rea de variveis (VAR), j a manipulao dos campos de um arquivo deve ser feita atravs da referncia do nome do registro e o nome do campo unidos por um ponto (.).

Exemplos : TYPE registro = RECORD nome : STRING[30]; ende : STRING[25]; fone : STRING[8]; idade : BYTE; END; tela = RECORD atributo : BYTE; caracter : CHAR;

15

END;

Podemos ainda ter uma estrutura de registro seletivo :


Exemplo: Program ExemploRegistroSeletivo; uses crt; type Classe = (Num, Str);

uniao = record Nome: string[30]; case campo: Classe of Num: (N: real); Str: (S: string); end; var un: uniao; op: char; begin clrscr; write('Nome: '); readln(un.nome); writeln('O que voce quer armazenar: [N] Numero - [S] - String?'); op:=readkey; if upcase(op)='N' then begin write('Digite um numero: '); readln(un.N); writeln(un.nome); writeln(un.N:0:6); end else begin write('Digite uma string: '); readln(un.S); writeln(un.nome); writeln(un.S); end; readkey; end.

Neste tipo de registro, temos a possibilidade de variar a estrutura. dependendo da condio encontrada no decorrer do programa para um campo-sinal previamente declarado como tipo escalar, esta estrutura de seleo chamada de unio discriminada, cabendo desta forma ao programador manter vlida. Exemplo de referncia de um campo do registro :
PROGRAM teste_reg; TYPE registro = RECORD nome :STRING[30]; ende :STRING[25]; fone :STRING[8]; idade :BYTE; END; VAR reg : registro; BEGIN reg.nome := Roberto;

16

reg.ende := Rua Annima, 0; reg.fone := 999-9999; reg.idade:= 30; writeln(Nome: , reg.nome); writeln(Endereo: , reg.ende); writeln(Fone: , reg.fone); writeln(Idade: , reg.idade); write(Nome: ); readln(reg.nome); write(Endereo: readln(reg.ende); write(Fone: readln(reg.fone); write(Idade: readln(reg.idade); writeln(Nome: , reg.nome); writeln(Endereo: , reg.ende); writeln(Fone: , reg.fone); writeln(Idade: , reg.idade); END.

Pode-se tambm armazenar os registros em memria, atravs do uso de vetores ou matrizes de estrutras.
Exemplo: Program ExemploRegistroSeletivo; uses crt; type cadastro = record codigo: integer; nome: string[30]; rg: string[15]; end; var cad: Array [1..10] of cadastro; i: integer; begin clrscr; for i:= 1 to 10 do begin write('Codigo: '); readln(cad[i].codigo); write('Nome: '); readln(cad[i].nome); write('RG: '); readln(cad[i].rg); end; clrscr; for i:= 1 to 10 do begin writeln(cad[i].codigo, ' - ', cad[i].nome, ' - ', cad[i].rg); end; readkey; end.

3.2 Conjuntos - Set


SET OF permite a definio de tipos conjuntos, que podem ser referenciados e manipulados como qualquer operao de conjunto disponivel em matemtica : <identificador> = SET OF <tipo>; 17

TYPE carac = SET OF CHAR; digitos = SET OF 0..9; vogal = SET OF ('A','E','I','O','U','a','e','i','o','u');

a manipulao dos conjuntos depende da declarao deste tipo na rea de variveis:

VAR car : carac; dig : digitos; vog : vogal;

a construo de um conjunto feita a partir de um construtor '[' ']' e o contedo :

[1, 3, 7, 11, 13] {conjunto dos numeros inteiros 1,3,7,11,13} ['A' .. 'Z', 'a' .. 'z'] {conjunto das letras de 'A' a 'Z' minsculas e maisculas } [] {conjunto vazio}

As operaes com conjuntos so similares s operaes de conjuntos em matemtica. Atribuio de valores para conjuntos: :=
car := ['s','S','n','N'];

Permite a unio entre conjuntos: +


dig := [1,2,3,4,5] + [2,4,6,8,10]; {resulta [1,2,3,4,5,6,8,10]}

Diferena entre conjuntos: vog := ['a','u'] - ['a','e','i','o']; {resulta ['u']}

Interseco entre conjuntos: *


car := ['s','S','n','N'] * ['s','S']; {resulta nos elementos comuns entre ambos ['s','S']}

E ainda relao entre conjuntos: =


if conj1 = conj2 {verdadeiro se todos elementos forem idnticos}

18

<>
if conj1 <> conj2 {verdadeiro se pelo menos um elemento pertencer a ambos os conjuntos}

<=
if conj1 <= conj2 {verdadeiro se todos elementos de conj1 estiverem contidos em conj2}

>=
if conj1 >= conj2 {verdadeiro se todos elementos de conj2 estiverem contidos em conj1}

IN
if conj1 IN conj2 {verdadeiro se conj1 for um elemento de conj2}

19

4. ARQUIVOS
Para comearmos a manipular arquivos, necessitamos lembrar alguns conceitos bsicos a respeito dos mesmos. Quando pensamos em arquivos, a primeira coisa que nos vem memria a idia de um arquivo fsico igual aos arquivos encontrados nos escritrios. De forma geral um arquivo serve para armazenar informaes atravs de fichas. Em informtica, estes conceitos so perfeitamente vlidos, pois para o armazenamento de informaes em um arquivo, temos que dar uma forma ao arquivo e dar forma s informaes que estaro contidas nele.

4.1 Arquivo tipado


4.1.1 Declarao de arquivos A primeira preocupao com relao ao contedo de um arquivo. Em um arquivo fsico, este contedo feito normalmente atravs de fichas que tm informaes de uma mesma forma. Para ns, esta ficha um registro e como j vimos anteriormente, o registro uma estrutura que deve ser definido, na rea de tipos, porm s a definio de um registro no implica na formao de um arquivo. Para form-lo devemos ter um conjunto destas fichas, no nosso caso, devemos declarar uma varivel do tipo arquivo e esta varivel pode ser declarada de duas formas bsicas : FILE Esta declarao define uma varivel como sendo arquivo. Sua sintaxe : identificador : FILE OF tipo; ou identificador : FILE; Vejamos um exemplo de definio de um tipo arquivo:
TYPE registro = RECORD nome : STRING[30]; cep : LONGINT; rg : STRING[8]; cic : STRING[11]; END; VAR arquivo_pessoal : FILE OF registro; arquivo_numerico : FILE OF BYTE; arquivo_sem_tipo : FILE;

Porm, somente a declarao de uma varivel do tipo arquivo no quer dizer que j podemos manipular o arquivo, assim como no arquivo fsico, aquele de ao, aqui teremos tarefas semelhantes, como por exemplo abrir uma gaveta, retirar ou colocar uma ficha, fechar uma gaveta, identificar o arquivo atravs de uma etiqueta qualquer, organizar as informaes, etc. Veremos ento os comandos para fazermos tais tarefas: O primeiro comando que veremos o que nos permite associar a varivel do tipo arquivo ao nome externo deste arquivo, ou seja, o nome local que este deve estar. 20

ASSIGN Este procedimento permite que associemos o nome externo de um arquivo a uma varivel do tipo arquivo. O nome externo aquele utilizado pelo sistema operacional, portanto deve ser vlido para o mesmo. So possveis todas as formas de referncia usadas no PATH, e quando a unidade ou subdiretrios forem omitidos, estes assumiro o default. Aps o uso do ASSIGN, este continuar valendo at que seja dado um novo ASSIGN. O tamanho mximo do nome do arquivo de 79 bytes. Este procedimento nunca deve ser usado em um arquivo j aberto, pois caso o nome do arquivo tenha tamanho zero, o arquivo ser associado ao dispositivo padro de saida. Sua sintaxe: ASSIGN(VAR <arq>, <nomearq> : STRING); arq, deve ser uma varivel do tipo arquivo. por exemplo:
Exemplo: PROGRAM teste_assign; VAR arq1, arq2 : FILE OF BYTE; BEGIN ASSIGN(arq2,teste.dat); ASSIGN(arq1,''); {o nome externo do arquivo omitido} END. {determina que o dispositivo de saida } {ser o standart }

4.1.2 Funes de abertura e fechamento de arquivos Com a definio de uma varivel do tipo arquivo e a sua associao a um nome de arquivo externo, j temos um bom caminho andado, porm ainda nos faltam alguns comandos bsicos para, efetivamente, podermos manipular os arquivos. Voltando analogia com um arquivo de ao, veremos que a primeira coisa que faremos para dar incio a manipulao deste arquivo abri-lo. Pois bem, no nosso arquivo, tambm deveremos ter esta atividade, ou seja, abrir o arquivo e para isto temos dois comandos bsicos:

RESET Este procedimento permite-nos abrir um arquivo j existente. No caso do uso deste, para a tentativa de abertura de um arquivo no existente, ocorrer um erro de execuo. Para que o procedimento tenha sucesso necessrio que antes de execut-lo, tenhamos utilizado o procedimento ASSIGN. Sua sintaxe : RESET(VAR <arquivo> [:FILE; <tamanho> : WORD]);
Exemplo: PROGRAM teste_reset; Uses CRT; VAR arquivo : FILE OF BYTE; nomearq : STRING[67]; BEGIN WRITE('Entre com o nome do arquivo que quer abrir ') ; READLN(nomearq); ASSIGN(arquivo,nomearq); RESET(arquivo); END.

21

Este comando apenas permite-nos abrir um arquivo j existente. para que possamos abrir um arquivo novo, temos um outro comando:

REWRITE Este comando permite criar e abrir um novo arquivo. Caso o arquivo j exista, ter seu contedo eliminado e ser gerado um novo arquivo. Antes de executarmos este procedimento, devemos usar o ASSIGN, e caso a varivel do tipo arquivo no seja tipada, o tamanho de cada registro ser de 128 bytes, isto se no for especificado o tamanho do registro. Sua sintaxe: REWRITE(VAR <arquivo> [: FILE ; <tamanho> : WORD]); Vejamos um exemplo, usando o comando REWRITE :
PROGRAM teste_rewrite; Uses Crt; VAR arquivo : FILE OF BYTE; nomearq: STRING[67]; BEGIN WRITE('Entre com o nome do arquivo que quer abrir '); READLN(nomearq); ASSIGN(arquivo,nomearq); REWRITE(arquivo); END.

Em ambos os exemplos anteriores, vemos uma deficincia. Enquanto um comando apenas nos permite a abertura de arquivos j existentes, o outro apenas abre arquivos novos ou destri um possvel contedo anterior. Para resolver esse problema podemos usar as diretivas de compilao para checagem de erros de entrada/sada {$I }. Esta diretiva retorna um cdigo de erro em uma funo do Turbo Pascal chamada IORESULT. Para no abortar um programa quando ocorre um erro de I/O, usa-se a diretiva {$I}. Para voltar ao padro, usa-se a diretiva {$I+}. Por exemplo:
PROGRAM teste_rewrite_ou_close; Uses Crt; VAR arquivo : FILE OF BYTE; nomearq: STRING[67]; BEGIN WRITE('Entre com o nome do arquivo que quer abrir '); READLN(nomearq); ASSIGN(arquivo,nomearq); {$I-} RESET(arquivo); {$I+} if IORESULT <> 0 then REWRITE(arquivo); END.

A segunda tarefa que devemos nos preocupar o fato do arquivo permanecer aberto, ou abrirmos o mesmo arquivo mais de uma vez. Para termos um uso adequado de um arquivo, devemos abri-lo e depois de utilizado fech-lo.

22

RESET Este procedimento permite gue se feche um arquivo anteriormente aberto, s sendo permitido o fechamento de um arquivo por vez. Sua sintaxe: CLOSE (VAR <arq>); Exemplo :
PROGRAM teste_close; VAR arquivo : FILE OF BYTE; nomearq: STRING[67]; BEGIN WRITE('Entre com o nome do arquivo que quer abrir'); READLN(nomearq); ASSIGN(arquivo,nomearq); REWRITE(arquivo); CLOSE(arquivo); END.

4.1.3 Funes de escrita e gravao Porm para manipularmos um arquivo no basta apenas abr-lo e fech-lo, temos, na maioria das vezes, que ler uma informao contida nele, outras vezes registrar informaes novas, ou ainda, fazer manuteno em informaes j existentes. Vejamos ento, como so os comandos que nos permitem tais tarefas:

4.1.3.1 WRITE Este procedimento alm de ser usado para exibir mensagens e variveis no dispositivo padro de sada (video), pode ser usado para gravar informaes em outros dispositivos, como um arquivo. Sua sintaxe: WRITE(<arq>, <reg1>[,<reg2>,..., <regn>] ;

Exemplo: PROGRAM teste_write_arq; CONST nomearq = 'TESTE.DAT'; max = 10; TYPE registro = RECORD nome : STRING[30]; ender: STRING[25]; END; VAR arquivo : FILE OF registro; reg : registro; ind : BYTE; BEGIN ASSIGN(arquivo,nomearq); REWRITE(arquivo); FOR ind := 1 TO MAX DO BEGIN

23

WRITE('Digite o ',ind,'o Nome '); READLN(reg.nome); WRITE('Digite o ',ind,'o Endereco '); READLN(reg.ender); WRITE(arquivo,reg); END; CLOSE(arquivo); END.

4.1.3.2 READ Como j vimos anteriormente, este procedimento permite que atribuamos a uma varivel, um valor obtido por um dispositivo associado. Este dispositivo pode ser tambm um arquivo, e se no caso da leitura em arquivo, for detectado o fim do mesmo, ser gerado um erro. Sua sintaxe: READ(<arq>, <reg>); Vejamos um exemplo simples de uma leitura em arquivos:
PROGRAM teste_read_arq; Uses Crt; CONST nomearq = TESTE.DAT; max = 10; TYPE registro = RECORD nome : STRING[30]; ender : STRING[25]; END; VAR arquivo: FILE OF registro; reg: registro; ind: BYTE; BEGIN ASSIGN(arquivo,nomearq); RESET(arquivo); FOR ind := 1 TO MAX DO BEGIN READ(arquivo,reg); WRITELN ( 'Este o ', ind, '. Nome ', reg.nome) ; WRITELN('Este ' o ',ind,'. Endereco ',reg.ender); END; CLOSE(arquivo); END.

No exemplo anterior, no temos problema algum ao executar a leitura no arquivo, pois este havia sido gravado anteriormente por ns mesmos e com uma quantidade de registros predefinidos. Porm, na maioria das vezes, no temos idia da quantidade de registros contidos em um arquivo e para esta situao,devemos saber quando chegamos ao final de um arquivo. O Turbo nos fornece tal funo : 4.1.3.3 EOF Esta funo nos retorna o valor TRUE quando for encontrada a marca de fim de arquivo. Sua sintaxe : EOF(VAR <arq>) : BOOLEAN; Utilizando o exemplo anterior, com uma pequena variao : 24

PROGRAM teste_eof; Uses crt; CONST nomearq = 'TESTE.DAT'; TYPE registro = RECORD nome : STRING[30]; ender: STRING[25]; END; VAR arquivo: FILE OF registro; reg: registro; ind: BYTE; BEGIN ASSIGN(arquivo,nomearq); RESET(arquivo); ind := 1; WHILE NOT EOF(arquivo) DO BEGIN READ(arquivo,reg); WRITELN ( 'Este o ,ind,'. Nome ',reg.nome) ; WRITELN('Este o ',ind,'. Endereco ', reg.ender); ind := ind + 1; END; CLOSE(arquivo); END.

O lao repetido at que seja encontrada a marca de fim de arquivo. Porm, para a manuteno de alguns registros de um determinado arquivo, temos que saber qual a posio deste registro no arquivo e tambm qual a posio do ltimo registro. Quando estamos manipulando um arquivo, existe a todo momento um ponteiro que fica deslocando-se de acordo com as leituras e gravaes, ou seja, quando abrimos um arquivo,este ponteiro indica a posio zero, ou que o primeiro registro fisico do arquivo. A cada leitura ou gravao este ponteiro avana uma posio, mas existem algumas funes e procedimentos que permitem a manipulao deste ponteiro. 4.1.3.4 SEEK Este procedimento permite que movamos o ponteiro do arquivo para uma posio preestabelecida, podendo ser usado em arquivos de qualquer tipo exceto os de tipo TEXTO. S pode ser usado em arquivos previamente abertos. Sua sintaxe : SEEK(VAR <arq>; <posicao> : LONGINT);
Exemplo: PROGRAM teste_seek; CONST max = 10; TYPE registro = RECORD nome : STRING[30]; ender: STRING[25]; END;

25

VAR arquivo : FILE OF registro; reg : registro; ind : BYTE; BEGIN ASSIGN(arquivo,TESTE.DAT); REWRITE(arquivo); FOR ind := 1 TO MAX DO BEGIN WRITE('Digite o ',ind,'o Nome '); READLN(reg.nome); WRITE('Digite o ',ind,'o Endereco '); READLN(reg.ender); WRITE(arquivo,reg); END; SEEK(arquivo,4); READ(arquivo,reg); WRITELN ('Este o 5 Nome ',reg.nome) ; WRITELN ('Este o 5 Endereco ', reg.ender); CLOSE(arquivo); END.

4.1.3.5 FILEPOS Esta funo nos retoma a posio atual do ponteiro do arquivo. Assim que abrimos um arquivo, com RESET ou REWRITE, a posio do ponteiro zero, ou seja, o primeiro registro, no pode ser usado em arquivos do tipo TEXTO. Sua sintaxe : FILEPOS(VAR <arq>) : LONGINT;

4.1.3.6 FILESIZE Esta funo retorna o tamanho de um arquivo em nmero de registros. Caso o arquivo esteja vazio, a funo retornar 0. No pode ser usada, em arquivos do tipo TEXTO. Sua sintaxe: FILESIZE(VAR <arq>) : LONGINT;

Vejamos agora, um exemplo englobando estes ltimos comandos:


PROGRAM teste_ponteiro; Uses Crt; VAR nomearq: STRING[67]; arquivo: FILE OF BYTE; tamanho: LONGINT; BEGIN WRITE('Entre com o nome do arquivo '); READLN(nomearq); ASSIGN(arquivo,nomearq); RESET(arquivo); TAMANHO := FILESIZE(arquivo); WRITELN('O tamanho do arquivo ',nomearq, em bytes ',tamanho); WRITELN('Posicionando no meio do arquivo... '); SEEK(arquivo,tamanho DIV 2); WRITELN ('A posicao atual ', FILEPOS (arquivo) ) ; CLOSE(arquivo); READLN; END.

26

4.1.3.7 WITH Este comando permite que a referncia a uma varivel do tipo registro seja simplificada. Podemos encadear at sete registros em um comando WITH. A referncia aos campos dos registros pode ser efetuada com o uso deste comando sem a utilizao do ponto, ligando o nome do registro ao nome do campo. Sua sintaxe: WITH <registro1>, [<registron>...] DO BLOCO;
Exemplo PROGRAM teste_with; CONST max = 10; TYPE registro = RECORD nome : STRING[30]; ender: STRING[25]; END; VAR arquivo : FILE OF registro; reg : registro; ind : BYTE; BEGIN ASSIGN(arquivo,'TESTE.DAT'); REWRITE(arquivo); WITH reg DO BEGIN FOR ind := 1 TO MAX DO BEGIN WRITE('Digite o ',ind,' Nome '); READLN(nome); WRITE('Digite o ',ind,' Endereco '); READLN(ender); WRITE(arquivo,reg); END; SEEK(arquivo,0); WHILE NOT EOF(arquivo) DO BEGIN READ(arquivo,reg); WRITELN ('Este o ',filepos(arquivo),' Nome ',nome) ; WRITELN ('Este o ',filepos(arquivo),' Endereco ', ender); END; END; CLOSE(arquivo); END.

4.2 Arquivos Texto


Uma outra forma de se trabalhar com um arquivo, definindo-o como sendo do tipo Texto. Esta forma de definio permite-nos ter registros de tamanhos diferentes, e cada registro identificado pela sequncia CR LF, Carriage Retum, e Line Feed, correspondentes aos caracteres ASCII $0D e $0A. O identificador TEXT define uma varivel como sendo arquivo do tipo texto. Exemplo:
PROGRAM teste_text;

27

USES CRT; VAR arq : TEXT; ch : CHAR; BEGIN WRITELN('Escolha onde deseja que seja impressa a frase WRITELN(' Impressora, Disco, ou Video, (I, D,V) ' ) ; REPEAT ch := UPCASE(READKEY); UNTIL ch IN ['I','D','V']; IF ch = 'I' THEN ASSIGN(arq,'PRN') ELSE IF ch = 'D' THEN ASSIGN(arq,'TESTE.DAT') ELSE ASSIGN(arq,'CON'); {$I-} REWRITE(ARQ); {$I+} IF IORESULT <> O THEN WRITELN('Arquivo nao aberto ') ELSE BEGIN WRITELN(arq,'Teste de TEXT '); CLOSE(arq); END; WRITE('Tecle algo para continuar '); READKEY; END.

Este outro exemplo copia o arquivo autoexec.bat para um novo arquivo removendo todas as linhas marcadas com comentrio (rem).

Program ExemploArquivoTexto; Uses CRT, Strings; var arq1, arq2: text; buffer: string; aux: string[3]; i: integer; begin assign(arq1,'c:\autoexec.bat'); assign(arq2,'c:\autoexec.new'); {$I-} reset(arq1); {$I+} if IORESULT <> 0 then begin writeln('Nao foi possivel abrir o arquivo C:\AUTOEXEC,BAT'); end; rewrite(arq2); while not eof(arq1) do begin readln(arq1,buffer); aux:=copy(buffer,1,3); for i:= 1 to 3 do begin aux[i]:=upcase(aux[i]); end; if aux<>'REM' then begin writeln(arq2,buffer);

28

end; end; writeln(arq2,'rem - Alterado pela exemplo de Arquivos Texto usando Pascal!'); flush(arq2); end.

4.2.1 Funes para manipulao de arquivos texto 4.2.1.1 APPEND Procedimento que permite a abertura de um arquivo do tipo TEXT, permitindo-se que se faa incluso ao seu final. Se por acaso existir um caractere ^Z (ASCII 26) no arquivo, ser assumido este ponto como fim do mesmo. O arquivo dever ser previamente associado a um nome externo, e aps o uso deste procedimento somente ser permitida a incluso de dados ao seu final. Sua sintaxe: APPEND(var <arq> : TEXT); Este procedimento no permite que se faa a abertura de um arquivo que ainda no existe. O seu uso correto se faz apenas quando o arquivo j existir. Se em seu disco de trabalho existe um arquivo chamado AUTOEXEC.BAT, faa o seguinte teste :

PROGRAM teste_append; VAR arq : TEXT; {define arq do tipo arquivo texto} BEGIN ASSIGN(arq,'\autoexec.bat'); {associa o nome externo a APPEND(arq); WRITELN(arq,'ECHO INCLUIDO PELO TURBO'); CLOSE(arq); END.

arq}

4.2.1.2 EOF(TEXT) Esta funo retorna verdadeiro se for encontrado o fim de um arquivo do tipo texto. A diferena entre EOF para Texto e EOF para arquivos tipados que na primeira, a referncia ao arquivo opcional. Sua sintaxe: EOF[(VAR <arq> : TEXT)] : BOOLEAN;

4.2.1.3 SEEKEOF Esta funo retorna verdadeiro se encontrado status de final de arquivo. bastante parecida com a funo EOF, exceto que esta salta todos os brancos e tabs quando da leitura. Somente para arquivos do tipo texto. Sua sintaxe: SEEKEOF[(VAR <arq>:TEXT)] : BOOLEAN;

4.2.1.4 SEEKEOLN Esta funo retorna verdadeira se encontrada marca de fim de linha, salta todos os caracteres em branco e tabulaes encontradas durante a leitura. Sua sintaxe: 29

SEEKEOLN[(VAR <arq> : TEXT)] : BOOLEAN; 4.2.1.5 FLUSH Este procedimento permite que seja descarregada a rea de Buffer de um arquivo do tipo Texto. Em sua utilizao, pode ser usada a funo IORESULT, que retornar zero caso a operao for bem sucedida. Sua sintaxe : FLUSH(VAR <arq> : TEXT);
Exemplo: PROGRAM teste_flush; var arq : TEXT; BEGIN ASSIGN(arq,'\autoexec.bat'); APPEND(arq); WRITELN(arq,'ECHO INCLUIDO PELO TURBO'); FLUSH(arq); END.

No exemplo anterior, caso no tivssemos nos utilizando da rotina FLUSH, a gravao no teria sido efetivada, pois a informao estaria apenas bufferizada. 4.2.1.6 SETTEXTBUF Este procedimento permite-nos associar um arquivo com tipo TEXTO a uma rea de buffer na rea de dados do programa. Utilize sempre que possivel mltiplos de 128 para o tamanho do Buffer. Normalmente, esta rea j est disposta pelo sistema operacional e corresponde a 128 Bytes. Este comando deve ser usado antes que o arquivo seja aberto, podendo ser usado tanto para leitura como para gravao. Este procedimento acelera os procedimentos de leitura e de gravao. Sua sintaxe : SETTEXTBUF(VAR <arq>:TEXT; VAR <buffer> [;<tamanho> : WORD]);

4.3 ARQUIVOS SEM TIPOS


s vezes, temos a necessidade de manipular arquivos sem saber qual seu tipo de contedo. Poderiamos defini-lo do tipo BYTE, ou ainda, do tipo CHAR, porm se o arquivo tiver um tamanho relativamente grande, sua manipulao ser meio lenta e neste caso, temos algumas opes. 4.3.1 Funes para manipulao de arquivos sem tipos 4.3.1.1 BLOKREAD Procedimento que permite a leitura de um ou mais registros (blocos de 128 bytes), num mximo de 65535 bytes (64 K) de um arquivo sem tipo em uma varivel que pode ser de qualquer tipo. Sua sintaxe: BLOCKREAD(VAR <arq>: FILE; VAR <buffer>; <contador> : WORD; [VAR <resultado> : WORD];

30

4.3.1.2 BLOCKWRITE Procedimento que permite a gravao de um ou mais registros (bloco de 128 bytes) de um arquivo sem tipo em uma varivel que pode ser de qualquer tipo. Sua sintaxe: BLOCKWRITE(VAR <arq>: FILE; VAR <buffer>; <contador> : WORD; [VAR <resultado> : WORD]; Exemplo que utiliza estes comandos :

PROGRAM teste_biock; VAR arqent,arqsai nomeent,nomesai lidos, gravados buf : : : : FILE; STRING[79]; WORD; ARRAY[1..4096] OF CHAR;

BEGIN WRITE('Entre com o nome do arquivo de Origem '); READLN(nomeent); ASSIGN(arqent,nomeent); RESET(arqent,l); WRITE ('Entre com o nome do arquivo de Destino ') ; READLN(nomesai); ASSIGN(arqsai,nomesai); REWRITE(arqsai,l); WRITELN('Copiando ', FileSize(arqent),' bytes...'); REPEAT BLOCKREAD(arqent,buf,sizeof(buf),lidos); BLOCKWRITE(arqsai,buf,lidos,gravados); UNTIL (lidos = 0) OR (gravados <> lidos); Close(arqent); Close(arqsai); END.

de fundamental importncia, que ao abrir o arquivo coloque-se alm da varivel tipo File, tambm o tamanho 1.

4.3.1.3 TRUNCATE Este procedimento faz com que o arquivo seja truncado em seu tamanho, na posio corrente do ponteiro do arquivo. Sua sintaxe ; TRUNCATE(VAR <arq>);

4.4 Resumo do Uso de Arquivos Tipados


O primeiro passo para se trabalhar com arquivos definir quais os campos que queremos que constem nos registros e defini-los na seo TYPE do programa. 31

Exemplificando vamos criar um arquivo de clientes que contenha o cdigo, o nome e a cidade. Na seo TYPE do programa vamos incluir nosso novo tipo (clientes):

clientes=record codigo:integer; nome:string[40]; cidade:string[30]; end;

Clientes o conjunto de informaes que contm o cdigo, o nome e a cidade de cada cliente. Depois necessrio definir uma varivel para o tipo criado, pois somente a criao de tipos no define nada, pois eles no tem ligao direta com o programa. Na seo VAR do programa vamos definir as variveis para o tipo criado. Normalmente se usa uma abreviao do tipo para denominar a varivel, mas pode-se utilizar qualquer nome.

VAR cli:clientes; arq: file of clientes;

Toda a vez que no programa quisermos fazer referncia aos registros de CLIENTES, vamos utilizar a varivel CLI e toda a vez que quisermos fazer referncia ao arquivo vamos utilizar a varivel ARQ. Antes de se utilizar os comandos para criar, abrir ou fechar o arquivo necessrio associ-lo com um arquivo que vai ser gerado no Winchester ou no disquete com o comando ASSIGN, como por exemplo:
ASSIGN(ARQ,CLIENTES.DAT);

Observe que se no especificarmos a unidade de disco na qual ser gerado o arquivo CLIENTES.DAT ele ser gerado na unidade que est o programa principal, mas pode-se tambm especificar uma unidade especfica, como por exemplo:
ASSIGN(ARQ,B:CLIENTES.DAT);

Para criar arquivos usado o comando REWRITE(variavel do arquivo). No nosso programa seria utilizado REWRITE(ARQ). Para abrir os arquivos utilizado o comando RESET(ARQ) e para fechar o arquivo o comando CLOSE(ARQ). Em aplicativos torna-se interessante abrir o arquivo uma nica vez no incio do programa principal e caso no exista ento cria-se automaticamente e fech-lo no final do programa principal antes do END. evitando o constante abre-e-fecha arquivo. Para isso usa-se uma diretiva de compilao {$I-} que impede que o programa trave em caso de erro ao tentar abrir um arquivo que no existe e devolve o cdigo do erro na varivel IORESULT para ser analisada. Se o arquivo j existe, o cdigo de erro 0 (zero = sem erro) caso contrrio devese criar o arquivo. Em seguida voltamos a fazer com que o programa trave em caso de erro com a diretiva de compilao {$I+}.

procedure abre_arquivo; begin

32

assign(arq,'clientes.dat'); {$I-} reset(arq); {$I+} if IORESULT <> 0 then rewrite(arq); end;

Se o programa utilizar a gerao automtica de cdigo, no procedimento incluso devemos: 1. Posicionar o ponteiro no incio do registro: SEEK(ARQ,0). 2. Fazer um loop com o comando WHILE para que enquanto no chegue no final do arquivo, leia todos os registros para armazenar o ltimo cdigo. 3. Criar um codigo auxiliar que armazene o ltimo cdigo vlido ( > 0 ). 4. Testar se o ltimo cdigo maior ou menor que 0 e calcular o novo cdigo para o cliente. 5. Estabelecer uma condio de sada do lao da incluso (Nome vazio, no nosso caso ).

procedure inclusao var cod:integer; begin cod:=0; cli.codigo:=0; seek(arq,0); { posiciona o ponteiro no 1.0 registro } while(not eof(arq)) do { enquanto nao encontra o fim de arquivo } begin read(arq,cli); { le os registros } if cli.codigo>0 then cod:=cli.codigo; end; repeat { Repete o procedimento ... } if cli.codigo < 0 then begin cli.codigo:=cod+1; { incrementa o codigo para os novos registros } end else begin cli.codigo:=cli.codigo+1; end; write('Nome: '); readln(cli.nome); { le o nome do cliente que esta sendo cadastrado } if (cli.nome <> '') and (cli.nome <> ' ') then begin write('Cidade: '); readln(cli.cidade); write(arq,cli); {grava os registros} end; until (cli.nome='') or (cli.nome=' '); {...ate' que o nome esteja vazio} end;

Se o programa no utilizar a gerao automtica de cdigo, no procedimento incluso devemos: 1. Posicionar o ponteiro no final do arquivo: SEEK(ARQ, FILESIZE(ARQ)). 2. Estabelecer uma condio de sada do lao da incluso (Nome vazio, no nosso caso ).

33

procedure inclusao var cod:integer; begin cod:=0; cli.codigo:=0; seek(arq,FILESIZE(arq)); { posiciona o ponteiro no final do arquivo } repeat { Repete o procedimento ... } write(Cdigo: ); readln(cli.codigo) write('Nome: '); readln(cli.nome); if (cli.nome <> '') and (cli.nome <> ' ') then begin write('Cidade: '); readln(cli.cidade); write(arq,cli); {grava os registros} end; until (cli.nome='') or (cli.nome=' '); end;

Para Listar todo o registro, devemos posicionar o ponteiro no incio do arquivo, fazer um loop com o comando WHILE, para que enquanto no chegue no final do arquivo, leia os registros e mostre na tela os que no forem cdigo -1 (cdigos marcados para excluso).

procedure lista_tudo; var p:char; begin seek(arq,0); { posiciona no 1.o registro do arquivo } while (not eof(arq)) do { enquanto nao chega no final do arquivo } begin read(arq,cli); if cli.codigo > 0 then {se codigo for maior que 0 escreve os campos} begin writeln('Codigo: ',cli.codigo); writeln('Nome: ',cli.nome); writeln('Cidade: ',cli.cidade); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end; end; end;

Para Listar um intervalo, no caso de estar utilizando um programa com gerao automtica de cdigo, devemos: 1. 2. 3. ler duas variveis que correspondem ao intervalo a ser listado, posicionar o ponteiro no incio do arquivo, procurar atravs de um loop com o comando WHILE os registros do intervalo,

4. mostrar na tela os registros cujo cdigo no esteja marcado para excluso (cdigo negativo).

procedure lista_intervalo; var p:char; pos, x, cod1, cod2:integer; begin write('Listar desde o codigo: ');

34

readln(cod1); write(' ate o codigo: '); readln(cod2); seek(arq,0); while (not eof(arq)) do { procura os registros ou o final do arquivo } begin read(arq,cli); if (cli.codigo >= cod1) and (cli.codigo <= cod2) then begin writeln('Codigo: ',cli.codigo); writeln('Nome: ',cli.nome); writeln('Cidade: ',cli.cidade); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; {espera que seja press. uma tecla para listar outro } end; end; end;

Para listar um registro, alterar um registro ou excluir um registro, o mtodo para localiz-lo sempre o mesmo e pode ser definido por uma funo que recebe o cdigo como parmetro e retorna TRUE se encontrou o cdigo ou retorna FALSE se no o encontrou:

1. 2. 3.

posiciona-se o ponteiro no incio do arquivo. procurar atravs de um loop com o comando WHILE o registro desejado, armazenando a sua posio. posicionar no prprio registro para que, se for alterado, o ponteiro j esteja na posio correta.

function achoucliente(cod: integer): boolean; var pos: integer; begin achoucliente:=FALSE; seek(arq,0); while not eof(arq) do begin pos:=filepos(arq); read(arq,cli); if(cli.codigo=cod) then begin seek(arq,pos); achoucliente:=TRUE; break; end; end; end;

Com essa funo fica simples fazer a alterao ou excluso: 1. 2. 3. 4. l-se o cdigo a alterar chama-se a funo encarregada de encontrar o cdigo l-se os novos dados e grava-se com a funo read. Observando que a funo de localizao se encarrega de deixar o ponteiro posicionado no registro que se quer alterar se for uma excluso, no precisa ler os novos valores e sim atribu-los como nulo e com cdigo negativo 35

procedure alterar; var codaux, x: integer; begin write('Codigo a alterar: '); readln(codaux); if (achoucliente(codigoaux)) then begin write('Novo Nome: '); readln(cli.nome); write('Nova Cidade: '); readln(cli.cidade); write(arq,cli); {grava os registros na posio correta} end else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end; end;

Para listar um nico registro: 1. 2. 3. l-se o cdigo a listar chama-se a funo encarregada de encontrar o cdigo imprime-se os dados, j que a funo se encarregou de deixar na memria os dados do registro escolhido

procedure listar; var codaux, x: integer; begin write('Codigo a alterar: '); readln(codaux); if (achoucliente(codigoaux)) then begin writeln(Nome: ,cli.nome); writeln('Cidade: ',cli.cidade); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end; end;

Para fazer a incluso de registros sem gerao automtica de cdigo devemos: 1. 2. 3. l-se o cdigo a incluir chama-se a funo encarregada de localizar o cdigo somente continua a incluso aps certificar-se que a funo no localizou o cdigo no arquivo, garantindo que o cdigo no est includo no arquivo.

36

procedure inclusao; var cod, codaux:integer; begin repeat write(Codigo a Incluir: ); readln(codaux); until (not achoucliente(codigoaux)); cli.codigo:=codaux; seek(arq,FILESIZE(arq)); { posiciona o ponteiro no final do arquivo } write(arq,cli); {grava os registros} end;

Para compactar o arquivo, devemos: 1. posicionar o ponteiro no incio do arquivo. 2. contar todos os registros do arquivo para definir o loop de i. 3. testar do primeiro registro at o penltimo com o (primeiro depois do i at o ltimo registro). 4. se encontrar um com cdigo -1 l os registros posteriores e grava-os um registro para trs.

procedure compacta; { elimina os registros que tiverem codigo -1 } var exc, cont, i, j, ultcod, pontaux:integer; op:char; begin seek(arq,0); cont:=-1; while (not eof(arq)) do begin read(arq,cli); cont:=cont+1; if cli.codigo > 0 then ultcod:=cli.codigo; {armazena o ultimo codigo p/ apagar o restante quando chegar nele } end; for i:=0 to cont-1 do { testa ate o penultimo registro } begin seek(arq,i); read(arq,cli); { le o registro que esta sendo testado } if cli.codigo=ultcod then begin i:= cont-1; truncate(arq); {se for o ultimo codigo apaga o restante } end else begin if cli.codigo < 0 then { se o codigo e' -1 } begin pontaux:=1; { variavel que conta os registros c/cod. -1 consecutivos a partir de i } for j:= i+1 to cont do { conta desde o 1.o registro apos o que e' -1 ate o ultimo do arquivo } begin seek(arq,j); read(arq,cli); if cli.codigo<0 then begin {se o registro da frente tambem esta marcado para exclusao } pontaux:=pontaux+1; { incrementa a variavel pontaux e procura um novo registro } end else begin

37

seek(arq,j-pontaux); anterior } write(arq,cli); anterior }

{ posiciona no registro { e grava sobre o

if cli.codigo=ultcod then begin { se for o ultimo registro } truncate(arq); { apaga tudo dessa posicao em diante } j:=cont; {fora a saida do laco contador j} i:=cont-1; {fora a saida do laco contador i} end; end; end; end; end; end; end;

Exemplo de Programa Principal

begin abre_arquivo; repeat clrscr; writeln(' MENU PRINCIPAL writeln(' 1 - CADASTRAR writeln(' 2 - LISTAR CADASTRO writeln(' 3 LISTAR INTERVALO writeln(' 4 - ALTERAR CADASTRO writeln(' 5 - EXCLUIR writeln(' 6 - COMPACTAR ARQUIVO writeln(' 9 - FIM writeln(' Escolha uma opcao: readln(o); if o=1 then inclusao; if o=2 then lista_tudo; if o=3 then lista_intervalo; if o=4 then alterar; if o=5 then excluir; if o=6 then compacta; until o=9; fecha_arquivo; end.

'); '); '); '); '); '); '); '); ');

38

5. ALOCAO DINMICA DE MEMRIA


Quando um programa em C ou Pascal compilado, a memria do computador dividida em quatro zonas que contm o cdigo do programa, todos os dados globais, a pilha, e o heap. O heap a rea de memria livre (algumas vezes chamada de armazm livre) que manipulada com as funes de alocao dinmica malloc e free (C) e Getmem e Freemem (Pascal). Quando malloc (C) ou Getmem (Pascal) chamada, ela aloca um bloco contguo de armazenagem para o objeto especificado e ento retorna um ponteiro para o incio do bloco. A funo free (C) ou Freemem (Pascal) retorna a memria alocada previamente para o heap, permitindo que a poro da memria seja realocada. O argumento passado para malloc um inteiro no-sinalizado que representa o nmero necessrio de bytes de armazenagem. Se houver memria disponvel, malloc retornar void * que pode ser convertido para o tipo desejado de ponteiro. Um ponteiro void significa um ponteiro de tipo desconhecido ou genrico. Um ponteiro void no pode ser usado para referenciar qualquer coisa (pois ele no aponta para qualquer tipo especfico de dado), mas pode conter um ponteiro de qualquer outro tipo. Portanto podemos converter qualquer ponteiro num ponteiro void e reconverter sem qualquer perda de informao. O seguinte exemplo aloca armazenagem para um nmero varivel de valores float:
#include <stdio.h> #include <conio.h> #include <stdlib.h> float *fpt; int i, qtd; void main() { clrscr(); printf("Quantidade de valores: "); scanf("%d",&qtd); fpt = (float *) malloc( sizeof(float) * qtd); for (i=0;i<qtd;i++) { printf("%d valor: ",i+1); scanf("%f",&fpt[i]); } for (i=0;i<qtd;i++) { printf("%d valor: %6.2f\n",i+1,fpt[i]); } getch(); free(fpt); }

A funo malloc obtm armazenagem suficiente para tantas vezes o tamanho de um float quanto for solicitado. Cada bloco de armazenagem requisitado inteiramnte separado e distinto de todos os outros. No podemos fazer suposies sobre onde os blocos sero alocados. Os blocos so tipicamente identificados com algum tipo de informao que permite que o sistema operacional gerencie sua localizao e tamanho. Quando o bloco no mais necessrio, podemos retorn-lo para o sistema operacional por meio do seguinte comando: free( < nome_ponteiro > ) Em Pascal as funes so semelhantes no modo de operar mas um pouco diferentes na sintaxe. O procedimento Getmem recebe dois parmetros, sendo o primeiro o nome do ponteiro e o segundo a quantidade de bytes a serem alocados. Uma diferena a ser observada que o procedimento de liberao de memria Freemem precisa tambm da informao da quantidade de bytes a serem liberados. O exemplo abaixo em Pascal semelhante ao anterior escrito em C: 39

uses crt; type vetor = Array [1..1] of real; var fpt: ^vetor; i, qtd: integer; begin clrscr; write('Quantidade de valores: '); readln (qtd); GetMem(fpt, sizeof(real) * qtd); for i:= 1 to qtd do begin write(i,' valor: '); readln(fpt^[i]); end; for i:= 1 to qtd do begin writeln(i,' valor: ', fpt^[i]:6:2); end; readkey; FreeMem(fpt,sizeof(real)*qtd); end.

Quando precisamos de variveis com um tamanho desconhecido na compilao, devemos usar alocao dinamica de memria, no heap. Devemos porm tomar o cuidado de liberar a memria quando no precisamos mais da varivel, pois o compilador s devolve automaticamente a memria ocupada pelas variveis locais. Se no liberarmos a memria corretamente, o programa pode vir a travar. A grande vantagem da utilizao de alocao dinmica que permite-se criar estruturas semelhantes a matrizes com dimenses desconhecidas no momento da compilao. Devido a uma limitao da linguagem Pascal, uma matriz ou vetor no pode ocupar mais do que 64 Kbytes de memria, portanto, quando necessitamos de uma matriz ou vetor com mais de 64 Kbytes devemos usar alocao dinmica de memria. Pode-se inclusive criar matrizes com nmero variado de colunas para cada linha, como por exemplo:

#include #include #include #include

<stdio.h> <stdlib.h> <conio.h> <alloc.h>

int **matriz; int *colunas; unsigned long lin, col, i, j; void main() { clrscr(); printf("Memoria livre: %lu bytes\n", (unsigned long) coreleft()); printf("Linhas: "); scanf("%d",&lin); matriz=(int **) malloc(sizeof(int *) * lin); colunas=(int *) malloc(sizeof(int) * lin); printf("Memoria livre: %lu bytes\n", (unsigned long) coreleft()); if (matriz==NULL) { printf("Nao foi possivel alocar memoria\n"); exit(1); } for(i=0;i<lin;i++) { printf("Colunas na linha %d: ",i); scanf("%d",&col); matriz[i]=(int *) malloc(sizeof(int)*col);

40

if(matriz[i]==NULL) { printf("Nao foi possivel alocar memoria\n"); exit(1); } colunas[i]=col; printf("Memoria livre: %lu bytes\n", (unsigned long) coreleft()); } printf("Memoria livre: %lu bytes\n", (unsigned long) coreleft()); for(i=0;i<lin;i++) { printf("Linha %d:\n",i); for(j=0;j<colunas[i];j++) { printf("%d Valor: ",j); scanf("%d",&matriz[i][j]); } } for(i=0;i<lin;i++) { printf("\n"); for(j=0;j<colunas[i];j++) { printf("%5d",matriz[i][j]); } } for(i=0;i<lin;i++) { free(matriz[i]); } free(matriz); free(colunas); printf("\nMemoria livre: %lu bytes\n", (unsigned long) coreleft()); getch(); }

41

6. LISTAS LINEARES
Uma das formas mais comumente usadas para se manter dados agrupados a lista. Afinal, quem nunca organizou uma lista de compras antes de ir ao mercado, ou ento uma lista dos amigos que participaro da festa? As listas tm-se mostrado um recurso bastante til e eficiente no dia-a-dia das pessoas. Em computao, no tem sido diferente: a lista uma das estruturas de dados mais empregadas no desenvolvimento de programas.

6.1 Fundamentos
Uma lista linear uma coleo L: [a1, a2, ... an ], n 0, cuja propriedade estrutural baseia-se apenas na posio relativa dos elementos, que so dispostos linearmente. Se n = 0 dizemos que a lista vazia; caso contrrio, so vlidas as seguintes propriedades: a1 o primeiro elemento de L; an o ltimo elemento de L; ak , 1 < k < n, precedido pelo elemento ak - 1 e seguido pelo elemento ak + 1 em L. Em outras palavras, a caracterstica fundamental de uma lista linear o sentido de ordem unidimensional dos elementos que a compem. Uma ordem que nos permite dizer com preciso onde a coleo inicia-se e onde termina, sem possibilidade de dvida. Entre as diversas operaes que podemos realizar sobre listas, temos: acessar um elemento qualquer da lista; inserir um elemento numa posio especfica da lista; remover um elemento de uma posio especfica da lista; combinar duas listas em uma nica; particionar uma lista em duas; obter cpias de uma lista; determinar o total de elementos na lista; ordenar os elementos na lista procurar um determinado elemento na lista; apagar uma lista; outras ...

Considerando-se somente as operaes de acesso, insero e remoo, restritas aos extremos da lista, temos casos especiais que aparecem muito freqentemente na modelagem de problemas a serem resolvidos por computador. Tais casos especiais recebem tambm nomes especiais: Pilha: lista linear onde todas as inseres, remoes e acessos so realizados em um nico extremo. Listas com esta caracterstica so tambm denominadas lista LIFO (Last-In/First-Out) ou em portugus: UEPS (ltimo que Entra/Primeiro que Sai). Fila: lista linear onde todas as inseres so feitas num certo extremo e todas as remoes e acessos so realizados no outro extremo. Filas so tambm denominadas listas FIFO (First-In/First-Out) ou em portugus: PEPS (Primeiro que Entra/Primeiro que Sai). Fila Dupla: lista linear onde as inseres, remoes ou acessos so realizados em qualquer extremo. Filas Duplas so tambm denominadas DEQUE (Double-Ended QUEue), ou em portugus: fila de extremidade dupla. Uma Fila Dupla pode ainda gerar dois casos especiais: Fila Dupla de Entrada Restrita (se a entrada for restrita a um nico extremo) e Fila Dupla de Sada Restrita (se a remoo for restrita a um nico extremo)

42

6.2 Formas de Armazenamento


Ao desenvolver uma implementao para listas lineares, o primeiro problema que surge : como podemos armazenar os elementos da lista, dentro do computador? Sabemos que antes de executar um programa, o computador precisa carregar seu cdigo executvel para a memria. Da rea de memria que reservada para o programa, uma parte usada para armazenar as instrues a serem executadas e a outra destinada ao armazenamento dos dados. Quem determina quanto de memria ser usado para as instrues o compilador. Alocar rea para armazenamento de dados entretanto, responsabilidade do programador. A alocao de memria, do ponto de vista do programador, estar em uma das quatro categorias apresentadas a seguir: Seqencial Esttica Dinmica Encadeada

Esttica Seqencial Dinmica Seqencial

Esttica Encadeada Dinmica Encadeada

6.2.1 Alocao Esttica versus Dinmica Dizemos que as variveis de um programa tm alocao esttica se a quantidade total de memria utilizada pelos dados previamente conhecida e definida de modo imutvel, no prprio cdigo-fonte do programa. Durante toda a execuo, a quantidade de memria utilizada pelo programa no varia. Por outro lado, se o programa capaz de criar novas variveis enquanto executa, isto , se as reas de memria que no foram declaradas no programa possam a existir durante a sua execuo, ento dizemos que a alocao dinmica. 6.2.2 Alocao Seqencial A forma mais natural de armazenar uma lista dentro do computador consiste em colocar os seus elementos em clulas de memria consecutivas, um aps o outro. Assim, se cada clula tem um endereo nico e um tamanho igual para todos, pode-se facilmente calcular onde esto as demais clulas. A maior vantagem no uso de uma rea seqencial de memria para armazenar uma lista linear que, dado o endereo inicial da rea alocada e o ndice de um elemento qualquer da lista, podemos acess-lo imediatametne, com um simples e rpido clculo. O ponto fraco desta forma de armazenamento aparece qaundo precisamos inserir ou retirar elementos do meio da lista, quando ento um certo esforo ser necessrio para movimentar os elementos, de modo a abrir espao para insero, ou de modo a ocupar o espao liberado por um elemento que foi removido. Nesta forma de armazenamento, a ordem fsica dos elementos dentro da estrutura de armazenamento sempre igual sua ordem lgica. 6.2.3 Alocao Encadeada Ao invs de manter os elementos agrupados numa rea contnua de memria, isto , ocupando clulas consecutivas, na alocao encadeada os elementos podem ocupar quaIsquer clulas (no necessariamente consecutivas) e, para manter a relao de ordem linear, juntamente com cada elemento armazenado o endereo do prximo elemento da lista. Desta forma, na alocao encadeada, os elementos so armazenados em blocos denominados nodos, sendo que cada nodo composto por pelo menos dois campos: um ou mais campos para armazenar dados (conjunto de informaes como cdigo, nome, ...) e outro para armazenar o endereo do prximo nodo. 43

Dois endereos especiais devem ser destacados: o endereo do primeiro elemento da lista (L) deve ser conhecido para poder saber onde se inicia a lista; o endereo nulo do elemento fictcio que segue o ltimo elemento da lista. Nesta forma de armazenamento, a ordem fsica dos elementos dentro da estrutura de armazenamento no precisa ser igual sua ordem lgica.

44

7. PILHAS
A pilha uma das estrutura de dados mais teis em computao. Uma infinidade de problemas clssicos da rea podem ser resolvidos com o uso delas. Uma pilha um tipo especial de lista linear em que toas as operaes de insero e remoo so realizadas numa mesma extremidade, denominada topo. Cada vez que um novo elemento deve ser inserido na pilha, ele colocado no seu topo; e em qualquer momento, apenas aquele posicionado no topo da pilha pode ser removido. Devido a esta disciplina de acesso, os elementos so sempre removidos numa ordem inversa quela em que foram inseridos, de modo que o ltimo elemento que entra exatamente o primeiro que sai. Da o fato destas listas serem tambm denominadas listas LIFO (Last-In/First-Out) ou em portugus: UEPS (ltimo que Entra/Primeiro que Sai). Uma pilha ou lista LIFO uma coleo que pode aumentar ou diminuir durante a sua existncia. O nome pilha advm justamente da semelhana entre o modo como as listas LIFO crescem e diminuem e o modo como as pilhas, no mundo real, funcionam. O exemplo mais comum do quotidiano uma pilha de pratos, onde o ltimo prato colocado o primeiro a ser usado (removido da pilha). Uma pilha suporta 3 operaes bsicas, tradicionalmente denominadas como: Top (Visualizar o Topo): acessa o elemento posicionado no topo da pilha; Push (Inserir): insere um novo elemento no topo da pilha; Pop (Retirar): remove um elemento do topo da pilha. Sendo P uma pilha e x um elemento qualquer, a operao Push ( P, x ) aumenta o tamanho da pilha P , acrescentando o elemento x no seu topo. A operao Pop ( P ) faz com que a pilha diminua, removendo e retornando o elemento existente em seu topo. Das trs operaes bsicas, a nica que no altera o estado da pilha Top ( P ); ela simplesmente retorna uma cpia do elemento existente no topo da pilha sem remov-lo. Exerccio 1: Dadas as operaes sobre uma pilha P, preencha o quadro a seguir com estado da pilha e o resultado de cada operao: Operao Push (P, 10) Push (P, 15) Pop ( P ) Top ( P ) Pop ( P ) Push ( P, 5) Push ( P, 8) Pop ( P ) Push ( P, 13) Top ( P ) Pop ( P ) Pop ( P ) Push ( P, 3 ) Top ( P ) P: [] P: [ 10 ] P: [ 10, 15 ] P: [ 10 ] P: [ 10 ] Estado da Pilha Resultado 15 10

45

7.1 Organizao de Dados na pilha


Como os elementos da pilha so armazenados em seqncia, um sobre o outro, e a incluso ou excluso de elementos no requer movimentao de dados, o esquema de alocao seqencial de memria mostra-se bastante apropriado para implement-las. Neste esquema, a forma mais simples de se represntar uma pilha na memria consiste em : um vetor, que serve para armazenar os elementos contidos na pilha; um ndice, utilizado para indicar a posio corrente de topo da pilha. Nota que vetor o mecanismo oferecido pelas linguagen de programao nos premite alocar, de forma esttica, uma rea seqencial de memria, e o ndice o recurso necessrio para disciplinar o acesso aos elementos da pilha. 7.1.1 Declarando uma pilha Para declarar uma pilha, precisamos ento de duas variveis: um vetor com o tamanho necessrio para armazenar as informaes e uma varivel que indica o topo, como no exemplo abaixo, que implementa uma pilha de tamanho mximo igual a 50:
Program Pilha; Var V: Array[1..50] of integer; topo: integer;

7.1.2 Inicializando uma pilha Para inicializar uma pilha, deve-se zerar o topo:
Program Pilha; Var V: Array[1..50] of integer; topo: integer; Begin topo := 0; ...

7.1.3 Verificando limites da pilha Uma insero s pode ser feita na pilha se o topo for menor que o seu tamanho mximo, caso contrrio ocorre o que se chama de estouro de pilha ou stack overflow. Da mesma forma, uma retirada da pilha s pode ser feita se o topo for maior que zero, caso contrrio ocorre o que se chama de stack underflow. Para verificar se uma pilha est cheia, basta testar se o topo igual ao seu tamanho mximo. O teste para verificar se pode ser feita uma insero numa pilha que tem tamanho mximo igual a 50 mostrado no exemplo abaixo:
if topo =50 then begin writeln(Pilha cheia!); end;

Para verificar se uma pilha est vazia, basta testar se o topo igual a zero. O teste para verificar se pode ser feita uma retirada mostrado no exemplo abaixo:
if topo = 0 then begin

46

writeln(Pilha vazia!); end;

7.1.4 Inserindo elementos na pilha Para inserir um elemento na pilha, precisamos saber qual o elemento a ser inserido e testar se a pilha est cheia. Se a mesma no est cheia, incrementamos o topo e na posio do topo inserir o novo elemento. O exemplo abaixo ilustra a insero de um elemento na pilha:
Program Pilha; Uses Crt; Var V: Array[1..50] of integer; topo: integer; x: integer; Begin topo := 0; write(Valor a inserir: ); readln (x); if topo = 50 then begin writeln(Pilha cheia!); end else begin topo := topo + 1; V[topo] := x; end; ... end.

7.1.5 Removendo elementos na pilha Para remover um elemento na pilha, precisamos saber se a pilha no est vazia. Se a mesma no est vazia, mostra-se o valor que est no vetor na posio apontada pelo topo e decrementa-se o topo. O exemplo abaixo ilustra a remoo de um elemento da pilha:
Program Pilha; Uses Crt; Var V: Array[1..50] of integer; topo: integer; x: integer; Begin topo := 0; write(Valor a inserir: ); readln (x); if topo = 50 then begin writeln(Pilha cheia!); end else begin topo := topo + 1; V[topo] := x; end; if topo = 0 then begin writeln(Pilha vazia!); end else begin

47

writeln(O valor retirado : , V[topo]); topo := topo - 1; end; end.

7.1.6 Verificando o elemento do topo da pilha Para verificar o elemento que est no topo da pilha, precisamos saber se a pilha no est vazia. Se a mesma no est vazia, mostra-se o valor que est no vetor na posio apontada pelo topo sem decrementar o topo. O exemplo abaixo ilustra a verificao do elemento que est no topo da pilha:
Program Pilha; Uses Crt; Var V: Array[1..50] of integer; topo: integer; x: integer; Begin topo := 0; write(Valor a inserir: ); readln (x); if topo = 50 then begin writeln(Pilha cheia!); end else begin topo := topo + 1; V[topo] := x; end; if topo = 0 then begin writeln(Pilha vazia!); end else begin writeln(O valor que est no topo : , V[topo]); end; end.

7.2 Estruturas do Tipo Pilha


Outra forma de implementar uma pilha usando uma estrutura do tipo registro e implementar a pilha com procedimentos e funes, como na implementao a seguir, em Pascal:
unit Pilhas; interface const MAX = 50; type Elem = char; Pilha = record topo: integer; memo: array[1..MAX] of Elem; end; var P: Pilha; procedure function function procedure function Init(var P:Pilha); IsEmpty(var P:Pilha):boolean; IsFull(var P:Pilha):boolean; Push(var P:Pilha; x:Elem); Pop(var P:Pilha):Elem;

48

function

Top(var P:Pilha):Elem;

implementation procedure Init(var P:Pilha); begin P.topo: = 0; end; function IsEmpty(var P:Pilha):boolean; begin if P.topo= 0 then IsEmpty:= true else IsEmpty:= false; end; function IsFull(var P:Pilha):boolean; begin if P.topo= max then IsFull:= true else IsFull:= false; end; procedure Push(var P:Pilha; x:Elem); begin if not IsFull(P) then begin P.topo:= P.topo + 1; P.memo[P.topo]:= x; end else begin writeln (Stack Overflow!); end; end; procedure Pop(var P:Pilha):Elem; begin if not IsEmpty(P) then begin Pop:= P.memo [P.topo]; P.topo:= P.topo - 1; end else begin writeln (Stack Overflow!); end; end; function Top(var P:Pilha):Elem; begin if not IsEmpty (P) then begin Top:= P.memo[P.topo] end else begin writeln (Stack Underflow!); end; end.

49

7.3 Pilhas usando a biblioteca STL do C++


Um adaptador stack da STL do C++ implementa os seguintes mtodos: push: empilha pop: desempilha empty: verifica se est vazia top: obtm valor do topo da pilha

Exemplo: #include <stack> #include <iostream> #include <string> using namespace std; stack <string> pilha; int op; string s; int main() { do { cout << endl << "Exemplo Pilha " << endl; cout << "1 - Empilhar " << endl; cout << "2 - Desempilhar " << endl; cout << "3 - Verifica se esta vazia" << endl; cout << "4 - Mostra o Topo: " << endl; cout << "5 - Desempilhar Tudo: " << endl; cout << "9 - Sair: " << endl; cout << "Escolha a opcao: " << endl; cin >> op; if (op==1) { cout << "Digite a informacao a ser empilhada: "; cin >> s; pilha.push(s); } if (op==2) { if (!pilha.empty()) { s = pilha.top(); pilha.pop(); cout << "a informacao retirada da pilha e: " << s << endl; } else { cout << "pilha vazia" << endl; } } if (op==3) { if (pilha.empty()) cout << "A pilha esta vazia." << endl; else cout << "A pilha NAO esta vazia." << endl; } if (op==4) { s = pilha.top(); cout << "a informacao que esta no topo da pilha e: " << s << endl; } if (op==5) { cout << "Desempilhando tudo: " << endl; while (!pilha.empty()) { cout << pilha.top() << " "; pilha.pop(); } } } while (op!=9); return 0; }

50

7.4 Exerccios
1. Fazer um programa em Pascal que manipule uma pilha. O programa deve ter um menu principal com as seguintes opes: 1 - Inserir um elemento na pilha 2 - Retirar um elemento na pilha 3 - Verificar o elemento que est no topo da pilha 4 - Sair Os elementos da pilha devem ser a descrio dos objetos (string).

2. Fazer um programa em Pascal que implemente o jogo Par Ou mpar Com Baralho com 52 cartas (A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K) com seus respectivos valores numricos (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13). As 52 cartas devem ser embaralhadas e distribudas em 3 pilhas sobre a mesa. Um jogador escolhe par e o outro, obviamente escolhe mpar e o jogo comea, com cada um escolhendo uma das 3 pilhas para retirar uma carta (pode-se optar por no retirar uma carte e apenas uma vale para o par/mpar). A cada rodada as cartas escolhidas so mostradas e o placar do Par ou mpar atualizado. O jogo encerra quando no h mais cartas para serem retiradas das pilhas. O programa a seguir implementa esse jogo em C++ usando a biblioteca STL:
using namespace std; #include <stack> #include <iostream> #include <string> stack <int> p1, p2, p3; int opp, opi, i, j, aux; int v[52]; int carta, carta1, carta2, par, impar, soma; int main() { srand ( time(NULL) ); // inicializa gerador de numeros aleatorios carta=1; par=0; impar=0; for(i=0;i<52;i++) { v[i]=carta; carta=carta % 13 + 1; // incrementa a carta e quando chega a 13 volta para 1 } for(i=0;i<52;i++) // embaralha o vetor de cartas { j = rand() % 52; aux = v[i]; v[i]=v[j]; v[j]=aux; } for(i=0;i<52;i++) // distribui as cartas em 3 pilhas { if (i%3==0) p1.push(v[i]); if (i%3==1) p2.push(v[i]); if (i%3==2) p3.push(v[i]); } do { cout << endl << "Escolha a pilha para o jogador que quer par "; if (!p1.empty()) cout << " [1] "; if (!p2.empty()) cout << " [2] "; if (!p3.empty()) cout << " [3] "; cin >> opp; // le a escolha do jogador 1 cout << "Escolha a pilha para o jogador que quer impar: "; if (!p1.empty()) cout << " [1] "; if (!p2.empty()) cout << " [2] "; if (!p3.empty()) cout << " [3] ";

51

cin >> opi; // le a escolha do jogador 2 carta1=0; carta2=0; if (opp==1 && !p1.empty()) // retira a carta de acordo com a escolha do jogador { carta1=p1.top(); p1.pop(); } if (opp==2 && !p2.empty()) { carta1=p2.top(); p2.pop(); } if (opp==3 && !p3.empty()) { carta1=p3.top(); p3.pop(); } if (opi==1 && !p1.empty()) { carta2=p1.top(); p1.pop(); } if (opi==2 && !p2.empty()) { carta2=p2.top(); p2.pop(); } if (opi==3 && !p3.empty()) { carta2=p3.top(); p3.pop(); } cout << "Cartas: " << carta1 << " - " << carta2 << endl; soma=carta1+carta2; if (soma % 2 == 0 ) par++; else impar++; cout << soma << " - Par " << par << " x " << impar << " Impar" << endl; } while (!p1.empty() || !p2.empty() || !p3.empty()); if (par>impar) cout << " Par Ganhou - Par " << par << " x " << impar << " Impar" << endl; if (impar>par) cout << " Impar Ganhou - Impar " << impar << " x " << par << " Par" << endl; if (impar==par) cout << " Empate - Par " << par << " x " << impar << " Impar" << endl; system("pause"); return(0); }

52

8. FILAS
Uma fila um tipo especial de lista linear em que as inseres so realizadas num extremo, ficando as remoes restritas ao outro. O extremo onde os elementos so inseridos denominado final da fila, e aquele de onde so removidos denominado comeo da fila. Cada vez que uma operao de insero executada, um novo elemento colocado no final da fila. Na remoo, sempre retornado o elemento que aguarda h mais tempo na fila, ou seja, aquele posicionado no comeo. A ordem de sada corresponde diretamente ordem de entrada dos elementos na fila, de modo que os primeiros elementos que entram so sempre os primeiros a sair. Por este motivo, as filas so denominadas listas FIFO (First-In/First-Out) ou PEPS (Primeiro que Entra/Primeiro que Sai). Um exemplo bastante comum de filas verifica-se num balco de atendimento, onde pessoas formam uma fila para aguardar at serem atendidas. Naturalmente, devemos desconsiderar os casos de pessoas que furam a fila ou que desistem de aguardar! Diferentemente das filas no mundo real, o tipo abstrato de dados no suporta insero nem remoo no meio da lista. comeo sai final entra

Figura 4.1 - Uma fila de caixa bancrio

A palavra queue, da lngua inglesa, significa fila. Por tradio, as operaes bsicas que uma fila suporta so: Enqueue (Inserir): insere um novo elemento no final da fila; Dequeue (Retirar): remove um elemento do comeo da fila. Sendo F uma fila e x um elemento qualquer, a operao Enqueue ( F, x ) aumenta o tamanho da fila F , acrescentando o elemento x no seu final. A operao Dequeue ( F ) faz com que a fila diminua, removendo e retornando o elemento existente em seu comeo. Exerccio 1: Dadas as operaes sobre uma fila F, preencha o quadro a seguir com estado da fila e o resultado de cada operao: Operao Enqueue ( F, 10 ) Enqueue ( F, 15 ) Dequeue ( F ) Enqueue ( F, 5 ) Enqueue ( F, 8 ) Dequeue ( F ) Enqueue ( F, 13 ) Dequeue ( F ) Dequeue ( F ) Enqueue ( F, 21 ) Enqueue ( F, 50 ) Dequeue ( F ) F: [ ] F: [ 10 ] F: [ 10, 15 ] F: [ 15 ] Estado da Fila Resultado 10

53

Dequeue ( F ) Dequeue ( F )

8.1 Implementao Seqencial de Filas


Graficamente, representamos uma fila como uma coleo de objetos que cresce da esquerda para a direita, com dois extremos bem definidos: comeo e final: comeo F: a b c d final ...

Figura 4.2 - Representao grfica de uma fila F: [a, b, c, d]


A partir da representao grfica percebemos que possvel implementar uma fila tendo trs recursos bsicos: espao de memria para armazenas os elementos (um vetor); uma referncia ao primeiro elemento da coleo (uma varivel); uma referncia primeira posio livre, aps o ltimo elemento da fila (uma varivel). 8.1.1 Declarando uma fila Para declarar uma fila, precisamos ento de trs variveis: um vetor com o tamanho necessrio para armazenar as informaes; uma varivel que indica o comeo da fila e uma varivel que indica o final da fila, como no exemplo abaixo, que implementa uma fila de tamanho mximo igual a 50:
Program Fila; Var V: Array[1..50] of integer; comeco, final : integer;

8.1.2 Inicializando uma fila Para inicializar uma fila, deve-se atribuir o valor 1 s variveis que controlam o comeo e o final da fila:
Program Fila; Var V: Array[1..50] of integer; comeco, final : integer; Begin comeco := 1; final := 1; ...

8.1.3 Verificando limites da fila Uma insero s pode ser feita na fila se a fila no estiver cheia (final for menor que o seu tamanho mximo). Da mesma forma, uma retirada da fila s pode ser feita se existem elementos na fila (o comeo for diferente do final).

54

Para verificar se uma fila est cheia, basta testar se o final maior que o seu tamanho mximo. O teste para verificar se pode ser feita uma insero numa fila que tem tamanho mximo igual a 50 mostrado no exemplo abaixo:
if final > 50 then begin writeln(Fila cheia!); end;

Para verificar se uma fila est vazia, basta testar se o comeo igual ao final. O teste para verificar se pode ser feita uma retirada mostrado no exemplo abaixo:
if comeco = final then begin writeln(Fila vazia!); end;

8.1.4 Inserindo elementos na fila Para inserir um elemento na fila, precisamos saber qual o elemento a ser inserido e testar se a fila est cheia. Se a mesma no est cheia, o elemento inserido na posio apontada pela varivel final, que deve ser incrementada depois da insero. O exemplo abaixo ilustra a insero de um elemento na fila:
Program Fila; Var V: Array[1..50] of integer; comeco, final : integer; x: integer; Begin comeco := 1; final := 1; write(Valor a inserir: ); readln (x); if final > 50 then begin writeln(Fila cheia!); end else begin V[final] := x; final := final + 1 end; ... end.

8.1.5 Removendo elementos da fila Para remover um elemento da fila, precisamos saber se a fila no est vazia. Se a mesma no est vazia, mostra-se o valor que est no vetor na posio apontada pelo comeco e incrementa-se o comeo. O exemplo abaixo ilustra a remoo de um elemento da fila:
Program Fila; Var V: Array[1..50] of integer; comeco, final : integer; x: integer; Begin comeco := 1; final := 1; write(Valor a inserir: ); readln (x);

55

if final > 50 then begin writeln(Fila cheia!); end else begin V[final] := x; final := final + 1 end; if comeco = final then begin writeln(Fila vazia!); end else begin writeln(O valor retirado : , V[comeco]); comeco := comeco + 1; end; end.

8.1.6 Problemas na Implementao Seqencial de Filas Vimos que cada vez que um elemento removido, o ndice que aponta o comeo da fila desloca-se uma posio direita (incrementado). Supondo que a fila tenha um tamanho mximo de 50, aps 50 inseres o ponteiro que aponta para o final da fila tem o valor 51 e a fila estar cheia, pois o final maior que o tamanho mximo representa fila cheia, no podendo ser inserido mais nenhum elemento. Aps 50 retiradas, o ponteiro que aponta para o comeo da fila ter o valor 51 e a fila estar vazia, pois o comeo igual ao final representa fila vazia. Resumindo, chegamos a uma situao extremamente indesejvel. Temos uma fila cheia e vazia ao mesmo tempo. Afinal como isto possvel? Chegamos concluso de que esta nossa implementao no muito eficiente, apresentando tanto desperdcio de memria quanto problemas de lgica.

8.2 Solucionando os Problemas da Implementao Seqencial


Eliminar o erro lgico, que sinaliza fila vazia e cheia ao mesmo tempo, bastante simples. Basta acrescentar uma varivel contadora para indicar quantos elementos esto armazenados na fila. Esta varivel deve ser inicialmente zerada. Quando um elemento for inserido, ela ser incrementada; quando for removido, ela ser decrementada. Desta forma, o impasse pode ser resolvido simplesmente consultando essa varivel. Para eliminar o desperdcio de memria, o ideal seria que cada posio liberada por um elemento removido se tornasse prontamente disponvel para receber um novo elemento inserido. Para isso teramos de dispor de uma rea seqencial de memria tal que a posio 1 estivesse imediatamente aps a ltima posio. Assim, a fila somente estaria cheia, quando realmente tivesse um elemento para cada posio.

56

1 a ... b c final 5 e d 4

comeo

2 3

Figura 4.3 - Uma fila na representao circular

8.3 Implementao Circular para Filas


A partir da representao grfica percebemos que possvel implementar uma fila circular tendo quatro recursos bsicos: espao de memria para armazenas os elementos (um vetor); uma referncia ao primeiro elemento da coleo (uma varivel); uma referncia primeira posio livre, aps o ltimo elemento da fila (uma varivel); um indicador da quantidade de elementos da fila.

8.3.1 Declarando uma fila Para declarar uma fila, precisamos ento de quatro variveis: um vetor com o tamanho necessrio para armazenar as informaes; uma varivel que indica o comeo da fila; uma varivel que indica o final da fila e uma varivel que indica a quantidade de elementos na fila, como no exemplo abaixo, que implementa uma fila de tamanho mximo igual a 50:
Program Fila; Var V: Array[1..50] of integer; qtd, comeco, final : integer;

8.3.2 Inicializando uma fila Para inicializar uma fila, deve-se atribuir o valor 1 s variveis que controlam o comeo e o final da fila e zero para a contadora de elementos:
Program Fila; Var V: Array[1..50] of integer; qtd, comeco, final : integer; Begin comeco := 1; final := 1; qtd := 0; ...

57

8.3.3 Verificando limites da fila Uma insero s pode ser feita na fila se a fila no estiver cheia, (se a quantidade de elementos for menor que a quantidade mxima). Da mesma forma, uma retirada da fila s pode ser feita se existem elementos na fila (se a quantidade de elementos for maior que zero). Para verificar se uma fila est cheia, basta testar se a varivel contadora igual ao seu tamanho mximo. O teste para verificar se pode ser feita uma insero numa fila que tem tamanho mximo igual a 50 mostrado no exemplo abaixo:
if qtd = 50 then begin writeln(Fila cheia!); end;

Para verificar se uma fila est vazia, basta testar se o comeo igual ao final. O teste para verificar se pode ser feita uma retirada mostrado no exemplo abaixo:
if qtd = 0 then begin writeln(Fila vazia!); end;

8.3.4 Inserindo elementos na fila Para inserir um elemento na fila, precisamos saber qual o elemento a ser inserido e testar se a fila est cheia. Se a mesma no est cheia, o elemento inserido na posio apontada pela varivel final, que deve ser incrementada depois da insero. O exemplo abaixo ilustra a insero de um elemento na fila:
Program Fila; Var V: Array[1..50] of integer; qtd, comeco, final : integer; x: integer; Begin comeco := 1; final := 1; qtd := 0; write(Valor a inserir: ); readln (x); if qtd = 50 then begin writeln(Fila cheia!); end else begin V[final] := x; qtd := qtd + 1; if final = 50 then begin final := 1 ; end else begin final := final + 1 end; end; ... end.

58

8.3.5 Removendo elementos da fila Para remover um elemento da fila, precisamos saber se a fila no est vazia. Se a mesma no est vazia, mostra-se o valor que est no vetor na posio apontada pelo comeco e incrementa-se o comeo. O exemplo abaixo ilustra a remoo de um elemento da fila:
Program Fila; Var V: Array[1..50] of integer; qtd, comeco, final : integer; x: integer; Begin comeco := 1; final := 1; qtd := 0; write(Valor a inserir: ); readln (x); if qtd = 50 then begin writeln(Fila cheia!); end else begin V[final] := x; qtd := qtd + 1; if final = 50 then begin final := 1 ; end else begin final := final + 1 end; end; if qtd = 0 then begin writeln(Fila vazia!); end else begin writeln(O valor retirado : , V[comeco]); qtd := qtd - 1; if comeco = 50 then begin comeco := 1 ; end else begin comeco := comeco + 1; end; end; end.

Exerccios Fazer um programa em Pascal que manipule uma fila de nomes de alunos. O programa deve ter um menu principal com as seguintes opes: 1 - Inserir um aluno na fila 2 - Retirar um aluno da fila 3 - Sair

59

8.4 Manipulao de Pilhas e Filas com Procedimentos e Funes


A grande vantagem de desenvolver procedimentos e funes para manipular a pilhas e filas que voc implementa uma nica vez um procedimento ou uma funo para cada operao da pilha ou fila e faz a chamada do mesmo para manipular quantas filas ou pilhas quiser. Pode-se ainda personalizar os nomes dos procedimentos e funes para facilitar o entendimento do programa. Por exemplo, para manipular uma pilha, vamos implementar os seguintes procedimentos/funes, de acordo com a operao: InicializaPilha init inicializa a varivel de controle topo com zero PilhaEstaVazia isEmpty testa se a pilha est vazia e retorna verdadeiro ou falso PilhaEstaCheia isFull testa se a pilha est cheia e retorna verdadeiro ou falso InsereNaPilha push insere um valor na pilha RetiraDaPilha pop retira um valor da pilha MostraTopoPilha top mostra quem est no topo da pilha, sem retirar

Para usar a pilha necessrio criar uma estrutura para armazenar os dados:
const MAX = 50; type Elem = string; // define o tamanho mximo da pilha como 50 // determina o tipo de elementos que ter na pilha

Pilha = record topo: integer; v: array[1..MAX] of Elem; end;

O exemplo a seguir implementa duas pilhas, fazendo chamada para os procedimentos/funes implementados. O enunciado do programa seria: Os jogadores do Grmio e do Inter resolveram autografar e doar algumas camisetas para seus torcedores. Os jogadores autografavam e entregavam a camiseta para o organizador, que a colocava numa pilha de acordo com o time. Faa um programa em Pascal que manipule 2 pilhas de camisetas (Grmio e Inter), com as operaes: 1 - Colocar camisa do Grmio na pilha 2 - Colocar camisa do Inter na pilha 3 - Retirar camisa do Grmio da pilha 4 - Retirar camisa do Inter da pilha

Observe, no programa a seguir que foram criadas duas pilhas, uma se chamando camisagremio e outra se chamando camisainter:
var

camisagremio, camisainter: Pilha;


Estas pilhas sero manipulada atravs das operaes: InsereNaPilha(camisagremio, x); InsereNaPilha(camisainter, x); x:=RetiraDaPilha(camisagremio); x:=RetiraDaPilha(camisainter); Observe tambm que chamado o mesmo procedimento 60

InsereNaPilha porm o que muda o primeiro parmetro que passado, informando em qual pilha dever ser inserido, se na pilha camisagremio ou na pilha camisainter, da mesma forma que a retirada da pilha usa a mesma funo RetiraDaPilha tambm informando de qual pilha ser a retirada. Execute o programa a seguir, identificando o funcionamento da pilha e as chamadas das operaes, de acordo com a opo escolhida no menu principal.
program ExemploPilha; uses crt; const MAX = 50; type Elem = string; // define o tamanho mximo da pilha como 50 // determina o tipo de elementos que ter na pilha

Pilha = record topo: integer; v: array[1..MAX] of Elem; end; var camisagremio, camisainter: Pilha; x: Elem; op: integer;

procedure InicializaPilha(var P:Pilha); begin P.topo:= 0; end; function PilhaEstaVazia(var P:Pilha):boolean; begin if P.topo= 0 then PilhaEstaVazia:= true else PilhaEstaVazia:= false; end; function PilhaEstaCheia(var P:Pilha):boolean; begin if P.topo= max then PilhaEstaCheia:= true else PilhaEstaCheia:= false; end; procedure InsereNaPilha(var P:Pilha; x:Elem); begin if not PilhaEstaCheia(P) then begin P.topo:= P.topo + 1; P.v[P.topo]:= x; end else begin writeln ('Pilha Cheia'); end; end; function RetiraDaPilha(var P:Pilha):Elem; begin if not PilhaEstaVazia(P) then begin RetiraDaPilha:= P.v[P.topo]; P.topo:= P.topo - 1; end else

61

begin RetiraDaPilha:=''; writeln ('Pilha Vazia!'); end; end; function MostraTopoPilha(var P:Pilha):Elem; begin if not PilhaEstaVazia(P) then begin MostraTopoPilha:= P.v[P.topo] end else begin MostraTopoPilha:=''; writeln ('Stack Underflow!'); end; end;

Begin InicializaPilha(camisagremio); InicializaPilha(camisainter); repeat clrscr; writeln('1 - Colocar camisa do Gremio na pilha'); writeln('2 - Colocar camisa do Inter na pilha'); writeln('3 - Retirar camisa do Gremio da pilha'); writeln('4 - Retirar camisa do Inter da pilha'); writeln('9 - Sair'); write ('Escolha a Opao: '); readln(op); if op=1 then begin writeln('Informe o nome do jogador que autografou a camisa do Gremio: '); readln(x); InsereNaPilha(camisagremio, x); end; if op=2 then begin writeln('Informe o nome do jogador que autografou a camisa do Inter: '); readln(x); InsereNaPilha(camisainter, x); end; if op=3 then begin x:=RetiraDaPilha(camisagremio); if x<>'' then writeln('Camisa do Gremio autografada por ',x); end; if op=4 then begin x:=RetiraDaPilha(camisainter); if x<>'' then writeln('Camisa do Inter autografada por ',x); end; readkey; until op=9; end.

De forma semelhante pode-se manipular as filas tambm personalizando os nomes dos procedimentos e funes para facilitar o entendimento do programa. Por exemplo, para manipular uma fila, vamos implementar os seguintes procedimentos/funes, de acordo com a operao: InicializaFila init inicializa as variveis de controle quantidade (com zero), comeo e final (com 1) FilaEstaVazia isEmpty testa se a fila est vazia e retorna verdadeiro ou falso FilaEstaCheia isFull testa se a fila est cheia e retorna verdadeiro ou falso InsereNaFila Enqueue insere um valor na fila 62

RetiraDaFila Dequeue retira um valor da fila Para usar a fila tambm necessrio criar uma estrutura para armazenar os dados:
const MAX = 50; type Elem = string; // define o tamanho mximo da pilha como 50 // determina o tipo de elementos que ter na pilha

Fila = record quant : integer; comeco: integer; final : integer; v: array[1..max] of Elem; end;

O exemplo a seguir implementa duas filas, fazendo chamada para os procedimentos/funes implementados. O enunciado do programa seria: Os torcedores do Grmio e do Inter vo ganhar camisetas autografadas de alguns jogadores de seu time. Para isso precisam ir at a organizao do evento e entrar na fila de acordo com o seu time. Faa um programa em Pascal que implemente duas filas de torcedores, com as seguintes operaes: 1 - Inserir Gremista na fila 2 - Inserir Colorado na fila 3 - Retirar Gremista da fila 4 - Retirar Colorado da fila 5 - Retirar Todos Gremistas da fila 6 - Retirar Todos Colorados da fila

Observe, no programa a seguir que foram criadas duas filas, uma se chamando gremistas e outra se chamando colorados:
var

Gremistas, Colorados: fila;


Estas filas sero manipulada atravs das operaes: InsereNaFila(Gremistas, x); InsereNaFila(Colorados, x); x:=RetiraDaFila(gremistas); x:=RetiraDaFila(colorados); Observe tambm que chamado o mesmo procedimento InsereNaFila porm o que muda o primeiro parmetro que passado, informando em qual fila dever ser inserido, se na fila Gremistas ou na fila Colorados, da mesma forma que a retirada da fila usa a mesma funo RetiraDaFila tambm informando de qual fila ser a retirada. Execute o programa a seguir, identificando o funcionamento da fila e as chamadas das operaes, de acordo com a opo escolhida no menu principal.
program testeFila; uses crt; const max = 50; type Elem = string;

63

Fila = record quant : integer; comeco: integer; final : integer; v: array[1..max] of Elem; end; var Gremistas, Colorados: fila; x: Elem; op: integer; procedure InicializaFila(var F: Fila); begin F.quant:= 0; F.comeco:= 1; F.final:= 1; end; function FilaEstaVazia(var F: Fila): boolean; begin if F.quant = 0 then FilaEstaVazia:= true else FilaEstaVazia:= false; end; function FilaEstaCheia(var F: Fila): boolean; begin if f.quant = max then FilaEstaCheia:= true else FilaEstaCheia:= false; end; procedure InsereNaFila(var F:Fila; x: Elem); begin if not FilaEstaCheia(F) then begin f.quant:=f.quant+1; F.v[F.final]:=x; F.final:= F.final+1; if F.final>max then F.final:= 1; end else writeln('Fila Cheia'); end; function RetiraDaFila(var F: Fila): Elem; begin if not FilaEstaVazia(F) then begin f.quant:=f.quant-1; RetiraDaFila:= F.v[F.comeco]; F.comeco:= F.comeco+1; if F.comeco>max then F.comeco:= 1; end else begin RetiraDaFila:=''; writeln('Fila Vazia'); end; end; Begin InicializaFila(Gremistas); InicializaFila(Colorados); repeat clrscr; writeln('1 - Inserir Gremista na fila');

64

writeln('2 - Inserir Colorado na fila'); writeln('3 - Retirar Gremista da fila'); writeln('4 - Retirar Colorado da fila'); writeln('5 - Retirar Todos Gremistas da fila'); writeln('6 - Retirar Todos Colorados da fila'); writeln('9 - Sair'); write ('Escolha a opcao: '); readln(op); if op=1 then begin write('Informe o nome do gremista: '); readln(x); InsereNaFila(Gremistas, x); end; if op=2 then begin write('Informe o nome do colorado: '); readln(x); InsereNaFila(Colorados, x); end; if op=3 then begin x:=RetiraDaFila(gremistas); if x<>'' then writeln('Retirando gremista da fila: ',x); end; if op=4 then begin x:=RetiraDaFila(colorados); if x<>'' then writeln('Retirando colorado da fila: ',x); end; if op=5 then begin if FilaEstaVazia(gremistas) then writeln('Fila de Gremistas Vazia') else writeln('Retirando todos gremistas da fila: '); while not FilaEstaVazia(gremistas) do begin writeln(RetiraDaFila(gremistas)); end; end; if op=6 then begin if FilaEstaVazia(colorados) then writeln('Fila de Colorados Vazia') else writeln('Retirando todos colorados da fila: '); while not FilaEstaVazia(colorados) do begin writeln(RetiraDaFila(colorados)); end; end; readkey; until op=9; end.

Atividade: Fazer um programa em Pascal que controla as pilhas de camisetas autografadas de acordo com o time e tambm a fila de torcedores. Juntar os dois programas anteriores (estruturas da pilha, fila, procedimentos e funes) e alterar o menu principal para executar as seguintes operaes: 1 - Colocar camisa do Grmio na pilha 2 - Colocar camisa do Inter na pilha 3 - Inserir Gremista na fila 4 - Inserir Colorado na fila 5 - Entregar Camiseta para um Gremista da fila 8 - Retirar Colorado da fila 9 - Retirar Todos Gremistas da fila 10 - Retirar Todos Colorados da fila 99 Sair

65

8.5 Filas usando a biblioteca STL do C++


Um adaptador queue da STL do C++ implementa os seguintes mtodos: push: coloca na fila pop: retira da fila empty: verifica se est vazia front: obtm o valor que est na frente da fila back: obtm o valor que est no final da fila size: mostra o tamanho da fila
#include <queue> #include <iostream> #include <string> using namespace std; queue <string> fila; int op; string s; int main() { do { system("cls"); cout << endl << "Exemplo Fila " << endl; cout << "1 - Colocar na Fila " << endl; cout << "2 - Retirar da Fila " << endl; cout << "3 - Verifica se esta vazia" << endl; cout << "4 - Mostra Quem Est na Frente da Fila: " << endl; cout << "5 - Mostra Quem Est no Final da Fila: " << endl; cout << "6 - Retirar Tudo: " << endl; cout << "9 - Sair: " << endl; cout << "Escolha a opcao: " << endl; cin >> op; if (op==1) { cout << "Digite a informacao para ir para a fila: "; cin >> s; fila.push(s); } if (op==2) { if (!fila.empty()) { s = fila.front(); fila.pop(); cout << "a informacao retirada da fila e: " << s << endl; } else { cout << "fila vazia" << endl; } } if (op==3) { if (fila.empty()) cout << "A fila esta vazia." << endl; else cout << "A fila NAO esta vazia." << endl; } if (op==4) { s = fila.front(); cout << "quem esta na frente da fila e: " << s << endl; } if (op==5) { s = fila.back(); cout << "quem esta no final da fila e: " << s << endl; } if (op==6) {

66

cout << "Retirando tudo: " << endl; while (!fila.empty()) { cout << fila.front() << " "; fila.pop(); } } cout << endl << endl; system("pause"); } while (op!=9); return 0; }

Exerccios Uma empresa resolveu entrevistar os candidatos s vagas de vendedor que chegarem ao escritrio da empresa num horrio pr-determinado. Os candidatos sero chamados em ordem de chegada at que as vagas sejam preenchidas. Quando as vagas forem preenchidas, os candidatos restantes devem ser chamados (remover todos elementos da fila) e escolher se querem ou no participar da entrevista para o cargo de servios gerais. Se quiserem, devem ser inseridos numa nova fila. Fazer um programa em Pascal que manipule 2 filas com os nomes dos candidatos. O programa deve ter um menu principal com as seguintes opes: 1 - Inserir um candidato na fila 2 - Chamar um candidato para o cargo de vendedor 3 - Cadastrar os candidatos restantes (que quiserem) para as vagas de servios gerais 4 - Chamar um candidato para o cargo de servios gerais. 9 - Sair

67

9. LISTAS ORDENADAS
Uma lista ordenada L:[a 1, a 2, ..., a n ] uma lista linear tal que, sendo n > 1, temos: a 1 a k para qualquer 1 < k n; a k a n para qualquer 1 k < n; a k - 1 a k a k+1 , para qualquer 1 < k < n. Se L uma lista ordenada, podemos garantir que nenhum elemento em L inferior a a 1 ou superior a a n. Alm disso, tomando um elemento qualquer no meio da lista, nenhum elemento sua esquerda o supera e nenhum elemento sua direita inferior a ele. Entre as diversas operaes que podem ser realizadas com uma lista ordenada, podemos destacar: Ins (Inserir): insere um novo elemento na lista ordenada; Rem (Remover): remove um elemento da lista ordenada; Find (Encontrar): procura um elemento na lista ordenada. Sendo L uma lista ordenada e x um elemento qualquer, a operao Ins ( L, x ) aumenta o tamanho da lista L , acrescentando o elemento x na sua respectiva posio. A operao Rem ( L ) faz com que a lista diminua, se o elemento estiver na mesma, removendo o elemento e retornando verdadeiro. A operao Find ( L, x ) retornar a posio do elemento x na lista. Exerccio 1: Dadas as operaes sobre uma lista ordenada L, preencha o quadro a seguir com estado da lista e o resultado de cada operao: Operao Ins (L, 10) Ins (L, 15) Ins (L, 8) Ins (L, 12) Ins (L, 5) Ins (L, 20) Rem (L, 8) Rem (L, 10) Rem (L, 9) Find (L, 12) Ins (L, 13) Find (L, 15) Find (L, 7) Rem (L, 10) Estado da Lista Ordenada L: [ ] L: [ 10 ] L: [ 10, 15 ] L: [ 8, 10, 15 ] L: [ 8, 10, 12, 15 ] L: [ 5, 8, 10, 12, 15 ] L: [ 5, 8, 10, 12, 15, 20 ] L: [ 5, 10, 12, 15, 20 ] L: [ 5, 12, 15, 20 ] L: [ 5, 12, 15, 20 ] L: [ 5, 12, 15, 20 ] L: [ 5, 12, 13, 15, 20 ] L: [ 5, 12, 13, 15, 20 ] L: [ 5, 12, 13, 15, 20 ] L: [ 5, 12, 13, 15, 20 ] Resultado V V F 2 4 0 F

9.1 Organizao de Dados na Lista Ordenada


Como numa lista ordenada as operaes de insero e retirada no esto restritas aos extremos, necessitaramos de uma grande movimentao de dados para deix-los ordenados. Para evitar essa movimentao dos dados, vamos usar 2 vetores: um para armazenar a informao e outro para armazenar o ndice do prximo elemento. Alm disso, necessrio uma varivel para armazenar o endereo (ndice) do primeiro elemento da lista. 68

Como em uma retirada precisamos remover um elemento de qualquer parte, precisamos armazenar essa informao numa lista (pilha) para poder novamente utilizar essa posio. Portanto, para implementar uma lista ordenada, precisaremos de: um vetor (info), que serve para armazenar os elementos contidos na lista; um vetor (elo), que serve para armazenar o ndice do prximo elemento; um ndice (prim), que serve para armazenar o ndice do primeiro elemento; um vetor (pnd), que serve para armazenar a pilha dos nodos disponveis; um ndice, (topo) utilizado para indicar a posio corrente de topo da pilha dos nodos disponveis.

Uma Lista Ordenada pode ser graficamente representada: prim

info
elo

a1
2

a2
3

a3
4

...
...

an
0

9.1.1 Declarando uma lista ordenada Para declarar uma pilha, precisamos ento das cinco variveis apresentadas anteriormente, como no exemplo abaixo, que implementa uma lista ordenada de tamanho mximo igual a 50:
Program Lista_Ordenada; var info : Array[1..50] of integer; elo, pnd : Array[1..50] of integer; prim, topo : integer;

9.1.2 Inicializando uma Lista Ordenada Para inicializar uma lista ordenada, precisamos: 1 - Inicializar a pilha dos nodos disponveis; 2 - Colocar todos os elos da lista ordenada na pilha dos nodos disponveis; 3 - Inicializar o primeiro elemento com 0.
program listaordenada; uses crt; var info : Array[1..50] of integer; elo, pnd : Array[1..50] of integer; prim, topo : integer; i, x, nodo, aux: integer; Begin topo := 0; for i:=50 downto 1 do begin if topo = 50 then begin writeln('Pilha cheia!'); end else begin topo:=topo+1; pnd[topo] := i; end; end; prim := 0; ...

69

9.1.3 Inserindo elementos em uma Lista Ordenada Para inserir um elemento numa lista ordenada com capacidade para 50 elementos, precisamos: 1 - Obter a informao a ser inserida; 2 - Obter o nodo disponvel para a insero atravs da retirada de um elemento da pilha dos nodos disponveis 3 - Inserir no vetor de informaes (info) da lista, no nodo disponvel, a informao lida; 4 - Atualizar os elos da lista, podendo ter 4 casos: A lista ordenada est vazia: Neste caso o primeiro elo passa a ser o nodo obtido e o prximo elo passa a ser nulo (0). O Elemento x menor ou igual ao primeiro elemento da lista: Neste caso o primeiro elo passa a ser o nodo obtido e o prximo elo passa a ser o nodo que era o primeiro antes da insero. O Elemento x maior que o primeiro elemento da lista, porm existe outro que o supera: Neste caso temos que encontrar o local correto para realizar a insero. Como no possvel o acesso direto aos nodos da lista encadeada, devemos partir do primeiro nodo e ir seguindo os campos de ligao, um a um at encontrar o nodo que armazena um elemento que supera x. Quando encontrado, o elo anterior aponta para o nodo inserido e o elo do nodo inserido aponta para o elemento que supera x. O Elemento x maior que todos os elementos da lista: Neste caso, novamente partimos do primeiro elemento e seguimos os campos de ligao at encontrar o ltimo. O ltimo nodo da lista aponta para o nodo inserido e o elo do nodo inserido passa a ser nulo (0). Podemos simplificar o algoritmo, agrupando numa mesma operao os dois ltimos casos. O exemplo abaixo exemplifica uma operao de 10 inseres em uma lista com capacidade para 50 elementos:
program listaordenada; uses crt; var info : Array[1..50] of integer; elo, pnd : Array[1..50] of integer; prim, topo : integer; i, x, nodo, aux: integer; Begin topo := 0; for i:=50 downto 1 do begin if topo = 50 then begin writeln('Pilha cheia!'); end else begin topo:=topo+1; pnd[topo] := i; end; end; prim := 0; for i:=1 to 10 do begin write('Valor a inserir '); readln(x); if topo = 0 then begin writeln('Pilha vazia'); end else begin nodo:=pnd[topo]; topo:=topo-1;

70

info[nodo]:=x; if (prim=0) then begin prim:=nodo; elo[nodo]:=0; end else begin if (x <= info[prim]) then begin elo[nodo]:=prim; prim:=nodo; end else begin aux := prim; while (elo[aux] <> 0) and (x > info[elo[aux]]) do begin aux:=elo[aux]; end; elo[nodo]:=elo[aux]; elo[aux]:=nodo; end; end; end; end; ... end.

9.1.4 Removendo elementos de uma Lista Ordenada Para remover um elemento de uma lista ordenada com capacidade para 50 elementos, precisamos: 1 - Obter a informao a ser removida (elemento x); 2 - Descobrir se a informao est na lista ordenada; 3 - Remover do vetor de informaes (info) da lista, no nodo localizado, a informao desejada; 4 - Inserir na pilha de nodos disponveis o nodo retirado; 4 - Atualizar os elos da lista, podendo ter 2 casos: O Elemento x menor ou igual ao primeiro elemento da lista: Neste caso o primeiro elo passa a ser o elo do primeiro nodo. O Elemento x maior que o primeiro elemento da lista: Neste caso temos que encontrar o nodo em que o elemento se encontra. Como no possvel o acesso direto aos nodos da lista encadeada, devemos partir do primeiro nodo e ir seguindo os campos de ligao, um a um at encontrar o nodo em que o elemento se encontra. Quando encontrado, o elo anterior deve apontar para o elo do nodo encontrado. O exemplo abaixo exemplifica uma operao de 3 remoo em uma lista com capacidade para 50 elementos:
program listaordenada; uses crt; var info : Array[1..50] of integer; elo, pnd : Array[1..50] of integer; prim, topo : integer; i, x, nodo, aux: integer; Begin ... for i:=1 to 3 do begin write('Valor a Retirar: '); readln(x); if (prim=0) or (x<info[prim]) then begin

71

writeln('O valor no esta na lista!'); end else begin if (x=info[prim]) then begin if topo=50 then begin writeln('Pilha cheia!'); end else begin writeln('O valor foi retirado da lista!'); topo := topo+1; pnd[topo]:=prim; prim:=elo[prim]; end; end else begin aux:=prim; while (elo[aux] <> 0) and (x > info[elo[aux]]) do begin aux:=elo[aux]; end; nodo:=elo[aux]; if (aux<>0) and (info[nodo]=x) then begin if topo=50 then begin writeln('Pilha cheia!'); end else begin writeln('O valor foi retirado da lista!'); topo := topo+1; pnd[topo]:=nodo; elo[aux]:=elo[nodo]; end; end else begin writeln('O valor no esta na lista!'); end; end; end; end; ... readkey; end.

9.1.5 Imprimindo uma Lista Ordenada Para imprimir uma lista ordenada, precisamos partir do primeiro nodo e ir seguindo os campos de ligao, um a um at que seja encontrado um elo nulo (igual a 0). O exemplo abaixo exemplifica uma operao de impresso de uma lista:
program listaordenada; uses crt; var info : Array[1..50] of integer; elo, pnd : Array[1..50] of integer; prim, topo : integer; i, x, nodo, aux: integer; Begin ...

72

aux := prim; while (aux <> 0) do begin writeln(info[aux]); aux:=elo[aux]; end; ... end.

9.1.6 Pesquisando na Lista Ordenada Para pesquisar um elemento de uma lista ordenada com capacidade para 50 elementos, precisamos: 1 - Obter a informao a ser pesquisada (elemento x); 2 - Descobrir se a informao est na lista ordenada; 3 - Contar os elementos para informar a posio do mesmo. O exemplo abaixo exemplifica uma operao pesquisa em uma lista com capacidade para 50 elementos:
program listaordenada; uses crt; var info : Array[1..50] of integer; elo, pnd : Array[1..50] of integer; prim, topo : integer; i, x, nodo, aux, cont: integer; Begin ... writeln('Pesquisa de 5 elementos: '); for i:=1 to 5 do begin write('Valor a Pesquisar: '); readln(x); if (prim=0) or (x<info[prim]) then begin writeln('O valor no esta na lista!'); end else begin if (x=info[prim]) then begin writeln('O valor ESTA na lista na posio 1'); end else begin aux:=prim; cont:=2; while (elo[aux] <> 0) and (x > info[elo[aux]]) do begin aux:=elo[aux]; cont:=cont+1; end; nodo:=elo[aux]; if (aux<>0) and (info[nodo]=x) then begin writeln('O valor ESTA na lista na posio: ',cont); end else begin writeln('O valor no esta na lista!'); end; end; end; end; readkey; end.

73

9.1.7 Exemplo completo de uma Lista Ordenada com procedimentos O exemplo abaixo exemplifica uma lista ordenada com capacidade de armazenar 50 elementos, com as operaes: 10 inseres na lista, 1 impresso da lista aps as 10 inseres, 3 remoes da lista, 1 impresso da lista aps as 3 remoes, 5 pesquisas na lista.

program ExemploLista; uses crt; const MAX = 50; type Elem = string; // define o tamanho mximo da lista como 50 *****************

// determina o tipo de elementos que ter na lista ************

//***************** ESTRUTURA DA LISTA **************************************** Lista = record // * info: Array[1..MAX] of Elem; // Informao que est na lista * elo : Array[1..MAX] of integer; // elo para o prximo elemento da lista * pnd : Array[1..MAX] of integer; // pilha de nodos disponveis * prim: integer; // aponta para o primeiro elemento da lista* topo: integer; // controla o topo da pnd * end; // * //***************************************************************************** var x: Elem; // elemento que ser lido ou retirado da lista *************** Compras: Lista; // define o nome da lista ************************************ i: integer; //*********** PROCEDIMENTO DE MANIPULAO DA LISTA **************************** procedure InicializaLista(var L: Lista); //* begin L.topo := 0; for i:= MAX downto 1 do begin if L.topo = MAX then begin writeln('Pilha cheia!'); end else begin L.topo:=L.topo+1; L.pnd[L.topo] := i; end; end; L.prim := 0; end; procedure insereNaLista(var L: Lista; x: Elem); var nodo, aux: integer; begin if L.topo = 0 then begin writeln('Lista Cheia'); // no h nodos disponiveis na pilha (pnd) end else begin nodo:=L.pnd[L.topo]; L.topo:=L.topo-1; L.info[nodo]:=x; if (L.prim=0) then

74

begin L.prim:=nodo; L.elo[nodo]:=0; end else begin if (x <= L.info[L.prim]) then begin L.elo[nodo]:= L.prim; L.prim:=nodo; end else begin aux := L.prim; while (L.elo[aux] <> 0) and (x > L.info[L.elo[aux]]) do begin aux:=L.elo[aux]; end; L.elo[nodo]:= L.elo[aux]; L.elo[aux] := nodo; end; end; end; end; procedure MostraLista(var L: Lista); var aux: integer; begin aux := L.prim; while (aux <> 0) do begin writeln(L.info[aux]); aux:=L.elo[aux]; end; end; procedure removeDaLista(var L: Lista; x: Elem); var aux, nodo: integer; begin if (L.prim=0) or (x < L.info[L.prim]) then begin writeln('O valor no esta na lista!'); end else begin if (x=L.info[L.prim]) then begin if L.topo=MAX then begin writeln('Lista vazia!'); end else begin writeln('O elemento ', x,' foi retirado da lista!'); L.topo := L.topo+1; L.pnd[L.topo]:= L.prim; L.prim:=L.elo[L.prim]; end; end else begin aux:=L.prim; while (L.elo[aux] <> 0) and (x > L.info[L.elo[aux]]) do begin aux:=L.elo[aux]; end; nodo:=L.elo[aux]; if (aux<>0) and (L.info[nodo]=x) then begin if L.topo=MAX then begin

75

writeln('Lista Vazia!'); end else begin writeln('O elemento ', x, ' foi retirado da lista!'); L.topo := L.topo+1; L.pnd[L.topo]:=nodo; L.elo[aux]:=L.elo[nodo]; end; end else begin writeln('O elemento no esta na lista!'); end; end; end; end; procedure pesquisaNaLista(var L: Lista; x:Elem); var aux, nodo, cont: integer; begin if (L.prim=0) or (x<L.info[L.prim]) then begin writeln('O elemento no esta na lista!'); end else begin if (x=L.info[L.prim]) then begin writeln('O elemento ESTA na lista na posio 1'); end else begin aux:=L.prim; cont:=2; while (L.elo[aux] <> 0) and (x > L.info[L.elo[aux]]) do begin aux:=L.elo[aux]; cont:=cont+1; end; nodo:=L.elo[aux]; if (aux<>0) and (L.info[nodo]=x) then begin writeln('O elemento ESTA na lista na posio: ',cont); end else begin writeln('O elemento no esta na lista!'); end; end; end; end; //* //*********** FIM DOS PROCEDIMENTO DE MANIPULAO DA LISTA ********************* Begin clrscr; InicializaLista(Compras); writeln('Insero de 10 elementos: '); for i:=1 to 10 do begin write('Elemento a inserir '); readln(x); insereNaLista(Compras,x); end; MostraLista(compras); writeln('Remoo de 3 elementos: '); for i:=1 to 3 do begin write('Elemento a Retirar: '); readln(x); removeDaLista(compras,x);

76

end; MostraLista(compras); writeln('Pesquisa de 5 elementos: '); for i:=1 to 5 do begin write('Elemento a Pesquisar: '); readln(x); pesquisaNaLista(compras, x); end; readkey; end.

Exerccios Fazer um programa em Pascal que manipule uma lista encadeada de nomes de alunos. O programa deve ter um menu principal com as seguintes opes: 1 - Inserir um aluno na lista 2 - Retirar um aluno da lista 3 - Pesquisar se um aluno est na lista 4 - Imprimir a lista de alunos 5 Sair

9.2 Lista encadeada com alocao dinmica de memria:


Para representarmos um grupo de dados, j vimos que podemos usar um vetor, que a forma mais primitiva de representar diversos elementos agrupados, bastando declarar um vetor escolhendo um nmero mximo de elementos. No entanto, o vetor no uma estrutura de dados muito flexvel, pois precisamos dimension-lo com um nmero mximo de elementos. Se o nmero de elementos que precisarmos armazenar exceder a dimenso do vetor, teremos um problema, pois no existe uma maneira simples e barata (computacionalmente) para alterarmos a dimenso do vetor em tempo de execuo. Por outro lado, se o nmero de elementos que precisarmos armazenar no vetor for muito inferior sua dimenso, estaremos subutilizando o espao de memria reservado. A soluo para esses problemas utilizar estruturas de dados que cresam medida que precisarmos armazenar novos elementos (e diminuam medida que precisarmos retirar elementos armazenados anteriormente). Tais estruturas so chamadas dinmicas e armazenam cada um dos seus elementos usando alocao dinmica. Esta estrutura de dados conhecida como lista encadeada. Uma lista encadeada a forma ideal de armazenamento de dados quando no se sabe antecipadamente quantos itens de dados tero de ser armazenados. A lista encadeada pode ser ordenada ou no, dependendo da necessidade do programador, sendo a lista ordenada a forma mais usada. Numa lista encadeada, para cada novo elemento inserido na estrutura, alocamos um espao de memria para armazen-lo. Desta forma, o espao total de memria gasto pela estrutura proporcional ao nmero de elementos nela armazenado. No entanto, no podemos garantir que os elementos armazenados na lista ocuparo um espao de memria contguo, portanto no temos acesso direto aos elementos da lista. Para que seja possvel percorrer todos os elementos da lista, devemos explicitamente guardar o encadeamento dos elementos, o que feito armazenando-se, junto com a informao de cada elemento, um ponteiro para o prximo elemento da lista.Esquematicamente, uma lista encadeada semelhante a: Pri Ult Pri Ela funciona ssim: para cada item, existe tambm um ponteiro direcionado para o item de dados seguinte. A qualquer momento, pode-se acrescenta outro item de dados lista, contanto que o que o ltimo ponteiro seja atualizado a fim de apontar para o novo item de dados. Partindo-se do incio da lista, possvel percorr-la utilizando-se o ponterio de cada item, que aponta para o prximo item de dados. O tlimo ponteiro da lista 77

dados ponteiro

dados ponteiro

dados ponteiro

dados NULL

geralmetne um ponteiro NULL (C) ou NIL (Pascal/Delphi), portando, sabemos que estamos no final da lista quando encontramos um ponteiro NULL (C) ou NIL (Pascal/Delphi). preciso tambm termos 3 ponteiros auxiliares, para a alocao dinmica da memria: um ponteiro que aponta para o primeiro elemento, para saber onde inicia a lista, um ponteiro que aponta para o ltimo elemento da lista, para colocarmos o valor do novo ponteiro quando inserido um elemento sem ter que percorrer a lista inteira, e um ponteiro auxiliar, que aponta para a nova rea de memria alocada. Podemos observar no exemplo a seguir, uma lista encadeada com alocao dinmica de memria com opo de ordenao ou no das informaes, ou seja, as informaes so inseridas na lista de forma que ela sempre est com os dados em ordem crescente, se for escolhida a opo de ordenar ou inserir as informaes sempre no final da lista se no for escolhida a opo de ordenar:
program listadinamica; uses crt; type Elem = string; // determina o tipo de elementos que ter na lista ************

//***************** ESTRUTURA DA LISTA **************************************** listaDin = record info: string[20]; prox: Pointer; pri: Pointer; ult: Pointer; end; var x: Elem; Compras: ListaDin; op: integer; ordem: char; //************ PROCEDIMENTOS DE MANIPULAO DA LISTA ********************************** procedure inicializaLista(var L: ListaDin); begin L.pri:=NIL; L.pri:=NIL; end; procedure mostraLista(var L: ListaDin); var aux: ^listaDin; begin if L.pri=NIL then begin writeln('Lista Vazia.'); end else begin aux:= L.pri; while (aux<>nil) do begin writeln(aux^.info); aux:=aux^.prox; end; end; end; function pesquisaNaLista(var L: ListaDin; x: Elem): boolean; var aux: ^listaDin; cont: integer; begin cont:=0; pesquisaNaLista:=false; aux:= L.pri; while (aux<>nil) do begin cont:=cont+1;

78

if (aux^.info=x) then begin writeln('Encontrou na Posicao ',cont); pesquisaNaLista:=true; break; end; aux:=aux^.prox; end; end; function removeDaLista(var L: ListaDin; x: Elem): boolean; var aux, aux2: ^listaDin; begin removeDaLista:=false; if ListaDin(L.pri^).info = x then begin aux2 := L.pri; L.pri := ListaDin(L.pri^).prox; freemem(aux2,sizeof(listaDin)); removeDaLista:=true; end else begin aux:= L.pri; aux2:=L.pri; while (aux<>nil) do begin if (aux^.info=x) then begin aux2^.prox:=aux^.prox; if L.ult=aux then L.ult:=aux2; freemem(aux,sizeof(listaDin)); aux:=nil; removeDaLista:=true; break; end; aux2:=aux; aux:=aux^.prox; end; end; end; procedure limparLista(var L: ListaDin); var aux, aux2: ^listaDin; begin aux:=L.pri; L.pri:=NIL; while (aux<>nil) do begin aux2:=aux; aux:=aux^.prox; freemem(aux2,sizeof(listaDin)); end; end; procedure insereNaLista(var L: ListaDin; x: Elem); var inseri: integer; aux, ult, pen: ^listaDin; begin GetMem(aux,sizeof(listaDin)); if (aux=NIL) then begin writeln('Nao foi possivel alocar memoria'); exit; end; if (L.pri=NIL) then begin aux^.info:=x; aux^.prox:=NIL;

79

L.pri:=aux; L.ult:=aux; end else begin if (ListaDin(L.pri^).info > x) then begin aux^.info:=x; aux^.prox:=L.pri; L.pri:=aux; end else if (ListaDin(L.pri^).prox = NIL) then begin aux^.info:=x; aux^.prox:=NIL; ListaDin(L.pri^).prox:=aux; L.ult:=aux; end else if (ListaDin(L.ult^).info < x) then begin aux^.info:=x; aux^.prox:=NIL; ListaDin(L.ult^).prox:=aux; L.ult:=aux; end else begin inseri:=0; pen:=L.pri; ult:=L.pri; while (inseri = 0) and (ult^.prox<> NIL) do begin if (ult^.prox <> NIL) then begin pen:=ult; ult:=ult^.prox; end; if (ult^.info > x) then begin aux^.info:=x; aux^.prox:=ult; pen^.prox:=aux; inseri:=1; end; end; if (inseri=0) then begin aux^.info:=x; aux^.prox := NIL; ult^.prox := aux; L.ult:=aux end; end; end; end; procedure insereNoFinalDaLista(var L: ListaDin; x: Elem); var aux : ^listaDin; begin GetMem(aux,sizeof(listaDin)); if (aux=NIL) then begin writeln('Nao foi possivel alocar memoria'); exit; end; aux^.info:=x; aux^.prox:=NIL; if L.pri=NIL then L.pri:=aux else ListaDin(L.ult^).prox:=aux; L.ult:=aux; end;

80

//************ FIM DOS PROCEDIMENTOS DE MANIPULAO DA LISTA ***************************

begin inicializaLista(compras); repeat clrscr; write('A lista deve ficar ordenada [S/N]? '); ordem:=upcase(readkey); writeln(ordem); until (ordem='S') or (ordem='N'); repeat writeln('1 - Inserir na Lista'); writeln('2 - Remover da Lista'); writeln('3 - Mostrar Lista'); writeln('4 - Pesquisar na Lista'); writeln('5 - Limpar Lista'); writeln('9 - Sair'); readln(op); if op=1 then begin write('Elemento a inserir '); readln(x); if ordem='S' then insereNaLista(Compras,x) else insereNoFinalDaLista(Compras,x); end; if op=2 then begin write('Elemento a Retirar: '); readln(x); if removeDaLista(compras,x) then writeln(x, ' foi removido da lista.') else writeln(x, ' NAO estava na lista.') end; if op=3 then begin mostraLista(compras); end; if op=4 then begin write('Elemento a Pesquisar: '); readln(x); if pesquisaNaLista(compras, x) then writeln(x, ' esta na lista.') else writeln(x, ' NAO esta na lista.') end; if op=5 then begin limparLista(compras); end; until op=9; end.

81

9.3 Listas encadeadas para listagem em ordem de dados armazenados em arquivos


Existem vrias situaes em que dispomos dos dados que esto armazenados em disco e que precisamos mostrar essas informaes numa determinada ordem, diferente da ordem de insero dos registros. Para isso h duas alternativas usando listas encadeadas: criando uma lista encadeada com alocao dinmica de memria ou criando um arquivo de ndice dos registros sob a forma de lista ordenada. 9.3.1 Lista encadeada com alocao dinmica de memria para exibir os dados de um Arquivo Para usar uma lista encadeada, basta inserir um procedimento que percorre todo o arquivo e ao invs de listar os dados, insere na lista. Ao final do processo, percorre a lista e escreve os dados na tela. Como os programas deste porte comeam a ficar com pouca legibilidade devido ao seu tamanho, interessante separ-los em unidades distintas, como no exemplo a seguir, no qual foram separadas as informaes do controle da lista (primeiro e ltimo) e as informaes que compem o nodo da lista. Observe as chamadas para as funes/procedimentos que controlam a lista:
unlistadinamicaClientes.pas unit unlistadinamicaClientes; interface type Elem = string;

// determina o tipo de elementos que ter na lista ************

//***************** ESTRUTURA DA LISTA **************************************** listaDin = record pri: Pointer; ult: Pointer; end; nodo = record info: Elem; prox: Pointer; end; procedure inicializaLista(var L: ListaDin); procedure mostraLista(var L: ListaDin); function pesquisaNaLista(var L: ListaDin; x: Elem): boolean; function removeDaLista(var L: ListaDin; x: Elem): boolean; procedure limparLista(var L: ListaDin); procedure insereNaLista(var L: ListaDin; x: Elem); procedure insereNoFinalDaLista(var L: ListaDin; x: Elem); implementation uses crt; procedure inicializaLista(var L: ListaDin); begin L.pri:=NIL; L.ult:=NIL; end; procedure mostraLista(var L: ListaDin); var aux: ^nodo; begin if L.pri=NIL then begin writeln('Lista Vazia.'); end else begin aux:= L.pri; while (aux<>nil) do begin writeln(aux^.info); aux:=aux^.prox; end; end; end;

82

function pesquisaNaLista(var L: ListaDin; x: Elem): boolean; var aux: ^nodo; cont: integer; begin cont:=0; pesquisaNaLista:=false; aux:= L.pri; while (aux<>nil) do begin cont:=cont+1; if (aux^.info=x) then begin writeln('Encontrou na Posio ',cont); pesquisaNaLista:=true; break; end; aux:=aux^.prox; end; end; function removeDaLista(var L: ListaDin; x: Elem): boolean; var aux, aux2: ^nodo; begin removeDaLista:=false; if nodo(L.pri^).info = x then begin aux2 := L.pri; L.pri := nodo(L.pri^).prox; freemem(aux2,sizeof(nodo)); removeDaLista:=true; end else begin aux:= L.pri; aux2:=L.pri; while (aux<>nil) do begin if (aux^.info=x) then begin aux2^.prox:=aux^.prox; if L.ult=aux then L.ult:=aux2; freemem(aux,sizeof(nodo)); aux:=nil; removeDaLista:=true; break; end; aux2:=aux; aux:=aux^.prox; end; end; end; procedure limparLista(var L: ListaDin); var aux, aux2: ^nodo; begin aux:=L.pri; L.pri:=NIL; while (aux<>nil) do begin aux2:=aux; aux:=aux^.prox; freemem(aux2,sizeof(nodo)); end; end; procedure insereNaLista(var L: ListaDin; x: Elem); var inseri: integer; aux, ult, pen: ^nodo;

83

begin GetMem(aux,sizeof(nodo)); if (aux=NIL) then begin writeln('Nao foi possivel alocar memoria'); exit; end; if (L.pri=NIL) then begin aux^.info:=x; aux^.prox:=NIL; L.pri:=aux; L.ult:=aux; end else begin if (nodo(L.pri^).info > x) then begin aux^.info:=x; aux^.prox:=L.pri; L.pri:=aux; end else if (nodo(L.pri^).prox = NIL) then begin aux^.info:=x; aux^.prox:=NIL; nodo(L.pri^).prox:=aux; L.ult:=aux; end else if (nodo(L.ult^).info < x) then begin aux^.info:=x; aux^.prox:=NIL; nodo(L.ult^).prox:=aux; L.ult:=aux; end else begin inseri:=0; pen:=L.pri; ult:=L.pri; while (inseri = 0) and (ult^.prox<> NIL) do begin if (ult^.prox <> NIL) then begin pen:=ult; ult:=ult^.prox; end; if (ult^.info > x) then begin aux^.info:=x; aux^.prox:=ult; pen^.prox:=aux; inseri:=1; end; end; if (inseri=0) then begin aux^.info:=x; aux^.prox := NIL; ult^.prox := aux; L.ult:=aux end; end; end; end; procedure insereNoFinalDaLista(var L: ListaDin; x: Elem); var aux : ^nodo; begin GetMem(aux,sizeof(nodo));

84

if (aux=NIL) then begin writeln('Nao foi possivel alocar memoria'); exit; end; aux^.info:=x; aux^.prox:=NIL; if L.pri=NIL then L.pri:=aux else nodo(L.ult^).prox:=aux; L.ult:=aux; end; end.

ArquivoListaOrdenada.pas program ArquivoComListaOrdenada; uses crt, unlistadinamicaClientes; Type clientes=record codigo:integer; nome:string[40]; cpf:string[30]; salario: real; end; arquivo= file of clientes; var cli: clientes; arq: arquivo; o:integer; { registros dos clentes } { arquivo que contem os registros }

procedure abre_arquivo; begin assign(arq,'clientes.dat'); {$I-} { diretiva de compilacao que nao deixa o programa travar de erro } reset(arq); { se o arquivo ja existe, abre o arquivo, se nao existe codigo de erro } {$I+} { diretiva de compilacao que volta a travar o programa em erro } if IORESULT <> 0 then rewrite(arq); { se houve erro ao tentar abrir o arquivo, procedimento cria_arquivo } end; {------------------------ fecha arquivo ------------------------------ } procedure fecha_arquivo; begin CLOSE(ARQ); { fecha o arquivo arq } end; {------------------------ cadastro de cliente no arquivo ------------- } procedure cadastrar_clientes; var cod:integer; begin cod:=0; cli.codigo:=0; seek(arq,0); { posiciona o ponteiro no 1.0 registro } while(not eof(arq)) do { enquanto nao encontra o fim de arquivo } begin read(arq,cli); { le os registros } if cli.codigo>0 then cod:=cli.codigo; end; cli.codigo:=cod+1; write('Nome: '); readln(cli.nome); { le o nome do cliente que esta sendo cadastrado }

em caso gera um caso de chama o

85

if (cli.nome <> '') and (cli.nome <> ' ') then begin write('CPF: '); readln(cli.cpf); write('Salario: '); readln(cli.salario); write(arq,cli); {grava o registro} end; end; {--------lista todos cliente do arquivo em ordem de cadastro------------- } procedure lista_tudo; var cont: integer; begin cont:=0; seek(arq,0); { posiciona no 1.o registro do arquivo } writeln('--------------------------------------------'); writeln(' Cod - Nome - CPF - Salario'); writeln('--------------------------------------------'); while (not eof(arq)) do { enquanto nao chega no final do arquivo } begin read(arq,cli); if cli.codigo > 0 then { se o codigo for maior que 0 escreve os campos na tela } begin writeln(cli.codigo, ' - ', cli.nome, ' - ', cli.cpf, ' - ',cli.salario:0:2); cont:=cont+1; if cont mod 20 = 0 then readkey; end; end; writeln('--------------------------------------------'); readkey; end;

{ ------------------------- listar um item ---------------------------- } procedure lista_item; var cod:integer; begin write('Codigo a ser listado: '); readln(cod); seek(arq,0); while (cli.codigo <> cod) and (not eof(arq)) do begin read(arq,cli); end; if cli.codigo = cod then begin writeln('Codigo: ',cli.codigo); writeln('Nome: ',cli.nome); writeln('CPF: ',cli.cpf); writeln('Salario: ',cli.salario); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; { espera que seja pressionada uma tecla para listar outro registro } end else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; { espera que seja pressionada uma tecla para listar outro registro } end; end; {----------------------------- altera item -------------------------} procedure altera_item; var cod: integer; begin

86

write('Codigo a alterar: '); readln(cod); seek(arq,0); cli.codigo:=0; while (cli.codigo <> cod) and (not eof(arq)) do begin read(arq,cli); end; if cli.codigo = cod then begin { --- lista ---- } writeln('Codigo: ',cli.codigo); writeln('Nome: ',cli.nome); writeln('CPF: ',cli.cpf); writeln('Salario: ',cli.cpf); { --- altera --- } write('Novo Nome: '); readln(cli.nome); write('Novo CPF: '); readln(cli.cpf); write('Novo Salario: '); readln(cli.salario); seek(arq,filepos(arq)-1); { posiciona o ponteiro no registro a ser alterado } write(arq,cli); end else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end; end;

{--------------------------- exclui item ----------------------------- } procedure exclui_item; var cod: integer; begin write('Codigo a excluir: '); readln(cod); seek(arq,0); cli.codigo:=0; while (cli.codigo <> cod) and (not eof(arq)) do begin read(arq,cli); end; if cli.codigo = cod then begin seek(arq,filepos(arq)-1); cli.codigo:= -1; cli.nome:=' '; cli.cpf:=' '; cli.salario:=0; write(arq,cli); end else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end; end; procedure listagem_ordem_alfabetica; var ordemalfa: listadin; s: string; begin inicializaLista(ordemalfa); seek(arq,0); { posiciona no 1.o registro do arquivo }

87

while (not eof(arq)) do { enquanto nao chega no final do arquivo } begin read(arq,cli); if cli.codigo > 0 then { se o codigo for maior que 0 escreve os campos na tela } begin str(cli.salario:0:2, s); insereNaLista(ordemalfa, cli.nome + ' - ' + cli.cpf + ' - ' + s); end; end; writeln('--------------------------------------------'); writeln(' Nome - CPF - Salario'); writeln('--------------------------------------------'); mostraLista(ordemalfa); writeln('--------------------------------------------'); readkey; end; { -------------------- PROGRAMA PRINCIPAL --------------------------- } begin abre_arquivo; repeat clrscr; writeln(' MENU PRINCIPAL '); writeln(' 1 - CADASTRAR CLIENTES '); writeln(' 2 - LISTAR TODO CADASTRO '); writeln(' 3 - LISTAR UM CADASTRO '); writeln(' 4 - ALTERAR CADASTRO '); writeln(' 5 - EXCLUIR CLIENTE '); writeln(' 6 - LISTAGEM EM ORDEM ALFABEIICA'); writeln(' 9 - FIM '); writeln(' Escolha uma opcao: '); readln(o); if o=1 then begin cadastrar_clientes; end; if o=2 then begin lista_tudo; end; if o=3 then begin lista_item end; if o=4 then begin altera_item; end; if o=5 then begin exclui_item; end; if o=6 then begin listagem_ordem_alfabetica; end; until o=9; clrscr; fecha_arquivo; end.

88

9.3.2 Usando um arquivo de ndice com lista ordenada para exibir os dados de um Arquivo Para usar um arquivo com uma lista ordenada, necessrio criar um arquivo com a estrutura de lista ordenada e tambm algumas rotinas para manter esse arquivo consistente de acordo com as operaes de insero, excluso e alterao do arquivo principal. O exemplo a seguir apresenta um programa que manipula um arquivo de clientes e mantm atualizado o arquivo de ndice usando uma lista ordenada. Observe as rotinas de manipulao do arquivo de ndices:
ArquivoIndiceOrdenado.pas program ArquivoComListaOrdenada; uses crt; Type clientes=record codigo:integer; nome:string[40]; cpf:string[30]; salario: real; end; indicealfab = record nome: string[40]; registro: integer; prox: integer; end; arquivo= file of clientes; arqindicealfab = file of indicealfab; var cli: clientes; { registros dos clentes } arq: arquivo; { arquivo que contem os registros } arqalfab: arqindicealfab; { arquivo que contem os indices dos registros } o:integer; procedure listagem_ordem_alfabetica; var aux: indicealfab; cont: integer; begin cont:=0; seek(arqalfab,0); { posiciona no 1.o registro do arquivo } read(arqalfab,aux); writeln('--------------------------------------------'); writeln(' Cod - Nome - CPF - Salario'); writeln('--------------------------------------------'); while (aux.prox<>0) do { enquanto nao chega no final do arquivo } begin seek(arqalfab,aux.prox); read(arqalfab,aux); seek(arq,aux.registro); read(arq,cli); if cli.codigo > 0 then { se o codigo for maior que 0 escreve os campos na tela } begin writeln(cli.codigo, ' - ', cli.nome, ' - ', cli.cpf, ' - ',cli.salario:0:2); cont:=cont+1; if cont mod 20 = 0 then readkey; end; end; writeln('--------------------------------------------'); readkey; end; procedure removeDoIndice(nome: string; registro: integer); var aux, pri: indicealfab; penultimo, prox: integer; begin

89

seek(arqalfab,0); read(arqalfab,pri); if pri.prox>0 then begin seek(arqalfab,pri.prox); read(arqalfab,aux); if (aux.nome = nome) and (aux.registro=registro) then begin pri.prox:=aux.prox; seek(arqalfab,0); write(arqalfab,pri); end else begin while (aux.prox<>0) and (nome <> aux.nome) and (registro<>aux.registro) do begin penultimo:=filepos(arqalfab)-1; seek(arqalfab,aux.prox); read(arqalfab,aux); end; while (aux.prox<>0) and (nome > aux.nome) do begin penultimo:=filepos(arqalfab)-1; seek(arqalfab,aux.prox); read(arqalfab,aux); end; if (nome = aux.nome) and (registro=aux.registro) then begin prox:=aux.prox; seek(arqalfab,penultimo); read(arqalfab,aux); aux.prox:=prox; seek(arqalfab,filepos(arqalfab)-1); write(arqalfab,aux); end; end; end; end; procedure inserenoindice(nome: string; registro: integer); var aux, ind, pri: indicealfab; penultimo: integer; begin ind.nome:=nome; ind.registro:=registro; seek(arqalfab,0); read(arqalfab,pri); if (pri.prox=0) then begin pri.nome:=''; pri.registro:=0; pri.prox:=1; seek(arqalfab,0); write(arqalfab,pri); ind.prox:=0; seek(arqalfab,filesize(arqalfab)); write(arqalfab,ind); end else begin seek(arqalfab,pri.prox); read(arqalfab,aux); if (nome <= aux.nome) then begin ind.prox:= pri.prox; pri.prox:= filesize(arqalfab); seek(arqalfab,0); write(arqalfab,pri); seek(arqalfab,filesize(arqalfab)); write(arqalfab,ind);

90

end else begin seek(arqalfab,pri.prox); read(arqalfab,aux); while (aux.prox<>0) and (nome > aux.nome) do begin penultimo:=filepos(arqalfab)-1; seek(arqalfab,aux.prox); read(arqalfab,aux); end; if (nome< aux.nome) then begin seek(arqalfab,penultimo); read(arqalfab,aux); end; ind.prox:= aux.prox; aux.prox:=filesize(arqalfab); seek(arqalfab,filepos(arqalfab)-1); write(arqalfab,aux); seek(arqalfab,filesize(arqalfab)); write(arqalfab,ind); end; end; end;

procedure refazindices; var aux: indicealfab; begin writeln('refazendo indices...'); rewrite(arqalfab); aux.nome:=''; aux.prox:=0; write(arqalfab,aux); seek(arq,0); { posiciona no 1.o registro do arquivo } while (not eof(arq)) do { enquanto nao chega no final do arquivo } begin read(arq,cli); if cli.codigo > 0 then { se o codigo for maior que 0 escreve os campos na tela } begin insereNoIndice(cli.nome, filepos(arq)-1); end; end; end; procedure abre_arquivo; begin assign(arq,'clientes.dat'); {$I-} reset(arq); {$I+} if IORESULT <> 0 then rewrite(arq); assign(arqalfab,'clialfab.dat'); {$I-} reset(arqalfab); {$I+} if IORESULT <> 0 then rewrite(arqalfab); if filesize(arq)<>filesize(arqalfab)-1 then refazindices; end; {------------------------ fecha arquivo ------------------------------ } procedure fecha_arquivo; begin CLOSE(ARQ); { fecha o arquivo arq } end; {------------------------ cadastro de cliente no arquivo ------------- } procedure cadastrar_clientes;

91

var cod:integer; begin cod:=0; cli.codigo:=0; seek(arq,0); { posiciona o ponteiro no 1.0 registro } while(not eof(arq)) do { enquanto nao encontra o fim de arquivo } begin read(arq,cli); { le os registros } if cli.codigo>0 then cod:=cli.codigo; end; cli.codigo:=cod+1; write('Nome: '); readln(cli.nome); { le o nome do cliente que esta sendo cadastrado } if (cli.nome <> '') and (cli.nome <> ' ') then begin write('CPF: '); readln(cli.cpf); write('Salario: '); readln(cli.salario); write(arq,cli); {grava o registro} insereNoIndice(cli.nome,filepos(arq)-1); end; end; {--------lista todos cliente do arquivo em ordem de cadastro------------- } procedure lista_tudo; var cont: integer; begin cont:=0; seek(arq,0); { posiciona no 1.o registro do arquivo } writeln('--------------------------------------------'); writeln(' Cod - Nome - CPF - Salario'); writeln('--------------------------------------------'); while (not eof(arq)) do { enquanto nao chega no final do arquivo } begin read(arq,cli); if cli.codigo > 0 then { se o codigo for maior que 0 escreve os campos na tela } begin writeln(cli.codigo, ' - ', cli.nome, ' - ', cli.cpf, ' - ',cli.salario:0:2); cont:=cont+1; if cont mod 20 = 0 then readkey; end; end; writeln('--------------------------------------------'); readkey; end;

{ ------------------------- listar um item ---------------------------- } procedure lista_item; var cod:integer; begin write('Codigo a ser listado: '); readln(cod); seek(arq,0); while (cli.codigo <> cod) and (not eof(arq)) do begin read(arq,cli); end; if cli.codigo = cod then begin writeln('Codigo: ',cli.codigo); writeln('Nome: ',cli.nome); writeln('CPF: ',cli.cpf); writeln('Salario: ',cli.salario); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; { espera que seja pressionada uma tecla para listar outro registro } end

92

else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; { espera que seja pressionada uma tecla para listar outro registro } end; end; {----------------------------- altera item -------------------------} procedure altera_item; var cod: integer; begin write('Codigo a alterar: '); readln(cod); seek(arq,0); cli.codigo:=0; while (cli.codigo <> cod) and (not eof(arq)) do begin read(arq,cli); end; if cli.codigo = cod then begin { --- lista ---- } writeln('Codigo: ',cli.codigo); writeln('Nome: ',cli.nome); writeln('CPF: ',cli.cpf); writeln('Salario: ',cli.cpf); removedoindice(cli.nome,filepos(arq)-1); { --- altera --- } write('Novo Nome: '); readln(cli.nome); write('Novo CPF: '); readln(cli.cpf); write('Novo Salario: '); readln(cli.salario); insereNoIndice(cli.nome,filepos(arq)-1); seek(arq,filepos(arq)-1); { posiciona o ponteiro no registro a ser alterado } write(arq,cli); end else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end; end;

{--------------------------- exclui item ----------------------------- } procedure exclui_item; var cod: integer; begin write('Codigo a excluir: '); readln(cod); seek(arq,0); cli.codigo:=0; while (cli.codigo <> cod) and (not eof(arq)) do begin read(arq,cli); end; if cli.codigo = cod then begin removedoindice(cli.nome,filepos(arq)-1); seek(arq,filepos(arq)-1); cli.codigo:= -1; cli.nome:=''; cli.cpf:='';

93

cli.salario:=0; write(arq,cli); end else begin writeln('Codigo nao encontrado'); writeln(' pressione qualquer tecla p/continuar . . .'); readkey; end; end;

{ -------------------- PROGRAMA PRINCIPAL --------------------------- } begin abre_arquivo; repeat clrscr; writeln(' MENU PRINCIPAL '); writeln(' 1 - CADASTRAR CLIENTES '); writeln(' 2 - LISTAR TODO CADASTRO '); writeln(' 3 - LISTAR UM CADASTRO '); writeln(' 4 - ALTERAR CADASTRO '); writeln(' 5 - EXCLUIR CLIENTE '); writeln(' 6 - LISTAGEM EM ORDEM ALFABETICA'); writeln(' 9 - FIM '); writeln(' Escolha uma opcao: '); readln(o); if o=1 then begin cadastrar_clientes; end; if o=2 then begin lista_tudo; end; if o=3 then begin lista_item end; if o=4 then begin altera_item; end; if o=5 then begin exclui_item; end; if o=6 then begin listagem_ordem_alfabetica; end; until o=9; clrscr; fecha_arquivo; end.

94

9.4 Lista Duplamente Encadeadas


As estruturas de listas encadeadas vista anteriormente caracteriza-se por formar um encadeamento simples entre os elementos: cada elemento armazena um ponteiro para o prximo elemento da lista. Desta forma, no temos como percorrer de forma eficiente os elementos em ordem inversa, ou seja, do final para o incio da lista. O encadeamento simples tambm dificulta a retirada de um elemento da lista. Mesmo se tivermos o ponteiro do elemento que desejamos retirar, temos que percorrer a lista, elemento por elemento, para encontrarmos o elemento anterior, pois, dado um determinado elemento, no temos como acessar diretamente seu elemento anterior. Para solucionar esses problemas, podemos formar o que chamamos de listas duplamente encadeadas. Nelas, cada elemento tem um ponteiro para o prximo elemento e um ponteiro para o elemento anterior. Desta forma, dado um elemento, podemos acessar ambos os elementos adjacentes: o prximo e o anterior. Se tivermos um ponteiro para o ltimo elemento da lista, podemos percorrer a lista em ordem inversa, bastando acessar continuamente o elemento anterior, at alcanar o primeiro elemento da lista, que no tem elemento anterior (o ponteiro do elemento anterior vale nulo (nil). A figura a seguir ilustra a estruturao de uma lista duplamente encadeada:

A estrutura para controlar uma lista encadeada a seguinte:


type Elem = string; // determina o tipo de elementos que ter na lista

nodoLista = record info: Elem; prox: Pointer; ant: Pointer; end; Lista = record pri: ^nodoLista; ult: ^nodoLista; end;

Com esta estrutura, podemos utilizar uma lista duplamente encadeada de forma bastante flexvel, bastando criar variaes das rotinas de insero, listagem e remoo. As possveis variaes so: Inserir na Lista em ordem Inserir no Inicio da Lista Inserir no Final da Lista Remover da Lista Remover do Inicio da Lista Remover do Final da Lista Pesquisar Na Lista Mostrar Lista em ordem Mostrar Lista em ordem Invertida Limpar a Lista

Com esta estrutra de Lista Duplamente Encadeada com Alocao Dinmica e com todas essas operaes disponveis, podemos fazer esta lista se comportar como diferentes estrutruturas de acordo com as operaes que sero utilizadas. 95

Para esta lista se comportar como Lista Encadeada Sem Ordenao, basta usar somente as operaes: Inserir no Final da Lista Remover da Lista Pesquisar Na Lista Mostrar Lista em ordem Limpar a Lista

Para esta lista se comportar como Lista Encadeada Com Ordenao, basta usar somente as operaes: Inserir na Lista em ordem Remover da Lista Pesquisar Na Lista Mostrar Lista em ordem Mostrar Lista em ordem Invertida Limpar a Lista

Para esta lista se comportar como uma Fila, basta usar somente as operaes: Inserir no Final da Lista Remover do Inicio da Lista Mostrar Lista em ordem

Para esta lista se comportar como uma Pilha, basta usar somente as operaes: Inserir no Final da Lista Remover do Final da Lista Pesquisar Na Lista Mostrar Lista em ordem Invertida

O exemplo a seguir implementa uma lista duplamente encadeada com todas as rotinas descritas acima implementadas numa Unit separada, para facilitar sua utilizao em qualquer programa. Para criar uma unit, basta que o nome do arquivo da mesma seja igual ao que est declarado na primeira linha e que o mesmo esteja na mesma pasta do arquivo principal.
ListaCompleta.pas
unit ListaCompleta; interface type Elem = string;

// determina o tipo de elementos que ter na lista ************

//***************** ESTRUTURA DA LISTA **************************************** nodoLista = record info: Elem; prox: Pointer; ant: Pointer; end; Lista = record pri: ^nodoLista; ult: ^nodoLista; end; //***************** FIM DA ESTRUTURA DA LISTA ********************************* procedure inicializaLista(var L: Lista); procedure limparLista(var L: Lista); procedure insereNaLista(var L: Lista; x: Elem); procedure insereNoInicioDaLista(var L: Lista; x: Elem); procedure insereNoFinalDaLista(var L: Lista; x: Elem); procedure mostraLista(var L: Lista); procedure mostraListaInvertida(var L: Lista); function pesquisaNaLista(var L: Lista; x: Elem): boolean;

96

function removeDoInicioDaLista(var L: Lista): Elem; function removeDoFinalDaLista(var L: Lista): Elem; function removeDaLista(var L: Lista; x: Elem): boolean; implementation uses crt; procedure inicializaLista(var L: Lista); begin L.pri:=NIL; L.ult:=NIL; end; procedure limparLista(var L: Lista); var aux, aux2: ^nodoLista; begin aux:=L.pri; L.pri:=NIL; while (aux<>nil) do begin aux2:=aux; aux:=aux^.prox; freemem(aux2,sizeof(nodoLista)); end; inicializaLista(L); end; procedure insereNaLista(var L: Lista; x: Elem); var inseri: integer; aux, ult: ^nodoLista; begin GetMem(aux,sizeof(nodoLista)); if (aux=NIL) then begin writeln('Nao foi possivel alocar memoria'); exit; end; if (L.pri=NIL) then begin aux^.info:=x; aux^.prox:=NIL; aux^.ant:=NIL; L.pri:=aux; L.ult:=aux; end else begin if (L.pri^.info > x) then begin aux^.info:=x; aux^.prox:=L.pri; aux^.ant:=nil; L.pri^.ant:=aux; L.pri:=aux; end else if (L.pri^.prox = NIL) then begin aux^.info:=x; aux^.prox:=NIL; aux^.ant:=L.pri; L.pri^.prox:=aux; L.ult:=aux; end else if (L.ult^.info < x) then begin aux^.info:=x; aux^.prox:=NIL; aux^.ant:=L.ult; L.ult^.prox:=aux; L.ult:=aux; end else begin inseri:=0; ult:=L.pri; while (inseri = 0) and (ult^.prox<> NIL) do begin ult:=ult^.prox; if (ult^.info > x) then

97

begin aux^.info:=x; aux^.prox:=ult; aux^.ant:= ult^.ant; nodoLista(aux^.ant^).prox:=aux; ult^.ant:=aux; inseri:=1; end; end; if (inseri=0) then begin aux^.info:=x; aux^.prox := NIL; aux^.ant := ult; ult^.prox := aux; L.ult:=aux; end; end; end; end; procedure insereNoInicioDaLista(var L: Lista; x: Elem); var aux : ^nodoLista; begin GetMem(aux,sizeof(nodoLista)); if (aux=NIL) then begin writeln('Nao foi possivel alocar memoria'); exit; end; aux^.info:=x; aux^.prox:=L.pri; aux^.ant:=nil; if L.pri <> nil then L.pri^.ant:=aux; L.pri:=aux; if L.ult=nil then L.ult:=aux; end; procedure insereNoFinalDaLista(var L: Lista; x: Elem); var aux : ^nodoLista; begin GetMem(aux,sizeof(nodoLista)); if (aux=NIL) then begin writeln('Nao foi possivel alocar memoria'); exit; end; aux^.info:=x; aux^.prox:=NIL; aux^.ant := L.ult; if L.pri=NIL then L.pri:=aux else L.ult^.prox:=aux; L.ult:=aux; end; procedure mostraLista(var L: Lista); var aux: ^nodoLista; begin if L.pri=NIL then begin writeln('Lista Vazia.'); end else begin aux:= L.pri; while (aux<>nil) do begin writeln(aux^.info); aux:=aux^.prox; end; end; end; procedure mostraListaInvertida(var L: Lista); var aux: ^nodoLista; begin if L.ult=NIL then begin writeln('Lista Vazia.');

98

end else begin aux:= L.ult; while (aux<>nil) do begin writeln(aux^.info); aux:=aux^.ant; end; end; end; function pesquisaNaLista(var L: Lista; x: Elem): boolean; var aux: ^nodoLista; cont: integer; begin cont:=0; pesquisaNaLista:=false; aux:= L.pri; while (aux<>nil) do begin cont:=cont+1; if (aux^.info=x) then begin writeln('Encontrou na Posio ',cont); pesquisaNaLista:=true; break; end; aux:=aux^.prox; end; end; function removeDoInicioDaLista(var L: Lista): Elem; var aux: ^nodoLista; begin removeDoInicioDaLista:=''; if L.pri=nil then begin writeln('Lista vazia'); end else begin removeDoInicioDaLista:=L.pri^.info; aux := L.pri; if L.pri=L.ult then L.ult:=nil; L.pri := L.pri^.prox; L.pri^.ant:=nil; freemem(aux,sizeof(nodoLista)); end; end; function removeDoFinalDaLista(var L: Lista): Elem; var aux: ^nodoLista; begin removeDoFinalDaLista:=''; if L.ult=nil then begin writeln('Lista vazia'); end else begin removeDoFinalDaLista:=L.ult^.info; aux := L.ult; if L.pri=L.ult then L.pri:=nil; L.ult := L.ult^.ant; L.ult^.prox:=nil; freemem(aux,sizeof(nodoLista)); end; end; function removeDaLista(var L: Lista; x: Elem): boolean; var aux, aux2: ^nodoLista; begin removeDaLista:=false; if L.pri^.info = x then begin aux2 := L.pri;

99

if L.pri=L.ult then L.ult:=nil; L.pri := L.pri^.prox; if L.pri<> nil then L.pri^.ant:=nil; freemem(aux2,sizeof(nodoLista)); removeDaLista:=true; end else begin aux:= L.pri; aux2:=L.pri; while (aux<>nil) do begin if (aux^.info=x) then begin aux2^.prox:=aux^.prox; if L.ult=aux then L.ult:=aux2; if aux^.prox <> nil then nodoLista(aux^.prox^).ant:=aux2; freemem(aux,sizeof(nodoLista)); aux:=nil; removeDaLista:=true; break; end; aux2:=aux; aux:=aux^.prox; end; end; end; end.

Para usar esta estrutura de Lista Duplamente Encadeada com Alocao Dinmica e com todas essas operaes disponveis, podemos fazer um programa principal, acrescentando na clusula uses a ListaCompleta e declarar e inicializar a(s) lista(s) que ser usada, de acordo com as operaes necessrias. O exemplo a seguir apresenta um programa que utiliza a listaCompleta:
program listaDECompleta; uses crt, ListaCompleta; var x: Elem;

Compras: Lista;
op: integer; begin

inicializaLista(compras);
repeat writeln('0 - Limpar Lista'); writeln('1 - Inserir na Lista (em ordem)'); writeln('2 - Inserir no inicio da Lista'); writeln('3 - Inserir no final da Lista'); writeln('4 - Remover Elemento da Lista'); writeln('5 - Remover O Primeiro da Lista'); writeln('6 - Remover O Ultimo da Lista'); writeln('7 - Mostrar Lista em ordem '); writeln('8 - Mostrar Lista em ordem inversa'); writeln('9 - Pesquisar na Lista'); writeln('99 - Sair'); readln(op); if op=0 then begin

limparLista(compras);
end; if op=1 then begin write('Elemento a inserir '); readln(x);

insereNaLista(Compras,x);
end; if op=2 then begin write('Elemento a inserir '); readln(x);

insereNoInicioDaLista(Compras,x)
end; if op=3 then

100

begin write('Elemento a inserir '); readln(x);

insereNoFinalDaLista(Compras,x);
end; if op=4 then begin write('Elemento a Retirar: '); readln(x); if removeDaLista(compras,x) then writeln(x, ' foi removido da lista.') else writeln(x, ' NAO estava na lista.') end; if op=5 then begin

x:=removeDoInicioDaLista(compras);
if x <>'' then writeln(x, ' foi removido da lista.') end; if op=6 then begin

x:=removeDoFinalDaLista(compras);
if x <>'' then writeln(x, ' foi removido da lista.') end; if op=7 then begin

mostraLista(compras);
end; if op=8 then begin

mostraListaInvertida(compras);
end; if op=9 then begin write('Elemento a Pesquisar: '); readln(x); if pesquisaNaLista(compras, x) then writeln(x, ' esta na lista.') else writeln(x, ' NAO esta na lista.') end; if op=0 then begin

limparLista(compras);
end; until op=99; end.

Atividades
1. Testar o programa acima, implementando o programa e a Unit ListaCompleta num arquivo e salvando os dois na mesma pasta (os arquivos esto juntos com esse material no arquivo .zip). Fazer vrias as seguintes operaes e escrever como fica a lista ao final das mesmas:
ListaCompleta.Pas

Operao Insere (compras, carne); Insere (compras, carvo); Insere (compras, cerveja); Insere (compras, arroz); Insere (compras, alface); Insere (compras, gelo); Insere (compras, maionese); Insere (compras, abacaxi); Insere (compras, cebola); Insere (compras, vinagre); Remove (compras, abacaxi);

Situao da Lista Compras: [carne] Compras: [carne, carvo] Compras: [carne, carvo, cerveja] Compras: [arroz, carne, carvo, cerveja] Compras: [alface, arroz, carne, carvo, cerveja] Compras: [alface, arroz, carne, carvo, cerveja, gelo] Compras: [alface, arroz, carne, carvo, cerveja, gelo, maionese] Compras: [abacaxi, alface, arroz, carne, carvo, cerveja, gelo, maionese] Compras: [abacaxi, alface, arroz, carne, carvo, cebola, cerveja, gelo, maionese] Compras: [abacaxi, alface, arroz, carne, carvo, cebola, cerveja, gelo, maionese, vinagre] Compras: [alface, arroz, carne, carvo, cebola, cerveja, gelo, maionese, vinagre] 101

Remove (compras, gelo); Insere (compras, sal); InsereNoFinalDaLista (compras, vodka); InsereNoFinalDaLista (compras, acar); InsereNoFinalDaLista (compras, limo); InsereNoInicioDaLista (compras, po); InsereNoInicioDaLista (compras, po); InsereNoInicioDaLista (compras, po); Remove (compras, gelo); Remove (compras, acar);

Compras: [alface, arroz, carne, carvo, cebola, cerveja, maionese, vinagre] Compras: [alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre] Compras: [alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka] Compras: [alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka, acar] Compras: [alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka, acar, limo] Compras: [alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka, acar, limo, po] Compras: [po, alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka, acar, limo, po] Compras: [po, po, alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka, acar, limo, po] Compras: [po, po, alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka, acar, limo, po] Compras: [po, po, alface, arroz, carne, carvo, cebola, cerveja, maionese, sal, vinagre, vodka, limo, po]

Para manipular uma Lista Encadeada Ordenada, utilize somente as operaes: Inserir na Lista em ordem Remover da Lista Pesquisar Na Lista Mostrar Lista em ordem Mostrar Lista em ordem Invertida Limpar a Lista

2. Fazem um programa que implementa uma Lista Encadeada Ordenada de chamada de alunos. O programa deve ter as opes: 1. 2. 3. 4. Inserir Aluno na Lista (insereNaLista) Remover Aluno da Lista (removeDaLista) Mostrar a Lista Sair

3. Com a popularizao dos smartphones, podemos us-los nas atividades rotineiras, como na compra no Supermercado. Faa um programa que manipule uma lista de compras. Como nem todos os produtos que queremos encontramos no supermercado, vamos implementar tambm uma segunda lista, que a de produtos que no encontramos no mercado. Apenas para controle, tambm vamos armazenar numa nova lista os produtos j comprados. Faa, portanto um programa que implemente 3 Listas Encadeadas Ordenadas, com um menu com as seguintes operaes: Adicionar item na lista de compras (insereNaLista) Comprar item: remove da lista de compras e insere na lista de produtos comprados (removeDaLista e insereNaLista) Transferir item da lista de compras para a lista de produtos no encontrados (removeDaLista e insereNaLista) Mostrar lista de compras (que ainda falta comprar) Mostrar lista de produtos j comprados Mostras lista de produtos no encontrados Sair 102

4. A empresa Enxuga Gelo SA resolveu fazer uma doao de briquedos para os filhos dos seus funcionrios durante as comemoraes do aniversrio da empresa. Para isso, ela resolveu organizar uma fila de crianas, que sero chamadas para receber seu brinquedo. Enquanto as crianas ainda esto na fila, um responsvel vai verificar se a criana realmente filha de um funcionrio e se no for, ser removida da lista. Fazer um programa que implemente uma lista encadeada para organizar a fila, usando somente as operaes: Inserir no Final da Lista Remover da Lista Remover do Inicio da Lista Mostrar Lista em ordem Limpar a Lista

O programa deve ter um menu com as seguintes operaes: 1 Inserir criana na lista: inserir no final da lista encadeada, para o programa se comportar como se fosse uma fila 2 chamar criana para receber o brinquedo: remover do incio da lista, para o programa se comportar como se fosse uma fila e escrever o nome da criana na tela 3 excluir uma criana da lista: ler o nome da criana e remover da lista encadeada 4 excluir todas as crianas da lista: limpar a lista 9 - Sair

103

10. CLASSIFICAO (ORDENAO) DE DADOS


Classificar o processo de ordenar os elementos pertencente a uma estrutura de dados em memria (vetor) ou em disco (registros de uma tabela de dados) em ordem ascendente ou descendentes. Os fatores que influem na eficcia de um algoritmo de classificao so os seguintes: o nmero de registros a serem classificados se todos os registros cabero ou no na memria interna disponvel o grau de classificao j existente forma como o algoritmo ir ordenar os dados

Existem diversos algoritmos de classificao (ordenao) de dados, dos quais se detacam o Bubble Sort, o Selection Sort, o Insertion Sort, o Heap Sort, o Merge Sort, o Quick Sort e o Shell Sort. Estes algoritmos apresentam diferentes desempenhos e necessidades de recursos, principalmente memria e CPU.

10.1 Bubble Sort


Por ser simples e de entendimento e implementao fceis, o Bubblesort (bolha) est entre os mais conhecidos e difundidos mtodos de ordenao de vetores, porm o mesmo no um algoritmo eficiente, e estudado apenas para fins de desenvolvimento de raciocnio lgico. O princpio do Bubblesort a troca de valores entre posies consecutivas, fazendo com que os valores mais altos ( ou mais baixos ) "borbulhem" para o final do vetor, e por isso ele chadado de Bubblesort. Na figura a seguir, vamos ordenar o vetor em ordem crescente de valores. Consideremos inicialmente um arranjo qualquer desordenado. O primeiro passo se fazer a comparao entre os dois elementos das primeiras posies:

Assim verificamos que neste caso os dois primeiros elementos esto desordenados entre si, logo devemos troc-los de posio. E assim continuamos com as comparaes dos elementos subsequentes:

Aqui, mais uma vez, verificamos que os elementos esto desordenados entre si. Devemos fazer a troca e continuar nossas comparaes at o final do vetor:

Pode-se dizer que o nmero 5 "borbulhou" para a sua posio correta, l no final do vetor. O prximo passo agora ser repetir o processo de comparaes e trocas desde o incio do vetor. S que dessa vez o processo no precisar comparar o penltimo com o ltimo elemento, pois o ltimo nmero, o 5, est em sua posio correta no vetor.

program ExemploBubbleSort; uses crt; var v: array[1..10] of integer; i, j, aux: integer; begin for i:= 1 to 10 do begin

104

readln(v[i]); end; for i:= 1 to 10 do begin write(v[i]:8); end; for i:=1 to 9 do begin for j:= 1 to 10-i do begin if v[j] > v[j+1] then // Caso o elemento de uma posio menor begin aux := v[j]; // for maior que um elemento de uma posio v[j] := v[j+1]; // maior, faz a troca. v[j+1] := aux; end; end; end; for i:= 1 to 10 do begin write(v[i]:8); end; readkey; end.

10.2 Mergesort
Mergesort (ordenao por intercalao) divide o vetor de entrada em dois subvetores com metade do tamanho do vetor original (em caso de tamanho mpar, um dos subvetores ter um elemento a mais que o outro). Cada um dos subvetores ordenado recursivamente. Os dois subvetores so intercalados em um vetor temporrio. O Mergesort garante que os dois subproblemas tem tamanho n/2, mas requer alocao de memria para o vetor temporrio. O algoritmo do Mergesort :

A idia principal deste algoritmo combinar duas listas j ordenadas. O algoritmo quebra um array original em dois outros de tamanhos menor recursivamente at obter vetores de tamanho 1, depois retorna da recurso combinando os resultados. Cada um dos dois segmentos possui um ponteiro (x e y) que incrementado ao se selecionar o valor. 105

Um dos principais problemas com o MergeSort que ele faz uso de um array auxiliar. A figura abaixo ilustra a ao do MergeSort.

O programa a seguir apresenta uma implementao do merge sort em Pascal:


program ExemploOrdena; uses crt; type INTARRAY = array[1..100000] of integer; var v: intarray; i: integer; procedure Merge (var A: INTARRAY; p, q, r: integer); var i, j, k: integer; var B: INTARRAY; begin { Merge } i := p; j := q + 1; k := p; while ((i <= q) and (j <= r)) do begin if (A[i] < A[j]) then begin B[k] := A[i]; i := i + 1; end else begin B[k] := A[j]; j := j + 1; end; k := k + 1; end; while (i <= q) do begin B[k] := A[i]; k := k + 1; i := i + 1; end; while (j <= r) do

106

begin B[k] := A[j]; k := k + 1; j := j + 1; end; for k := p to r do A[k] := B[k]; end; procedure MergeSort (var A: INTARRAY; p, r: integer); var q: integer; begin { MergeSort } if (p < r) then begin q := (p + r) div 2; MergeSort (A, p, q); MergeSort (A, q + 1, r); Merge (A, p, q, r); end; end;

begin for i:= 1 to 20 do begin readln(v[i]); end; for i:= 1 to 20 do begin write(v[i]:8); end; mergesort(v, 1, 20); for i:= 1 to 20 do begin write(v[i]:8); end; readkey; end.

O programa a seguir apresenta a implementao do MergeSort em C:


#include <stdio.h> #include <stdlib.h> #include <time.h> int vet[10000000]; time_t hi, hf; void mergesort(int *v,int inicio,int fim) { int i,j,k,m,*t; if (inicio==fim) return; // ordenacao recursiva das duas metades m = (inicio+fim)/2; mergesort(v,inicio,m); mergesort(v,m+1,fim); // intercalacao no vetor temporario t i = inicio; j = m+1; k = 0; t = (int *) malloc(sizeof(int) * (fim-inicio+1)); while(i<m+1 || j<fim+1) { if (i==m+1) { // i passou do final da primeira metade, pegar v[j] t[k] = v[j]; j++; k++; } else if (j==fim+1) { // j passou do final da segunda metade, pegar v[i] t[k] = v[i];

107

i++; k++; } else if (v[i] < v[j]) { // v[i]<v[j], pegar v[i] t[k] = v[i]; i++; k++; } else { // v[j]<=v[i], pegar v[j] t[k] = v[j]; j++; k++; } } // copia vetor intercalado para o vetor original for(i=inicio;i<=fim;i++) v[i] = t[i-inicio]; free(t); } int main() { srand ( time(NULL) ); for(int i=0;i<10000000;i++) { vet[i]=rand()%100000; //printf("%d ", vet[i]); } hi = time(NULL); puts("ordenando..."); mergesort(vet, 0, 9999999); hf = time(NULL); for(int i=0;i<10000000;i++) { //printf("%d ", vet[i]); } printf(" demorou %ld ",hf-hi); system("pause"); }

10.3 Quicksort
O algoritmo Quicksort no requer armazenamento temporrio, mas seu procedimento de partio pode gerar subvetores de tamanhos diferentes. Quicksort escolhe um elemento piv, e particiona o vetor de tal forma que todos os elementos menores que o piv fiquem esquerda, e todos os elementos maiores ou iguais ao pivot fiquem direita. As duas parties so ordenadas recursivamente, e o resultado o vetor ordenado. No pior caso, quicksort leva tempo n2. No caso mdio, n log(n). O Quicksort foi inventado por C.A.R. Hoare em 1960, quando visitou a Universidade de Moscovo como estudante. Naquela poca, Hoare trabalhou em um projeto de traduo de um dicionrio de ingls para russo, ordenando as palavras, e seu desempenho o melhor na maioria das vezes. O quicksort pode ser expresso pelos dois algoritmos a seguir:

108

O primeiro elemento da lista a ser classificada escolhido como o piv. Depois da primeira fase da classificao, o piv ocupa a posio que ocupar quando a lista estiver completamente classificada. Os registros com valores de chaves menores do que o valor de chave do registro piv o precedem na lista e os registros com valores de chaves maiores do que o valor de chave do registro piv o sucedem na lista. Cada registro comparado com o registro piv e suas posies so permutadas se o valor de chave do registro for maior e o registro preceder o registro piv, ou se o valor de chave do registro for menor e o registro suceder o registro piv. A posio do registro piv no final de uma fase divide a lista original em duas sublistas (parties), cada uma delas precisando ser ordenada. Inicialmente, pega-se um vetor inicial e particiona-se da seguinte forma:

Suponhamos o vetor abaixo:

escolhemos a chave 9 como particionadora e a guardamos em uma varivel cp. Com isso, a posio ocupada por ela se torna disponvel para ser ocupada por outra chave. Essa situao indicada:

a partir da, consideremos 2 ponteiros (i: incio e f: fim)

109

Observe ento que embora os segmentos S1 e S3 no estejam ainda ordenados, a chave particionadora se encontra na sua posio definitivamente correta. A sequncia a seguir exibe apenas o final de cada processo de particionamento. As chaves particionadas so mostradas em tipo negrito, enquanto os segmentos de apenas um elemento que se formarem so mostrados em tipo itlico.

O programa a seguir apresenta uma implementao do QuickSort em Pascal:


program ExemploOrdena; uses crt; type INTARRAY = array[1..100000] of integer; var v: intarray; i: integer; function partition(var v: INTARRAY; inicio, fim: integer): integer; var x,i,j,t: integer; begin x := v[inicio]; i := inicio - 1; j := fim + 1; while true do begin

110

repeat j:=j-1; until(v[j]<=x); repeat i:=i+1; until (v[i]>=x); if (i<j) then begin t := v[i]; v[i] := v[j]; v[j] := t; end else begin partition:=j; break; end; end; end; procedure quicksort(var v: INTARRAY; inicio, fim: integer); var q: integer; begin if (inicio < fim) then begin q := partition(v,inicio,fim); quicksort(v,inicio,q); quicksort(v,q+1,fim); end; end; begin for i:= 1 to 20 do begin readln(v[i]); end; for i:= 1 to 20 do begin write(v[i]:8); end; quicksort(v, 1, 20); for i:= 1 to 20 do begin write(v[i]:8); end; readkey; end.

O programa a seguir apresenta a implementao do QuickSort em C:


#include <stdio.h> #include <stdlib.h> #include <time.h> int vet[10000000]; time_t hi, hf;

int partition(int *v,int inicio,int fim) { int x,i,j,t; x = v[inicio]; i = inicio - 1; j = fim + 1; for(;;) {

111

do { j--; } while(v[j]>x); do { i++; } while(v[i]<x); if (i<j) { t = v[i]; v[i] = v[j]; v[j] = t; } else return j; } } void quicksort(int *v,int inicio,int fim) { int q; if (inicio < fim) { q = partition(v,inicio,fim); quicksort(v,inicio,q); quicksort(v,q+1,fim); } } int main() { srand ( time(NULL) ); for(int i=0;i<10000000;i++) { vet[i]=rand()%100000; //printf("%d ", vet[i]); } hi = time(NULL); puts("ordenando..."); quicksort(vet, 0, 9999999); hf = time(NULL); for(int i=0;i<10000000;i++) { //printf("%d ", vet[i]); } printf(" demorou %ld ",hf-hi); system("pause"); }

O programa a seguir apresenta a implementao de um programa que usa ordenao em C++. Observe que a funo sort j implementada pelas bibliotecas do C++, bastando fazer a chamada, sem a necessidade de implement-la.
#include <iostream> #include <stdlib.h> #include <time.h> using namespace std; int vet[10000000]; time_t hi, hf; int main() { srand ( time(NULL) ); for(int i=0;i<10000000;i++) { vet[i]=rand()%100000; //cout << vet[i] << " "); } hi = time(NULL); cout << "ordenando..." << endl; sort(&vet[0],&vet[9999999]); hf = time(NULL); for(int i=0;i<10000000;i++) { //cout << vet[i] << " ";

112

} cout << " demorou system("pause"); }

" << hf-hi;

113

11. RVORES
Uma rvore uma coleo finita de n 0 nodos. Se n = 0 dizemos que a rvore nula, caso contrrio, uma rvore apresenta uma das seguintes caractersticas: existe um nodo especial denominado raiz; os demais so particionados em T1 , T2 , ... , Tk estruturas disjuntas de rvores; as estruturas T1 , T2 , ... , Tk denominam-se subrvores. A exigncia de que as estruturas T1 , T2 , ... , Tk sejam colees disjuntas, garante que um mesmo nodo no aparecer em mais de uma subrvore ao mesmo tempo, ou seja, nunca teremos subrvores interligadas.

O nmero de subrvores (filhos) de um nodo denomina-se grau. Um nodo que possui grau 0 denomina-se terminal ou folha. O grau de uma rvore (n-aridade), definido como sendo igual ao mximo dos graus de todos os seus nodos. As razes das subrvores de um nodo denominam-se filhos do nodo, que o pai delas. Os filhos do mesmo pai denominam-se irmos. Por definio dizemos que a raiz de uma rvore encontra-se no nvel 1. Estando um nodo no nvel n, seus filhos estaro no nvel n + 1. A altura de uma rvore definida como sendo o mximo dos nveis de todos os seus nodos. Uma rvore nula tem altura 0. O grau de uma rvore o mximo grau de seus ns. Exemplos: B pai de E e F. B, C, D so irmos E, F, G e H tm nvel 3 I, J, K, C, L, H so folhas. A tem grau 3, B tem grau 2, G tem grau 1. A rvore do exemplo tem altura 4. A rvore do exemplo tem grau 3.

11.1 Formas de representao de rvores


rvores podem ser representadas de diversas formas: 114

11.1.1 Forma natural (cima-baixo)

11.1.2 Forma Hierrquica


A

11.1.3 Forma linear

( A (B(E)) (C(F)(G)) (D (H(I)(J)(K))))

11.1.4 Forma Grfica

A D B
E

C
F G I

H
J K

11.1.5 Forma em ndices 1. - A 1.1 - B 1.1.1 - E 1.2 - C 1.2.1 - F 1.2.2 - G 1.3 - D 1.3.1 - H 115

1.3.1.1 - I 1.3.1.2 - J 1.3.1.3 - K

A B E C F G D H

_________________ _________________ _________________ _________________ _________________ _________________ _________________ _________________ I _________________ J _________________ K _________________

A B E C F G D H I J K

Para a implementao da rvore acima, que possui grau 3 (3 nodos filhos), teramos que usar uma lista encadeada ou um vetor para a informao e 3 vetores para armazenar os nodos filhos, conforme a ilustrao abaixo: ndice 1 2 3 4 5 6 7 8 9 10 11 Info A B C D E F G H I J K Nodo1 2 5 6 8 9 Nodo2 3 7 10 Nodo3 4 11 -

De acordo com as definies e a rvore apresentada acima, preencha a tabela abaixo: Nodo A B C D E F G H Grau 3 1 2 1 0 0 0 3 Nvel 1 2 2 2 3 3 3 3 Descendentes B, C, D, E, F, G, H, I, J, K E F, G H, I, J, K I, J, K Ancestral A A A B, A C, A C, A D, A Pai A A A B C C D Filhos B, C, D E F, G H I, J, K Irmos C, D B, D B, C G F 116

I J K

0 0 0

4 4 4

H, D, A H, D, A H, D, A

H H H

J, K I, K I, J

11.2 rvore Binria


Uma rvore binria uma rvore que pode ser nula, ou ento ter as seguintes caractersticas: existe um nodo especial denominado raiz; os demais nodos so particionados em T1 , T2 estruturas disjuntas de rvores binrias; T1 denominada subrvore esquerda e T2 subrvore direita rvore Binria um caso especial de rvore em que nenhum nodo tem grau superior a 2, isto , nenhum nodo tem mais que dois filhos. Para transformar uma rvore qualquer em rvore binria, preciso ter em mente que em cada subrvore, o nodo a esquerda representa o filho e os nodos direita representam os irmos. Abaixo podemos ver a rvore apresentada anteriormente e a respectiva transformao em rvore binria. rvore Qualquer
A B B C D E F G E F G H I I J K J K H C D

rvore Binria
A

11.2.1 Implementao de uma rvore binria Para implementar uma rvore binria necessrio um vetor para armazenar a informao e 2 vetores para armazenar os elos, sendo que o elo da esquerda armazena o endereo (ndice) do nodo que representa o filho e o elo da direita armazena o endereo (ndice) do nodo que representa o irmo. A tabela abaixo representa a rvore binria acima: ndice 1 2 3 4 5 6 7 8 9 EloE 2 5 6 8 9 Info A B C D E F G H I EloD 3 4 7 10 117

10 11

J K

11 -

O Exemplo abaixo um programa que implementa uma rvore binria simples:


program arvore; uses crt; var cont,j,topo,x,xr:integer; eloe, elod:array [0..50] of integer; info:array [0..50] of string[50]; r,f,i:string[50]; pilha:array[1..50] of integer; begin clrscr; topo:=0; eloe[0]:=0; elod[0]:=0; info[0]:= ''; x:=1; cont:=1; write('Raiz: '); readln(r); info[x]:=r; xr:=x; topo:=topo+1; pilha[topo]:=x; repeat r:=info[pilha[topo]]; for j:=1 to 10 do begin if info[j]=r then xr:=j; end; topo:=topo-1; write('Irmao de ',r,': '); readln(i); if i<>'' then begin elod[xr]:=x+1; x:=x+1; info[x]:=i; topo:=topo+1; pilha[topo]:=x; end else begin elod[xr]:=0; end; write('Filho de ',r,': '); readln(f); if f<>'' then begin eloe[xr]:=x+1; x:=x+1; info[x]:=f; topo:=topo+1; pilha[topo]:=x; end else begin eloe[x]:=0; end; cont:=cont+1; until topo=0; for j:=1 to cont-1 do

118

begin writeln(j:2,' end; readkey; end.

',eloe[j]:2,' - ',info[j],' - ',elod[j]:2);

11.2.2 Caminhamento sobre rvore binria Caminhamento o ato de percorrer todos os nodos da rvore. Entende-se por percorrer o ato de consultar ou alterar informaes nelas contidas. Existem vrios mtodos de caminhamento em rvores, que permitem percorr-la de forma sistemtica e de tal modo que cada n seja acessado apenas uma vez. Existem 3 ordens de caminhamento mais utilizadas, que so determinadas em funo da ordem em que so visitados o nodo raiz, sua sub-rvore direita e a sub-rvore esquerda: Caminhamento Pr-Fixado (R E D) Visita a Raiz Visita a sub-rvore esquerda Visita a sub-rvore direita Caminhamento In-Fixado ou Central (E R D) Visita a sub-rvore esquerda Visita a raiz Visita a sub-rvore direita Caminhamento Ps-Fixado (E D R) Visita a sub-rvore esquerda Visita a sub-rvore direita Visita a raiz Exemplo 1:
B

A C A B

Pr (RED): A B C In (ERD): B A C Ps (EDR): B C A Pr (RED): A B In (ERD): B A Ps (EDR): B A Pr (RED): A B In (ERD): A B Ps (EDR): B A Pr (RED): A B D E H C F G In (ERD): D B H E A F C G Ps (EDR): D H E B F G C A
G

Exemplo 2:

Exemplo 3:

A B

Exemplo 4:
B D H E

A C F

119

11.3 Aplicaes que usam rvores e rvore binrias


As principais aplicaes que usam rvores so: Problemas de busca de dados armazenados na memria principal do computador: rvore binria de busca, rvores (quase) balanceadas como AVL, rubro-negra, etc. Problemas de busca de dados armazenados na memria secundrias principal do computador (disco rgico): B-rvores. Aplicaes em Inteligncia Artificial: rvores que representam o espao de solues, jogo de xadrez, resoluo de problemas, etc. No processamento de cadeias de caracteres: rvore de sufixos. Na gramtica formal: rvore de anlise sinttica. Em problemas onde a meta achar uma ordem que satisfaz certas restries (testar a propriedade de uns-consecutivos numa matriz, reconhecer grafos, intervalo, planaridade de grafo, etc.): rvorePQ.

11.4 rvore binria de busca


Uma rvore binria de busca serve para o armazenamento de dados na memria do computador e a sua subseqente recuperao. Em uma rvore binria de busca cada n contm um campo chamado key, podendo haver outras informaes, alm dos ponteiros left e right. O campo key especifica em geral uma chave que identifica de forma nica um determinado registro ou informao. Exemplos de chaves: nmero de identidade, nmero CPF, etc. Assim, suporemos que todos os valores de key so distintos. Dado um valor qualquer, deseja-se localizar o n da rvore, se houver, cujo key igual ao valor dado. Caracterstica da rvore binria de busca: Para todo n x da rvore binria de busca, as seguintes condies so verficadas. key(x) > key(y), para todo n y da subrvore esquerda. key(x) < key(y), para todo n y da subrvore direita.

A pesquisa binria realizada se a informao est armazenada de forma ordenada e em seqncia (em um array). Cada comparao na busca binria reduz o nmero de possveis candidatos por uma fator de 2. Sendo assim, o nmero mximo de comparaes da chave aproximadamente log2 n. Com busca binria obtemos a melhor eficincia possvel em array, mas, ficamos limitados representao seqencial (deslocamentos, previso de memria, etc). Podemos utilizar a rvore Binria de Pesquisa e obteremos o mesmo desempenho anterior (desde que a altura seja mnima). a caracterstica de altura mnima que garante que estamos tomando a chave do meio da poro pesquisada. Para garantir a performance tima temos que garantir que a rvore seja balanceada durante a construo e que o balanceamento seja mantido em inseres e eliminaes (rvore AVL). O exemplo a seguir apresenta uma implementao de uma rvore com busca binria, que coloca os caracteres de forma ordenada, para um caminhamento pr-fixado.
program testeArvore; uses crt; type arvore = ^ nodo; nodo = record esquerda, direita : arvore; info : char end;

120

var raiz : arvore; achou : boolean; chave : char; item : char; cont: integer; function subarvore( item : char ) : arvore; {cria a subarvore} var t : arvore; begin new( t ); t^.info := item; t^.esquerda := nil; t^.direita := nil; subarvore := t end; procedure infix( t : arvore); begin if t <> nil then begin infix( t^.esquerda ); write( t^.info ); infix( t^.direita ) end end; procedure prefix( t : arvore); begin if t <> nil then begin write( t^.info ); prefix( t^.esquerda ); prefix( t^.direita ) end end; procedure postfix( t : arvore); begin if t <> nil then begin postfix( t^.esquerda ); postfix( t^.direita ); write( t^.info ); end end; procedure insere( item : char; var t : arvore ); begin if t = nil then t := subarvore( item ) else if item <= t^.info then insere( item, t^.esquerda) else insere( item, t^.direita) end; procedure pesquisa( chave: char; var achou: boolean; t : arvore); begin cont:=cont+1; if (t <> nil) and not achou then begin if chave = t^.info then achou := true else if chave < t^.info then pesquisa(chave, achou, t^.esquerda) else pesquisa(chave, achou, t^.direita) end end;

begin raiz := nil; writeln('Digite varios caracteres ou uma frase: '); while not eoln do

121

begin read( item ); insere( item, raiz ) end; writeln('Frase na forma In-Fixada: (RED)'); infix( raiz ); writeln; writeln('Frase na forma Pre-Fixado: (ERD)'); prefix( raiz ); writeln; writeln('Frase na forma Pos-Fixado: (EDR)'); postfix( raiz ); writeln; readkey; writeln( 'Digite uma letra para pesquisar'); achou := false; chave:=readkey; pesquisa( chave, achou, raiz); if achou then writeln( chave, ' foi encontrada. Foram necessarios ', cont, ' acessos.') else writeln( chave, ' no foi encontrada.'); readkey; end.

11.4.1 rvore binria de busca tima H situaes em que sabemos com antecedncia quais as chaves que sero buscadas e, mais ainda, com que freqncia ou probabilidade cada chave ser buscada. Nesse caso, podemos construir uma rvore binria de busca tima que ser eficiente para a operao de busca. Inseres e remoes no so usualmente efetuadas na rvore assim construda pois elas podem modificar a sua estrutura. Caso inseres e remoes so permitidas, ento se deve periodicamente reconstruir a rvore tima. A melhor rvore binria de busca depende das probabilidades de acesso das chaves. Quando a distribuio dessas probabilidades no uniforme, a melhor rvore binria de busca no necessriamente uma rvore binria perfeitamente balanceada. Intuitivamente, queremos colocar as chaves mais buscadas mais prximas ao topo da rvore binria. Se uma rvore binria de busca tima, ento as suas subrvores so timas. Uma rvore tima nem sempre balanceada, mas os elementos mais acessados esto mais prximos no nodo raiz. 11.4.2 rvores binrias de busca balanceadas Numa rvore binria de busca com n chaves e de altura h, as operaes de busca, insero e remoo tm complexidade de tempo O(h). No pior caso, a altura de uma rvore binria de busca pode ser O(n). No caso mdio, vimos que a altura O(log n). rvores AVL, rvores 2-3 e rvores rubro-negras so alguns tipos de rvores binrias de busca ditas balanceadas com altura O(log n). Todas essas rvores so projetadas para busca de dados armazenados na memria principal (RAM). As rvores 2-3 so generalizadas para as chamadas B-rvores, que veremos mais tarde, para busca eficiente de dados armazenados em memria secundria (disco rgido). Veremos a rvore rubro-negra cuja altura no mximo igual a 2 log(n + 1). Uma rvore rubro-negra uma rvore binria de busca em que cada n constitudo dos seguintes campos: cor (1 bit): pode ser vermelho ou preto. key (e.g. inteiro): indica o valor de uma chave. left, right: ponteiros que apontam para a subrvore esquerda e direita, resp. pai: ponteiro que aponta para o n pai. O campo pai do n raiz aponta para nil. 122

O ponteiro pai facilita a operao da rvore rubro-negra, conforme ser visto a seguir.

Uma rvore rubro-negra uma rvore binria de busca, com algumas propriedades adicionais. Quando um n no possui um filho (esquerdo ou direito) ento vamos supor que ao invs de apontar para nil, ele aponta para um n fictcio, que ser uma folha da rvore. Assim, todos os ns internos contm chaves e todas as folhas so ns fictcios. As propriedades da rvore rubro-negra so: Todo n da rvore ou vermelho ou preto. A raiz preta. Toda folha (nil) preta. Se um n vermelho, ento ambos os filhos so pretos. Para todo n, todos os caminhos do n at as folhas descendentes contm o mesmo nmero de ns pretos.

11.4.3 B-Trees (B-rvores) Em computao, rvore B ou B-Tree uma estrutura de dado pertencente ao grupo das rvores, e muito utilizada em banco de dados e em sistemas de arquivos. rvores binrias de busca, balanceadas ou no, no so adequadas para o armazenamento e busca de dados em memria secundria (como disco rgido). O acesso a disco envolve um posicionamento da cabea do disco, alm da transferncia de dados propriamente ditos. O posicionamento depende do tempo de rotao do disco que da ordem de 8 mili-segundos. Um acesso a disco leva tipicamente 10 a 15 mili-segundos, o que considervel em comparao com o tempo de acesso memria primria (RAM), da ordem de 100 nano-segundos. No tempo para acessar uma vez o disco, podemos fazer cerca de 100.000 acessos memria. Mesmo em rvores balanceadas, de n chaves, O(log n) acessos a disco podem ser excessivos. Para uma rvore binria de busca balanceada de n = 1 milho de chaves armazenadas em disco, log n = 20 acessos a disco podem ser considerados custosos demais. Como um acesso a disco uma operao cara, ento ao invs de buscar um dado de cada vez, procura-se transferir, em cada acesso, uma quantidade maior de dados. Um n da B-rvore (chamado pgina) contm tipicamente centenas de chaves. B-rvore pode ser considerada uma generalizao de uma rvore binria de busca 123

balanceada. De fato, ela uma extenso da chamada rvore-2-3. B-rvore uma estrutura de dado muito bem sucedida, com diversas variantes usadas na implementao de bases de dados de uso comercial, como Oracle, Sybase, VSAM da IBM, dBASE, etc. Os criadores da B-rvore (R. Bayer e E. McCreight) no explicam o por qu do nome B-rvore. Conjectura-se que a letra B lembra Bayer, ou Balanceada ou ainda Boeing, a companhia onde trabalhavam os dois autores. Um n da B-rvore tambm conhecido pelo nome de pgina. Veremos que cada pgina pode conter uma grande quantidade de chaves. A chave exerce um papel importante na busca, pois ela identifica unicamente um elemento de informao. Naturalmente, alm da chave, podemos ter outras informaes associadas. Por exemplo, a chave pode ser o nmero CPF e a cada CPF podemos juntar ainda dados pessoais e rendimentos obtidos num ano fiscal. Nos exemplos representaremos apenas a chave, mas subentende-se que pode haver demais informaes associadas. Essas informaes podem estar armazenadas junto com a chave, ou em outros locais, sendo acessveis por ponteiros armazenados junto com a chave. Um caso particular de B-rvore a chamada rvore-2-3. Uma rvore-2-3 uma B-rvore de ordem b = 1. Cada n da rvore-2-3 tem 1 ou 2 chaves. Cada n da rvore-2-3 tem 2 ou 3 filhos, da o nome. A rvore-2-3 uma rvore usada fazer busca de dados armazenados na memria principal.

Para armazenamento e busca em disco, uma B-rvore usa uma ordem b grande, tipicamente de alguma centenas de chaves. A busca de uma dada chave x numa B-rvore anloga busca na rvore binria de busca. A busca comea pela pgina raiz. usual manter a raiz sempre na memria, evitando um acesso ao disco. As principais vantagens de rvores-B so: 1. Melhor desempenho por ter um nmero menor de ns do que uma rvore binria, por exemplo. Menos ns, significa menos altura que resulta em menos acessos ao disco. 2. Por garantir poucos ponteiros entre os ns, h uma economia de espao. 3. Maior rapidez em buscas pela utilizao de chaves primrias. 4. Sua estrutura dinmica, ajustando automaticamente o balanceamento da rvore, a cada incluso/excluso. 5. Permite um tempo de acesso de dados menor, em uma busca aleatria, por causa de suas ramificaes. As principais desvantagens so: 1. N no folha com n chaves, visitado n vezes, portanto o processo de busca s vezes pode se tornar lento. 2. As rvores B+ sempre mantm uma cpia de todos os dados nas folhas, o que em caso de necessidade de imprimir toda ela, por exemplo, permite uma rpida busca linear, fazendo com que a rvore B, em comparao, tenha menor performance. Em comparaes com outras Estruturas de Dados este tipo de rvore : a) Insero: Mais veloz se comparado tabelas de hash, por sua capacidade de se ajustar e balancear a cada insero, isto significa menores colises, e consequentemente menos laos para encontrar uma posio livre. No caso de precisar expandir, a duplicao feita somente na folha, e nos ns correspondentes se necessrio, o que garante menor espao vazio alocado e menos ocupao em disco, ao contrrio do hash que duplica a tabela por inteiro. 124

b) Excluso: No caso de uma excluso, rvores B so capazes de evitar grandes fragmentaes, pois se ajustam e balanceiam tambm nesse momento. J em tabelas de hash, quando h excluso de uma chave, o espao alocado para esta fica vazio, porm ainda alocado. Maior fragmentao significa menor desempenho. c) Busca: rvores B so to boas e velozes quanto tabelas de hash numa busca por igualdade, porm so muito superiores em velocidade e desempenho em buscas do tipo range. A superioridade da rvore B, se basea na chave primria, onde a busca comea pela raiz e direcionada aos ns e filhos de acordo com a chave procurada, isto torna mais objetivo e direto o resultado final. Porm, se comparado a rvore B+, sua performance pode cair um pouco em uma busca linear. rvores do tipo B+, sempre tm uma cpia de todos os ns nas folhas, sendo assim, necessrio apenas percorre-las para se ter uma viso completa de todas as chaves da estrutura. 11.4.4 rvore Balanceadas ABP e AVL Sucessivas inseres e remoes de ns em uma rvore binria de pesquisa fazem com que existam diferenas entre os nveis das suas folhas, o que acarreta grandes diferenas de performance no acesso a seus ns. No pior caso, se os dados j esto ordenados, a rvore ser uma lista, perdendo toda a eficincia da busca. Nesses casos, diz-se que a rvore ficou degenerada. Uma rvore dita balanceada quando, para qualquer n, as suas sub-rvores esquerda e direita possuem a mesma altura. Isso equivale a dizer que todos os ponteiros nulos esto no mesmo nvel, ou seja, que a rvore est completa. Isso nem sempre possvel, por conta do nmero de ns da rvore. Um critrio mais simplificado seria considerar uma rvore balanceada se o nmero mximo de ns da sub-rvore esquerda diferencie no mximo 1 da sub-rvore direita. O Balanceamento Esttico (ABP) consiste em construir uma nova verso de uma rvore binria de pesquisa, reorganizando-a. Essa reorganizao possui duas etapas: 1. Percurso in-ordem sobre a rvore, para gerar um vetor ordenado com o contedo de todos os seus ns. 2. Criao de uma ABP a partir desse vetor, da seguinte forma: a) Identifica-se o n mdio do vetor, que passa a ser considerado a raiz da ABP que est sendo criada. b) Cada uma das metades do vetor tratada analogamente, de modo que cada n intermedirio ser a raiz de uma sub-rvore. O Balanceamento Dinmico (AVL) foi criado em 1962, por dois matemticos russos (Adelson-Velskii e Landis) que definiram uma nova estrutura para balanceamento de rvores ABP e deram o nome de AVL. Esta nova estrutura permite o balanceamento dinmico da rvore e com boa performance. O Fator de Balanceamento (FB) de um n a altura da subrvore direita do n menos a altura da subrvore esquerda do n. A operao bsica em uma rvore AVL geralmente envolve os mesmos algoritmos de uma rvore de busca binria desbalanceada. A rotao na rvore AVL ocorre devido ao seu desbalanceamento, uma rotao simples ocorre quando um n est desbalanceado e seu filho estiver no mesmo sentido da inclinao, formando uma linha reta. Uma rotao-dupla ocorre quando um n estiver desbalanceado e seu filho estiver inclinado no sentido inverso ao pai, formando um "joelho". Para realizar uma Rotao Esquerda em uma rvore binria, basta empurrar o nodo N para baixo e para a esquerda. O filho direita de N o substitui, e o filho esquerda do filho direita vem a ser o novo filho direita de N. Segue pseudocdigo: Seja Y o filho direita de X Torne X filho esquerda de Y Torne o filho esquerda de Y o filho direita de X. 125

Para realizar uma Rotao Direita numa rvore binria basta empurrar o nodo N para baixo e para a direita. O filho esquerda de N o substitui, e o filho direita do filho esquerda vem a ser o novo filho esquerda de N. Segue pseudocdigo: Seja Y o filho esquerda de X Torne X o filho direita de Y Torne o filho direita de Y o filho esquerda de X.

interessante observar que as rotaes duplas nada mais so que duas rotaes simples seguidas, independentes se direita ou esquerda.

126

A eficincia do uso de rvores binrias de pesquisa exige que estas sejam mantidas balanceadas. Isso implica no uso de rvores AVL, e dos algoritmos associados para insero e eliminao de registros. Para uma rvore binria completamente balanceada, o nmero mximo de comparaes para localizar uma chave em uma rvore com N chaves igual altura da rvore, dada por log2 (N+1). Para uma rvore AVL, esse nmero 1.44*log2 (N+2). Portanto, dadas 1.000.000 de chaves, em uma rvore completamente balanceada uma busca iria percorrer at 20 nveis da rvore. Para uma rvore AVL, a busca poderia percorrer at 28 nveis. Entretanto, se as chaves esto em memria secundria, qualquer procedimento que exija mais do que 5 ou 6 acessos para localizar uma chave altamente indesejvel: 20 ou 28 seeks so igualmente inaceitveis. Assim, rvores balanceadas so uma boa alternativa se considerarmos o problema da ordenao, pois no requerem a ordenao do ndice e sua reorganizao sempre que houver nova insero. Por outro lado, as solues vistas at agora no resolvem o problema no nmero excessivo de acessos a disco. A Soluo por rvores Binrias Paginadas (Paged Binary Trees) pode amenizar o problema, pois se a busca por uma posio especfica do disco muito lenta, por outro lado, uma vez encontrada a posio, pode-se ler uma grande quantidade registros seqencialmente a um custo relativamente pequeno. Esta combinao de busca (seek ) lenta e transferncia rpida sugere a noo de pgina: em um sistema "paginado", voc no incorre no custo de fazer um "seek" para recuperar apenas alguns bytes: ao invs disso, uma vez realizado um seek, que consome um tempo considervel, todos os registros em uma mesma "pgina" do arquivo so lidos. Esta pgina pode conter um nmero bastante grande de registros, e se por acaso o prximo registro a ser recuperado estiver na mesma pgina, voc economizou um acesso a disco. A diviso de uma rvore binria em pginas ilustrada na figura a seguir.

127

Nessa rvore de 9 pginas, quaisquer dos 63 registros podem ser acessados em, no mximo, 2 acessos. Se a rvore estendida com um nvel de paginao adicional, adicionamos 64 novas pginas, e poderemos encontrar qualquer um das 511 chaves armazenadas com apenas 3 seeks. Se cada pgina dessa rvore ocupar 8KB, permitindo a armazenagem de 511 pares chave-referncia; e cada pgina contiver uma rvore completa perfeitamente balanceada, ento a rvore toda pode armazenar um total de 134.217.727 chaves, sendo que qualquer delas pode ser acessada em, no mximo, 3 acessos ao disco. Veja outros acessos: rvore binria: log2 (134.217.727) = 27 acessos verso em pginas: log511+1 (134.217.727) = 3 acessos

O preo a pagar por esta implementao o maior tempo na transmisso de grandes quantidades de dados, e, mais srio, a necessidade de manuteno da organizao da rvore. Construir uma rvore paginada relativamente simples se temos todo o conjunto de chaves antes de iniciar a construo, pois sabemos que temos de comear com a chave do meio para garantir que o conjunto de chaves seja dividido de forma balanceada. Entretanto, a situao se complica se estamos recebendo as chaves em uma seqncia aleatria e construindo a rvore a medida em que as chaves chegam. Provavelmente a rvore ficar desbalanceada. E ento, o que fazer? No d para rotacionar pginas como rotacionamos chaves individuais! Bayer e McCreight, propuseram que as rvores fossem construdas de baixo para cima. Desta forma, as chaves raiz da rvore emergem naturalmente. Uma idia elegante e poderosa. 11.4.5 rvores B+ Uma das maiores deficincias da rvore-B a dificuldade de percorrer as chaves seqencialmente. Uma variao da estrutura bsica da rvore-B a rvore-B+. Nesta estrutura, todas as chaves so mantidas em folhas, e algumas chaves so repetidas em ns no-folha para definir caminhos para localizar registros individuais. As folhas so ligadas atravs de uma lista duplamente encadeada, de modo a oferecer um caminho seqencial para percorrer as chaves na rvore. Esta lista chamada de Conjunto de Seqncia (Sequence Set). importante esta separao lgica da rvore-B+ em Conjunto de ndices (Index Set) e Conjunto de Seqncia, pois podemos fazer a maioria das incluses e excluses no conjunto de seqncia sem alterar o ndice (rvore-B). Quando houver necessidade de incluso ou excluso do referido ndice, usamos os algoritmos j conhecidos da rvore-B. 128

A insero numa rvore-B+ ocorre praticamente da mesma forma que numa rvore-B, exceto pelo fato de que, quando um n dividido, a chave do meio retida no meio n esquerdo, alm de ser promovida a pai. Quando uma chave eliminada de uma folha, ela pode ser retida nas no-folhas porque ela ainda um separador vlido entre as chaves nos ns abaixo dela. A rvore-B+ mantm o baixo custo das operaes de pesquisa, incluso e excluso e adquire a vantagem de requerer no mximo um acesso para satisfazer a operao da prxima chave. Alm disso, para um processamento seqencial nenhum nodo precisar ser acessado mais de uma vez como acontece no caminhamento in-order que precisamos fazer numa rvore -B. Portanto, enquanto a eficincia de localizao do registro seguinte em um rvore-B O(log n), numa rvore-B+ aumentada para O(1). A rvore-B+ ideal para aplicaes que requerem tanto acesso seqencial quanto aleatrio. Por isso e pelas vantagens citadas anteriormente, a rvore -B+ tornou-se bastante popular nos SGBDs comerciais. 11.4.6 rvores B* Existem vrias formas de aprimorar a utilizao do armazenamento de uma rvore-B. Um mtodo retardar a diviso de um n no caso de encher. Em substituio, as chaves no n e um de seus irmos adjacentes, bem como a chave no pai que separa entre os dois ns, so redistribudas uniformemente. Quando um n e seu irmo estiverem completos, os dois ns sero divididos em trs (two-to-three splitting). Isso assegurar uma utilizao mnima do armazenamento de quase 67%, contra 50% de uma rvore-B tradicional. Na prtica, a utilizao do armazenamento ser ainda mais alta. Esse tipo de rvore chamado de rvore-B*. Essa tcnica pode ser ainda mais ampliada redistribuindo as chaves entre todos os irmos e o pai de um n completo. Infelizmente, este mtodo impe um preo porque exige espaos adicionais dispendiosos durante as inseres, enquanto a utilizao do espao adicional alcanado pela considerao de cada irmo extra torna-se cada vez menor.

129

12. GRAFOS
Um grafo uma estrutura de dados formada por zero ou mais ns (nodos) interligados de modo arbitrrio. Eles so conectados por uma linha. Um grafo G(V,A) definido pelo par de conjuntos V e A, onde: V - conjunto no vazio: os vrtices ou nodos do grafo; A - conjunto de pares ordenados a=(v,w), v e w V: as arestas do grafo.

Seja, por exemplo, o grafo G(V,A) dado por: V = { p | p uma pessoa } A = { (v,w) | < v amigo de w > }

Esta definio representa toda uma famlia de grafos. Um exemplo de elemento desta famlia (ver G1) dado por:

G1:
V = { Maria, Pedro, Joana, Luiz } A = { (Maria, Pedro), (Joana, Maria), (Pedro, Luiz), (Joana, Pedro)} Neste exemplo estamos considerando que a relao <v amigo de w> uma relao simtrica, ou seja, se <v amigo de w> ento <w amigo de v>. Como conseqncia, as arestas que ligam os vrtices no possuem qualquer orientao Existem vrios tipos de grafos, sendo que os mais usuais so: Grafos Ponderados: cada linha possui um determinado peso ou valor; Grafos Dirigidos: cada linha possui uma direo, ou seja, a linha conecta o nodo A ao nodo B e no vice-versa. As reas de aplicao de grafos incluem os sistemas especialistas: lgica, inteligncia artificial, sistemas operacionais: grafos de seqncia de processos, compiladores, dentre outros. Exemplos:
A

B C F G E D

Progr.

Identif.

Identif.

) ) ; Bloco

130

Sempre que tivermos um grupo de informaes e desejamos mostrar uma determinada ordem na execuo destas informaes, utilizamos um grafo. Exemplo: Se temos 6 nodos N e uma seqncia de execuo denotada nos pares A: N = { 1, 2, 3, 4, 5, 6 } A = { (1,2), (2,1), (2,3), (2,4), (3,3), (4,1), (4,3), (5,6) } A representao grfica do grafo :

3 5 6

Para percorrer um grafo, existem dois critrios, que podem ser: percurso em amplitude (da esquerda para a direita) ou percurso em profundidade (de cima para baixo). Para implementar um grafo, utiliza-se uma matriz de adjacncia onde na linha e na coluna temos cada um dos nodos. Se ocorrer um relacionamento do nodo representado pela linha com o nodo representado pela coluna, ento marca-se com o valor 1, seno marca-se com o valor 0. No grafo do ltimo exemplo, teramos a seguinte matriz de adjacncia: nodos 1 2 3 4 5 6 0 1 0 0 0 0 1 1 0 1 1 0 0 2 0 0 1 0 0 0 3 1 0 1 0 0 0 4 0 0 0 0 0 1 5 0 0 0 0 0 0 6 Uma outra forma de representao a utilizao de uma matriz de incidncia, onde a linha representa cada um dos nodos e a coluna cada um dos relacionamentos. Utiliza-se os valores 1 para relacionamentos entre dois nodos e 0 para quando no existe relacionamento no caso de grafos no dirigidos e os valores 1 para o origem e 2 para o destino do relacionamento em grafos dirigidos. No grafo do ltimo exemplo, teramos a seguinte matriz de incidncia: nodos 1 2 3 4 5 6 1 1 2 0 0 0 0 2 2 1 0 0 0 0 3 0 1 2 0 0 0 4 0 1 0 2 0 0 5 0 0 1 0 0 0 6 2 0 0 1 0 0 7 0 0 2 1 0 0 8 0 0 0 0 1 2

Os grafos so utilizados para resolver problemas de: a) Colorao de grafos: o Teorema das quatro cores 131

b) Conjuntos de Grafos : Conjunto independente, Clique c) Problemas de roteamento: Sete pontes de Knigsberg, rvore de extenso mnima, Problema do caminho mnimo, Problema da inspeo de rotas (tambm conhecido como o "Problema do carteiro chins"), Problema do caixeiro viajante d) Fluxos de rede: Teorema do mnimo corte-mximo fluxo, conjectura da reconstruo e) Problemas de Isomorfismo (casamento de grafos) Rotulao cannica, Isomorfismo de subgrafos e monomorfismos, Mximo subgrafo comum O exemplo a seguir apresenta o algoritmo de Dijkstra que encontra o caminho mais curto entre dois pontos, utilizando grafo ponderado e dirigido atravs de uma matriz de adjacncia.
program exdijkstra; uses crt; const INFINITY=32768; MEMBER=1; NONMEMBER=0; MAXNODES=50; type arc = record adj: integer; // se adj=1 ento ADJACENTE; se adj=0 ento NO ADJACENTE peso: integer; // peso da aresta end; var g: array[0..MAXNODES, 0..MAXNODES] of arc; origem, destino: integer; // matriz de adjacncias

// inicializa matriz de adjacncia que representa o grafo // retorna ponteiro para esta matriz (tipo GRAPH) procedure inicializa; var i, j: integer; begin for i:=0 to MAXNODES do begin for j:=0 to MAXNODES do begin g[i, j].adj := 0; g[i, j].peso := INFINITY; end; end; end; // cria uma aresta que "liga" (incide) em dois ns e atribui o respectivo peso; // recebe dois ns (node1 e node2) e o peso (wt) da aresta procedure define(node1, node2, wt: integer); begin g[node1, node2].adj := 1; g[node1, node2].peso := wt; end; // remove uma aresta do grafo; // recebe os dois ns (node1 e node2); procedure remove (node1, node2: integer); begin g[node1, node2].adj := 0; g[node1, node2].peso := INFINITY; end; procedure dijkstra (s, t: integer); var dist, perm, path: array[0..MAXNODES] of integer; current, i, k, dc, smalldist, newdist, caminho: integer; begin // Inicializa todos os ndices de 'perm' como 0 e de 'dist' como INFINITY */ for i:= 0 to MAXNODES do

132

begin perm[i] := NONMEMBER; dist[i] := INFINITY; end; // Inclui 's' em perm (perm[s]=1) e configura(armazena) sua distancia como sendo zero */ perm[s] := MEMBER; dist[s] := 0; //* define 's' como origem (fonte) da busca */ current := s; k := current; while (current <> t) do begin smalldist := INFINITY; dc := dist[current]; for i := 0 to MAXNODES do begin //se o elemento NO est em perm if (perm[i] = NONMEMBER) then begin //calcula distncia a partir do vrtice corrente ao vrtice adjacente i newdist := dc + g[current, i].peso; //se a distncia partindo do vrtice corrente for menor, atualiza o vetor //de distncias e de precedncia if (newdist < dist[i]) then begin dist[i] := newdist; path[i] := current; end; //determina o vrtice (entre todos os no pertencentes a perm) com menor distncia if (dist[i] < smalldist) then begin smalldist := dist[i]; k := i; end; end; end; //* fim for */ //* embora estamos assumindo grafos ponderados e conexos, este if garante que // em caso de no existncia de um caminho o programa no entre em loop infinito */ if (current = k) then begin writeln('CAMINHO NAO EXISTE'); exit; end; current := k; perm[current] := MEMBER; end; //* fim while */ //* impressao do resultado ****************/ writeln('RESULTADO: '); caminho:= t; write(t, ' <- '); while (caminho <> s) do begin write(path[caminho]); caminho := path[caminho]; if (caminho <> s) then write (' <- '); end; writeln(' custo: ', dist[t]);

133

//****************************************/ end; //* fim dijkstra */ begin inicializa; define (0, 1, 10); define (0, 2, 5); define (1, 2, 2); define (1, 3, 1); define (2, 1, 3); define (2, 3, 9); define (2, 4, 2); define (3, 4, 4); define (4, 0, 7); define (4, 3, 6); write('Origem [0-4]: '); readln(origem); write('Destino [0-4]: '); readln(destino); dijkstra (origem, destino); readkey; end.

134

13. HASHING
Nas estruturas de dados vistas at agora, a busca por uma informao armazenada era feita com base na comparao de chaves, ou seja, dada uma chave usava-se os mecanismos de acesso para encontrar a mesma chave armazenada. O Hashing um mtodo de busca por chaves que tem como maior vantagem o fato de que, na mdia dos casos, possvel encontrar a chave com apenas uma operao de leitura. A idia geral do mtodo gerar, a partir da chave procurada, o endereo da entrada de uma tabela onde se encontra a informao associada chave. Este mtodo de transformao de chaves conhecido na literatura com hashing (espalhamento) e a tabela chamada tabela de disperso. A construo da tabela tambm feita com base em hashing, ou seja, a partir da chave gerado um endereo de uma entrada que dever ser ocupada na tabela. Como pode acontecer de que o mesmo endereo seja gerado a partir de mais de uma chave, so necessrios mecanismos para tratar estas situaes chamadas de colises.

13.1 Funes de Transformao de Chaves


Seja uma tabela de disperso com M entradas. Uma funo de transformao deve gerar para cada elemento de um conjunto de chaves um valor entre 0 e M-1 correspondente a um endereo na tabela. Como a transformao da chave uma operao aritmtica, preciso, no caso da chave ser composta de caracteres, gerar um valor numrico equivalente atravs da soma dos valores na tabela ASCII dos caracteres que compem a chave. A seguir, K usado para gerar o endereo para a tabela atravs de uma funo de transformao h(K). esperado que h(K) seja uma funo que produza um baixo nmero de colises ao mesmo tempo em que tenha um bom grau de "espalhamento", ou seja que os endereos sejam uniformemente gerados. Alm disso, importante que a funo seja simples de calcular. Um exemplo de chave simples seria usar o prprio cdigo do cpf, sem os dgitos verificadores como chave para localizao. O problema de uma chave desse tipo que seriam necessrias 999.999.999 entradas numa tabela para somente algunas centenas serem efetivamente usadas, causando um desperdcio de memria e espao em disco. A chave deve, portanto, ser otimizada para evitar desperdcios.

13.2 TRATAMENTO DE COLISES


Como muito comum o caso em que um mesmo endereo seja gerado para mais de uma chave, pois em geral o nmero N de chaves possveis muito maior que o nmero de entradas disponveis M e tambm porque no se pode garantir que as funes hashing em geral possuem um bom potencial de espalhamento, necessrio haver um tratamento de colises. As tcnicas de tratamento de coliso so basicamente de dois tipos: a) Encadeamento: Neste tipo tratamento de colises, as entradas da tabela de disperso possuem um apontador para uma lista encadeada cujos elementos armazenam as chaves que geram o endereo da entrada (e eventualmente a informao associada). Tambm possvel implementar tratamento de hashing por encadeamento sem usar alocao dinmica de memria. Para isso, acrescenta-se a cada entrada da tabela um campo que indica a prxima entrada a ser pesquisada se a entrada no contiver a chave que deveria conter e que foi deslocada por causa de uma coliso. Em alguns casos estas entradas extras se encontram na rea separada da tabela chamada rea de overflow. b) Endereamento Aberto (ReHash): Para evitar o uso de recursos adicionais na construo da estrutura de dados, a estratgia de endereamento aberto simplesmente aplica outra funo de transformao de chave quando houver uma coliso causada pela funo original h(K). Assim, se h 135

uma coliso causada por h(k) usa-se uma outra funo rh(h(K)) que determina outra entrada onde a chave deve ser inserida. Se houver outra coliso usa-se rh(rh(h(K))) e assim por diante. Se no encontrada nenhuma posio vazia porque a tabela j est cheia e no podem ser includos novos elementos. A busca da chave segue a mesma estratgia. Contudo, h um problema gerado com o espalhamento linear: quando h um conjunto com muitas entradas contguas preenchidas, este apresenta mais chances de promover uma insero que outro com entradas mais dispersas. Este fenmeno em que duas chaves espalhadas em dois valores diferentes competem entre si em sucessivos reespalhamentos chamado de agrupamento primrio. Existem vrios mtodos que resolvem o problema do agrupamento primrio. Contudo, eles no eliminam um outro fenmeno, conhecido como agrupamento secundrio, no qual chaves diferentes que espalham o mesmo valor seguem o mesmo percurso de reespalhamento. Uma maneira de eliminar todo esse agrupamento o espalhamento duplo, que aparece como soluo aos problemas do agrupamento primrio e agrupamento, pois usa duas funes, permitindo que os resultados da funo hash se aproximem de permutaes aleatrias.

136

14. TABELAS
Uma tabela uma coleo de entradas, cada uma delas formada por campos. Usualmente, uma tabela armazena informaes sobre vrios objetos de um mesmo tipo. Cada objeto corresponde a uma entrada. No exemplo abaixo temos uma tabela contendo informaes sobre funcinrios de uma empresa, onde cada entrada representa um funcionrio: # 1 2 3 4 5 6 Nmero 10135 13751 11854 13287 12633 11989 Nome Jos Joo Pedro Marta Monica Jenoveva Dada Nascim. 25/07/60 11/03/70 22/09/65 15/08/76 22/12/75 15/09/52 Funo Analista de Sistemas Programador Supervisor Analista de redes Programador Secretria

Os campos das entradas so: nmero, nome, data de nascimento e funo. Na tabela comum que existam campos que sero usados como chave para o acesso. Uma chave no necessariamente deve ser nica, pondendo existir outras chaves. Na tabela acima, podemos classificar o campo nmero como sendo chave. Em uma tabela temos 3 entradas envolvidas: Tabela: Conjunto de informaes, todas do mesmo tipo; Entrada: Uma linha (ocorrncia) na tabela Chave: Campo da tabela que usado para pesquisa

14.1 Pesquisa em Tabelas


Existem diversos mtodos de pesquisa em tabelas, muitos deles se estendendo a arquivos. Os tipos mais usuais de pesquisa so: pesquisa seqencial, pesquisa binria e pesquisa por clculo de endereco. a) Pesquisa Seqencial: Este o mtodo mais simples de pesquisa, j que parte do princpio de que a tabela no est ordenada. Consiste numa varredura serial da tabela, durante a qual o argumento de pesquisa comparado com a chave de cada entrada at ser encontrada uma que seja igual, ou ser atingido o final da tabela, caso a chave procurada no esteja presente na tabela. O desempenho deste mtodos de pesquisa bastante modesto, j que o nmero mdio de comparaes igual a metade dos elementos da tabela. Apresenta como vantagem o fato de que pode ser usado em tabelas desordenadas. b) Pesquisa Binria: A pesquisa binria um mtodo que pode ser aplicado a tabelas ordenadas, armazenadas em dispositivos de acesso direto. O mtodo consiste na comparao do argumento de pesquisa com a chave daquela entrada localizada no meio da tabela. Se o argumento for igual chave da entrada, a pesquisa termina com sucesso. Se o argumento for maior o processo repetido para a metade superior da tabela e se for menor, para a metade inferior. Assim, a cada comparao, a rea de pesquisa reduzida metado do nmero de elementos. Apresenta como vantagem o fato de efetuar poucas comparaes e ser bem mais rpido que a pesquisa seqencia. Como desvantagem apresenta o fato de que a tabela deve estar ordenada para realizar a pesquisa. c) Pesquisa por Clculo de Endereo: O mtodo do clculo de endereo ("hashing") no apenas um mtodo de pesquisa, mas tambm um mtodo de organizao fsica da tabela. Ele consiste, basicamente, no armazenamento de cada entrada em um endereo calculado pela aplicao de uma funo chave da entrada. O processo de pesquisa sobre uma tabela organizada desta maneira similar ao processo de insero de uma 137

entrada e consiste na aplicao da funo de clculo de endereo ao argumento da pesquisa, obtendo como resultado o endereo da entrada procurada. A vantagem desse mtodo o acesso direto, atravs de um clculo. Como desvantagem apresenta um grande desperdcio de espao de armazenamento das informaes.

14.2 Indexao
Para reduo do tempo de acesso a um registro o mais indicado o uso de ndice para orientar a busca. Por este motivo, um dos importantes componentes de um arquivo o diretrio que abriga uma coleo de um ou mais ndices do arquivo. Um ndice um mapeamento que associa a cada chave, uma referncia ao registro que a contm. Assim, dada uma chave de pesquisa, o ndice fornece imediatamente a localizao do registro correspondente. Resumindo, ndice um par (chave, endereo). Quando trabalhamos com chave primria, seus valores tero sempre valores nicos e no nulos de forma a identificar unicamente cada registro. Porm, comum a existncia de valores chaves (no chaves primrias, mas simplesmente chaves) que se repetem, como por exemplo, num arquivo de funcionrios, a chave funo certamente ter repeties. Mesmo com repeties de valores de chaves o uso de ndices no ser eliminado, ser apenas manipulado de maneira mais complexa e eficiente. Um arquivo que utiliza a estrutura de ndices composto basicamente por duas partes: um arquivo de dados e uma estrutura de ndice associada. A cada insero de um novo registro no arquivo de dados, inserido no ndice uma referncia a ele. Para acessar um determinado registro, para consulta ou alterao, feita a pesquisa na estrutura de ndice para que sua localizao no disco seja encontrada e, em seguida, feito o acesso direto para leitura ou gravao. O objetivo melhorar o desempenho no processo de incluso de novos registros, pesquisa do arquivo e atualizao do arquivo. No havendo movimentao dentro do arquivo, a eficincia na incluso pode ser facilmente obtida se os registros forem inseridos sempre no final do arquivo. Porm, a pesquisa ser extremamente lenta, uma vez que os registros no esto armazenados ordenados e a busca ser realizada atravs de uma leitura seqencial dos registros. Com o objetivo de garantir rapidez em situaes como esta, a utilizao de uma estrutura de ndice reduziria bastante o nmero de acessos a disco j que a chave procurada seria armazenada nesta estrutura que seria mantida temporariamente na memria principal. Considerando a existncia de ndice, o processo de atualizao ser rpido. Fornecida a chave do registro do registro a ser alterado, seu acesso ser feito rapidamente, bem como a gravao das novas informaes. No caso de remoo, ela poder ser feita simplesmente eliminando sua referncia ao ndice, fazendo apenas a remoo lgica. A excluso fsica ocorrer apenas no processo de reorganizao do arquivo, quando uma cpia dele ser gerada, contendo apenas os registros ainda referenciados pelo ndice. A implementao de um sistema que utiliza estruturas de ndice, permitindo excluses lgicas e fsicas, pode tambm ser feita acrescentado-se um campo lgico em cada nodo da rvore. Assim, ao invs de remover o nodo da rvore quando o registro for excludo, simplesmente ser feita a atualizao deste campo para indicar a excluso. A estrutura de ndice dever ser mantida em disco para que o sistema reconhea os registros excludos. A velocidade, a complexidade e o uso eficiente da memria variam muito de acordo com a estrutura interna implementada de um ndice. Como j foi visto anteriormente, um ndice pode ser uma simples tabela, mas pode se apresentar tambm em uma das formas de rvore e, especialmente, se apresentada na forma de uma rvore de busca binria para arquivos de porte mdio aliar a simplicidade da implementao a um desempenho satisfatrio. Para arquivos maiores, outros tipos de implementaes baseados em rvores podero ser mais indicados.

138

15. COMPONENTES DO DELPHI PARA USO COMO ESTRUTURAS DE DADOS


Os componentes do Delphi podem ser usados para a manipulao de estruturas de dados convencionais de forma bem simples, pois os mesmos so implementados com as estruturas bsicas.

15.1 Pilhas com Delphi


Para manipular uma pilha, coloque no Form: um componente ListBox um Edit para digitar os elementos que sero inseridos na Pilha dois botes, sendo um para inserir os elementos e outro para remover da Pilha

Implemente o evento OnClick do Boto Inserir com o seguinte cdigo:


ListBox1.Items.Insert(0, Edit1.text); Edit1.Clear; Edit1.SetFocus;

Implemente o evento OnClick do Boto Remover com o seguinte cdigo:


if ListBox1.items.count=0 then showmessage('Pilha vazia!') else begin showmessage('Retirado: '+ListBox1.items[0]); ListBox1.items.Delete(0); end;

15.2 Filas com Delphi


Para manipular uma fila, coloque no Form: um componente ListBox 139

um Edit para digitar os elementos que sero inseridos na Fila dois botes, sendo um para inserir os elementos e outro para remover da Fila

Implemente o evento OnClick do Boto Inserir com o seguinte cdigo:


ListBox1.Items.add(Edit1.text); Edit1.Clear; Edit1.SetFocus;

Implemente o evento OnClick do Boto Remover com o seguinte cdigo:


if ListBox1.items.count=0 then showmessage('Fila vazia!') else begin showmessage('Retirado: '+ListBox1.items[0]); ListBox1.items.Delete(0); end;

Observe que a nica mudana da Pilha para a Fila no procedimento de insero, onde na pilha se usa
ListBox1.Items.Insert(0, Edit1.text), fazendo a insero na posio 0 (topo) e na Fila se faz a insero no final com ListBox1.Items.add(Edit1.text)

Para manipular mais de uma lista ou pilha na mesma aplicao, basta incluir vria ListBox de acordo com a necessidade, sendo uma ListBox para cada estrutura. Para manipular dados entre diferentes ListBox, pode-se fazer a retirada de um elemento de uma ListBox e a insero do mesmo elemento em outra ListBox, como no exemplo a seguir, em que um elemento retirado da ListBox1 e inserido na ListBox2:
var x: string; Begin // este begin do OnClic do boto e no deve ser copiado if ListBox1.items.count=0 then showmessage('Fila vazia!') else begin x:= ListBox1.items[0]); // armazena o elemento da frente da lista1 em x ListBox1.items.Delete(0); // remove o elemento da frente da lista1 ListBox2.Items.add(x); // insere o elemento na lista2 end; end; // este end do OnClic do boto e no deve ser copiado

140

15.3 Listas com Delphi


Para manipular uma lista, coloque no Form: um componente ListBox um Edit para digitar os elementos que sero inseridos na Lista dois botes, sendo um para inserir os elementos e outro para remover da Lista

Se a lista deve ser ordenada, deve-se mudar a propriedade Sorted da ListBox para True. Implemente o evento OnClick do Boto Inserir com o seguinte cdigo:
ListBox1.Items.add(Edit1.text); Edit1.Clear; Edit1.SetFocus;

Implemente o evento OnClick do Boto Remover com o seguinte cdigo:


if ListBox1.items.count=0 then showmessage('Lista vazia!') else begin if ListBox1.Selected[ListBox1.ItemIndex] then // se item est selecionado begin showmessage('Retirado: ' + ListBox1.items[ListBox1.ItemIndex]); ListBox1.items.Delete(ListBox1.ItemIndex); end else begin showmessage('Selecione um item da lista para remover.'); end; end;

15.4 rvores com Delphi


Para manipular uma rvore, coloque no Form: 141

um componente TreeView da Palheta Win32 um Edit para digitar os elementos que sero inseridos na Lista dois botes, sendo um para inserir os elementos e outro para remover da Lista um componente ImageList da Palheta Win32 para armazenar os cones da rvore

D um duplo-clique sobre a ImageList e clique sobre o boto Add, para adicionar imagens para a TreeView: Escolha um cone para representar o nodo da rvore no selecionado, como por exemplo, o arquivo C:\Program Files (x86)\Common Files\Borland Shared\Images\Default\Outclose.bmp Esta imagem ser a de ndice 0. Escolha outro cone para representar o nodo da rvore quando estiver selecionado, como por exemplo, o arquivo Outopen.bmp . Esta imagem ser a de ndice 1. Feche a escolha da ImageList clicando no OK. Verifique se a ImageList ficou com os dois cones associados, como no exemplo a seguir:

142

Mude a propriedade Images da TreeView para ImageList1

Implemente o evento OnClick do Boto Inserir com o seguinte cdigo:


with TreeView1.Items.AddChildFirst(TreeView1.Selected, Edit1.text begin ImageIndex := 0; SelectedIndex := 1; MakeVisible; end; Edit1.Clear; Edit1.SetFocus; ) do

Implemente o evento OnClick do Boto Remover com o seguinte cdigo:


if TreeView1.Selected=nil then showmessage('Selecione um item da para remover.') else begin If MessageDlg('Tem certeza que deseja excluir o N Selecionado? ' + TreeView1.Selected.Text, MtConfirmation, [mbYes, mbNo],0 ) = mryes then begin TreeView1.Selected.Delete; end; end;

Para funcionalidades acidiconais, pode-se precisar de uma listagem da TreeView em modo texto, para poder copiar e colar em outra aplicao ou estrutura, alm de poder expandir ou recolher toda a rvore. Para isso, altere o Form incluindo: Um campo Memo, onde ser listada a rvore em modo texto 3 botes: Listar, Expandir e Recolher

Implemente o evento OnClick do Boto Listar com o seguinte cdigo:


var i, j: integer; x: string; Begin // este begin do OnClic do boto e no deve ser copiado for i := 0 to TreeView1.Items.Count - 1 do //percorre o TreeView begin x:=''; // x vai armazenar espaos em branco de acordo com o nvel + elemento for j:= 1 to TreeView1.items[i].Level do x:=x+' '; x:=x+TreeView1.items[i].Text;

143

memo1.lines.add(x); end; end; // este end do OnClic do boto e no deve ser copiado

Implemente o evento OnClick do Boto Expandir com o seguinte cdigo:


TreeView1.FullExpand;

Implemente o evento OnClick do Boto Recolher com o seguinte cdigo:


TreeView1. FullCollapse;

15.5 Exerccios
1. Faa um programa em Delphi que manipule uma lista de compras. Como nem todos os produtos que queremos encontramos no supermercado, vamos implementar tambm uma segunda lista, que a de produtos que no encontramos no mercado. Apenas para controle, tambm vamos armazenar numa nova lista os produtos j comprados. Faa, portanto um programa que implemente 3 Listas Encadeadas Ordenadas usando o componente ListBox, com as seguintes operaes implementadas: Adicionar item na lista de compras (insereNaLista) Comprar item: remove o item selecionado da lista de compras e insere na lista de produtos comprados Transferir item da lista de compras para a lista de produtos no encontrados

2. A empresa Enxuga Gelo SA resolveu fazer uma doao de briquedos para os filhos dos seus funcionrios durante as comemoraes do aniversrio da empresa. Para isso, ela resolveu organizar uma fila de crianas, que sero chamadas para receber seu brinquedo. Enquanto as crianas ainda esto na fila, um responsvel vai verificar se a criana realmente filha de um funcionrio e se no for, ser removida da lista. Fazer um programa em Delphi que implemente uma ListBox para organizar a fila, com as seguintes operaes implementadas nos respectivos botes: Inserir criana na lista: inserir no final da lista encadeada, para o programa se comportar como se fosse uma fila chamar criana para receber o brinquedo: remover do incio da lista, para o programa se comportar como se fosse uma fila e escrever o nome da criana na tela excluir uma criana da lista: remover o item selecionado da lista encadeada excluir todas as crianas da lista: limpar a lista

3. Fazer um programa em Delphi que implemente uma rvore atravs de uma TreeView para implementar o quadro de pessoal com a respectiva funo que exerce, de forma hierrquica. O programa deve ter 2 campos Edit onde ser digitado o cargo e no outro o nome do funcionrio. Para inserir na TreeView deve-se concatenar o cargo com o nome do funcionrio (Edit1.text + : + Edit2.text). O programa deve ter os botes Inserir, Remover, Listar em modo texto num Memo, Expandir e Recolher. O quadro de pessoal que deve ser cadastrado, para testar o programa : Presidente Jaime o Diretora Acadmica: Ana Chefe de Departamento: Paulo, Maria, Antnio Professor: Marcos, Pedro, Beatriz, Carla, Apoio Educacional: Rosane o Secretria Escolar: Jlia Estagiria: Ane, Michele 144

Diretor Administrativo: Roberto Tesoureiro: Vitor Auxiliar de Escritrio: Sandro, Vincius Estagirio: Jonas, Andr Coordenador de RH:Tadeu

145

16. BIBLIOGRAFIA
N. Wirth. Algorithms + Data Structures = Programs. Prentice Hall, 1976. T. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein. Introduction to Algorithms, second edition, The MIT Press, 2005. Bayer, R. and E. McCreight. Organization and maintenance of large ordered indexes. Acta Informatica, Vol. 1, 1972, pp. 173-189. D. Comer. The ubiquitous B-tree. ACM Computing Surveys, June 1979, pp. 121-137. Siang Wun Song. Universidade de So Paulo http://www.ime.usp.br/~song/mac5710/slides/. Acesso em 10/03/2011. IME/USP. Disponvel em

TENEMBAUM, Aaron M. Estrutura de Dados Usando C. So Paulo: Makron Books do Brasil, 1995. PEREIRA, Slvio Lago. Estruturas de Dados Fundamentais: Conceitos e Aplicaes. So Paulo: Ed. rica, 1996. VELLOSO, Paulo. Estruturas de Dados. Rio de Janeiro: Ed. Campus, 1991. VILLAS, Marcos Vianna & Outros. Estruturas de Dados. Conceitos e Tcnicas de implementao. Rio de Janeiro: Ed. Campus, 1993. BIBLIOGRAFIA COMPLEMENTAR (LIVROS REFERENCIADOS): HOLZNER, Steven. Fundamentos de Estruturas de Dados. 3 ed. Rio de Janeiro: Ed. Campus, 1987. HOROWITZ, Ellis. Fundamentos de Estruturas de Dados. 3 ed. Rio de Janeiro: Ed. Campus, 1987. SZWARCFITER, JAIME LUZ. Estruturas de Dados e seus Algoritmos. Rio de Janeiro: Ed. LTC, 1994.

146

Você também pode gostar