Escolar Documentos
Profissional Documentos
Cultura Documentos
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
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.
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.
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.
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.
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.:
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]
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.
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;
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.
TYPE carac = SET OF CHAR; digitos = SET OF 0..9; vogal = SET OF ('A','E','I','O','U','a','e','i','o','u');
[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'];
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.
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;
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.
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]);
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>);
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 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.
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+}.
32
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
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;
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
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:
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.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.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
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
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.
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
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
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.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.
56
1 a ... b c final 5 e d 4
comeo
2 3
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
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
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
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
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
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
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.
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 *****************
//***************** 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
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
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
//***************** 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 }
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
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;
//***************** 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
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
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.
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.
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.
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:
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:
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.
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.
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
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.
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
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
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 -
118
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
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.
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
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
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
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
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;
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
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
143
memo1.lines.add(x); end; end; // este end do OnClic do boto e no deve ser copiado
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