aulas estruturas de dados

Upload: igor-steuck-lopes

Post on 12-Jul-2015

1.857 views

Category:

Documents


0 download

TRANSCRIPT

Universidade Estadual do Oeste do Paran - UNIOESTE Centro de Cincias Exatas e Tecnolgicas - CCET Curso de Cincia da Computao

ESTRUTURAS DE DADOS

CASCAVEL - PR 2011

SUMRIOUNIDADE 1 INTRODUO S ESTRUTURAS DE DADOS .................................. 1 1.1 1.2 1.3 1.41.4.1 1.4.2 1.4.3

INFORMAES GERAIS .................................................................................... 1 TIPOS PRIMITIVOS DE DADOS ......................................................................... 1 MECANISMOS PARA CONSTRUO DE TIPOS .............................................. 2 PROCEDIMENTOS E FUNES ........................................................................ 7PASSAGEM DE PARMETROS ................................................................................................ 8 PASSAGEM DE PARMETROS POR VALOR .......................................................................... 9 PASSAGEM DE PARMETROS POR REFERNCIA ............................................................. 10

UNIDADE 2 MATRIZES ......................................................................................... 12 2.1 INTRODUO ................................................................................................... 12 2.2 MATRIZES: CASO GERAL ................................................................................ 12 2.3 MATRIZES ESPECIAIS ..................................................................................... 132.3.1 2.3.2 MATRIZES DIAGONAIS ........................................................................................................... 14 MATRIZES TRIANGULARES ................................................................................................... 14

2.4 MATRIZES ESPARSAS ..................................................................................... 16 UNIDADE 3 LISTAS, FILAS E PILHAS................................................................. 21 3.1 LISTAS LINEARES ............................................................................................ 213.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.1.10 FUNDAMENTOS ....................................................................................................................... 21 REPRESENTAES ................................................................................................................ 22 REPRESENTAO DE LISTAS POR ALOCAO SEQENCIAL......................................... 22 ACESSAR O K-SIMO N DE UMA LISTA ............................................................................. 23 ALTERAR O VALOR DO K-SIMO N DE UMA LISTA ..........................................................24 INSERIR UM NOVO N ANTES DO K-SIMO N DE UMA LISTA ....................................... 24 REMOVER O K-SIMO N DE UMA LISTA ............................................................................ 25 REPRESENTAO DE LISTAS POR ENCADEAMENTO DOS NS ..................................... 25 ALOCAO DINMICA DE MEMRIA .................................................................................... 26 ENTENDENDO LISTAS REPRESENTADAS ATRAVS DE ALOCAO ENCADEADA DOS NS .................................................................................................................................. 27 3.1.11 ROTINAS BSICAS DE TRATAMENTO DE LISTAS...............................................................28 3.1.12 LISTAS COM DESCRITOR....................................................................................................... 33 3.1.13 LISTAS DUPLAMENTE ENCADEADAS ................................................................................... 36

3.2 PILHAS .............................................................................................................. 383.2.1 3.2.2 3.2.3 3.2.4 3.3.1 3.3.2 3.3.3 3.3.4 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 INIT, ISEMPTY E ISFULL ......................................................................................................... 40 UM PRIMEIRO EXEMPLO DO USO DE PILHAS..................................................................... 41 IMPLEMENTAO SEQENCIAL DE PILHAS ....................................................................... 42 ALGORITMOS PARA MANIPULAO DE PILHAS.................................................................43 IMPLEMENTAO SEQENCIAL DE FILAS .......................................................................... 46 PROBLEMAS NA IMPLEMENTAO SEQENCIAL DE FILAS ............................................ 48 SOLUCIONANDO OS PROBLEMAS DA IMPLEMENTAO SEQENCIAL ......................... 49 IMPLEMENTAO CIRCULAR PARA FILAS .......................................................................... 50 INTRODUO........................................................................................................................... 52 USO DE RECURSO NA SOLUO DE PROBLEMAS ......................................................... 53 QUANDO APLICAR RECURSO? ........................................................................................... 54 ELIMINANDO A RECURSO DE CAUDA ................................................................................ 54 PILHAS E ROTINAS RECURSIVAS ......................................................................................... 55

3.3 FILAS ................................................................................................................. 45

3.4 RECURSIVIDADE .............................................................................................. 52

UNIDADE 4 - RVORES .......................................................................................... 57 4.1 RVORES BINRIAS ........................................................................................ 574.1.1 RVORES DE BUSCA BINRIA .............................................................................................. 58

4.1.2 4.1.3

OPERAES BSICAS EM RVORES DE BUSCA BINRIA ............................................... 59 ATRAVESSAMENTO EM RVORES BINRIAS ..................................................................... 63

4.2 RVORES BALANCEADAS .............................................................................. 67 4.3 RVORES-AVL.................................................................................................. 674.3.1 4.3.2 4.3.3 INCLUSO EM RVORES-AVL ............................................................................................... 68 IMPLEMENTAO DA INCLUSO .......................................................................................... 71 REMOES EM RVORES-AVL............................................................................................. 76

4.4 RVORES HEAP E HEAPSORT ....................................................................... 804.2.1 HEAPSORT ................................................................................................................................... 81

4.5 RVORES B ...................................................................................................... 834.4.1 RVORES B MULTIDIRECIONAIS .......................................................................................... 85

4.6 OUTROS TIPOS DE RVORES E SUAS REPRESENTAES ...................... 99 UNIDADE 5 PESQUISA DE DADOS ................................................................... 100 5.1 MTODOS DE BUSCA .................................................................................... 1005.1.1 5.1.2 5.2.1 5.2.2 5.2.3 5.2.4 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 BUSCA LINEAR ...................................................................................................................... 100 BUSCA BINRIA ..................................................................................................................... 101 INTRODUO......................................................................................................................... 102 O PROBLEMA DO CASAMENTO DE CADEIAS.................................................................... 102 O ALGORITMO DA FORA BRUTA ...................................................................................... 103 O ALGORITMO DE KNUTH, MORRIS E PRATT ................................................................... 104 FUNDAMENTOS ..................................................................................................................... 108 APLICABILIDADE DO ESPALHAMENTO .............................................................................. 109 TABELAS DE ESPALHAMENTO ............................................................................................ 111 FUNES DE ESPALHAMENTO .......................................................................................... 112 O MTODO DA DIVISO ....................................................................................................... 112 TRANSFORMAO DE CHAVES ALFANUMRICAS .......................................................... 113 OPERAES BSICAS SOBRE TABELA DE ESPALHAMENTO ........................................ 117

5.2 PROCESSAMENTO EM CADEIAS ................................................................. 102

5.3 ESPALHAMENTOS ......................................................................................... 107

1

UNIDADE 1 INTRODUO S ESTRUTURAS DE DADOS1.1 INFORMAES GERAIS Niklaus Wirth afirma que programas de computador podem ser divididos em dois componentes: lgica e dados. A lgica trata como as operaes sero encadeadas de maneira a chegar no resultado esperado. Este componente foi discutido na disciplina de algoritmos. O segundo componente - dados - so os elementos a serem manipulados no programa. Neste ponto torna-se importante o estudo dos dados, principalmente na sua forma de estruturao, armazenamento e manipulao. Este o objetivo da disciplina de Estrutura de Dados. Estudar como os dados so estruturados, como so armazenados e, principalmente, como estes dados podem ser manipulados. Lembrando que a manipulao est condicionada estrutura de dados empregada. 1.2 TIPOS PRIMITIVOS DE DADOS Sero considerados disponveis quatro tipos primitivos de dados: Tipo inteiro real lgico caractere Abreviao int real log car Contedo -5, -2, -1, 0, 1, 3, 100 -120.32, -50.12, 0, 1, 1.32 V ou F A, a, B, b, C, c, !, ?, /

Para cada um desses tipos supomos que utilizada uma representao adequada. No vamos nos preocupar com as restries que seriam impostas por um particular computador ou sistema; tais restries envolvem, por exemplo, valores mximos dos inteiros, preciso dos reais, etc. a) Tipo inteiro: Os valores possveis para um objeto do tipo int so os nmeros inteiros (negativos, zero ou positivos). As operaes permissveis so: Operao soma subtrao multiplicao diviso inteira resto da diviso Smbolo + * div mod

Cada uma dessas operaes recebe como argumentos um par de inteiros e fornece um resultado inteiro. Em particular, div e mod so, respectivamente, o quociente inteiro e o resto da diviso entre dois inteiros; por exemplo, 5 div 2 2, e 5 mod 2 1. Alm disso, podemos comparar dois inteiros para testar se so iguais (=), diferentes (), ou segundo a ordem ( , ).

2 b) Tipo real: Os objetos do tipo real so os nmeros racionais, isto , nmeros normalmente representados por uma parte inteira e uma parte fracionria. As operaes do tipo real so: Operao soma subtrao multiplicao diviso Smbolo + * /

Cada uma das quatro operaes acima recebe um par de nmeros do tipo real e fornece um resultado do mesmo tipo. Alm disso, como nos inteiros, podemos comparar dois elementos do tipo real conforme =, , , etc. c) Tipo lgico: Este tipo consiste de exatamente dois valores: verdadeiro e falso, sendo as constantes correspondentes V e F. As operaes sobre os valores lgicos so Operao e (conjuno) ou (disjuno) no (negao) Smbolo e ou & ou ou | not, ~ ou !

d) Tipo caractere: Os objetos deste tipo so os chamados caracteres alfanumricos: os dgitos decimais (0 - 9), as letras (A - Z) e alguns sinais especiais (espao em branco, sinais de pontuao, etc.) No tipo caractere podemos realizar comparaes do tipo =, , >, < e ainda a operao de adio (+) que concatena caracteres. 1.3 MECANISMOS PARA CONSTRUO DE TIPOS Veremos, a seguir, alguns mecanismos que permitem a construo de outro tipo a partir dos tipos primitivos. O formato geral para a definio de um tipo o seguinte: tipo nome_do_tipo_definido = definio_do_tipo Poderemos construir os seguintes tipos: vetor; matriz; registro; referncia (ponteiro); enumerao. a) Vetor: O vetor permite a construo de um tipo cujos valores so agregados

3 homogneos de um tamanho definido, isto , suas componentes so todas de um mesmo tipo. O formato de definio para vetor o seguinte: tipo nome_do_vetor = vetor [limite_inferior.. limite_superior] de tipo; onde limite_inferior e limite_superior so constantes do tipo inteiro e tipo o tipo das componentes. Por exemplo: tipo VET_NOME_ALUNOS = vetor [1..10] de caractere; Criamos um tipo cujos elementos so compostos por 10 nomes. Um elemento deste tipo pode conter o nome dos 10 alunos de uma classe. Supondo que uma varivel TURMA_A do tipo VET_NOME_ALUNOS possua os seguintes valores: Ana 1 do vetor. Caso queiramos referenciar Carla usaremos a seguinte notao: TURMA_A[4] O contedo deste elemento Carla. b) Matriz: A matriz, como o vetor, permite a construo de um tipo cujos valores so agregados homogneos de um tamanho definido, isto , suas componentes so todas de um mesmo tipo. O formato de definio para vetor o seguinte: tipo nome_da_matriz = matriz [lim_inf_1..lim_sup_1; lim_inf_2..lim_sup_2] de tipo; onde lim_inf_1, lim_inf_2, lim_sup_1 e lim_sup_2 so constantes do tipo inteiro e tipo o tipo das componentes. Por exemplo: tipo MAT_CLASSES_ALUNOS = matriz [1..3; 1..10] de caractere; Criamos um tipo cujos elementos so compostos por 30 nomes. Um elemento deste tipo pode conter o nome dos 10 alunos de cada uma das 3 classes de uma escola. Supondo que uma varivel TURMAS_QUINTA_SERIE do tipo MAT_CLASSES_ALUNOS possua os seguintes valores: 1 Ana Pedro Paulo Carla Jos Joo Maria Cludia Mrio Iara 2 Juca Roger Ivo Cris Joel Mrcio Mrcia Sara Denise Carlos 3 Jucy Darci Iloir Osmar Daniel Diogo Ana Cludia Josi Julia 1 2 3 4 5 6 7 8 9 10 Pedro 2 Paulo 3 Carla 4 Jos 5 Joo 6 Maria 7 Cludia 8 Mrio 9 Iara 10

Cada elemento num vetor possui um ndice que indica sua posio dentro

4 Cada elemento numa matriz referencial por dois ndices, que indicam sua posio dentro da matriz. Caso queiramos referenciar Mrcia usaremos a seguinte notao: TURMAS_QUINTA_SERIE [2, 7] O contedo deste elemento Mrcia. c) Registro: s vezes temos necessidade de agregados heterogneos, isto , cujas componentes no so necessariamente de um mesmo tipo. O formato de definio para um registro o seguinte: tipo nome_do_registro = registro campo1, campo2, ..., campon : tipo1; campo1, campo2, ..., campon : tipo2; ... campo1, campo2, ..., campon : tipon; fim registro; Por exemplo, se quisermos construir um registro para armazenar o nome da disciplina e a turma qual ser ofertada esta disciplina, teremos: tipo TURMAS_DISCIPLINA = registro DISCIPLINA : caractere; TURMA : inteiro; fim registro; Supondo uma varivel GRADE_DISCIPLINAS do tipo TURMAS_DISCIPLINAS para a qual queiramos inserir a seguinte informao: A disciplina de Clculo Diferencial ser ofertada para a turma nmero 3. A atribuio desta informao para a varivel GRADE_DISCIPLINAS se dar da seguinte maneira: GRADE_DISCIPLINAS.DISCIPLINA Clculo Diferencial; GRADE_DISCIPLINAS.TURMA 3; Noutro exemplo, o tipo frao pode ser assim constitudo: tipo FRACAO = registro NUMERADOR, DENOMINADOR : inteiro; fim registro; A frao 2/7 poderia ser armazenada numa varivel NUM_FRAC do tipo FRACAO, da seguinte forma: NUM_FRAC.NUMERADOR 2; NUM_FRAC.DENOMINADOR 7; d) Referncia (Ponteiro): At agora no nos importamos muito com a maneira pela qual um objeto de um determinado tipo armazenado na memria de um computador. Supomos

5 que a cada varivel corresponde um espao suficiente para guardar cada um de seus possveis valores. Dizemos que a cada varivel alocado um espao - que chamaremos de clula (do tipo da varivel). Vamos supor que esta alocao determinada pela simples ocorrncia da declarao da varivel. Assim, a declarao das variveis x e y como de tipo inteiro ocasiona a alocao de uma clula inteira para cada uma delas. O mecanismo de referncia ou ponteiro permitir uma modalidade dinmica de alocao. A definio de tipo pelo mecanismo de referncia obedece ao seguinte formato: tipo nome_do_ponteiro = ^tipo; Por exemplo: tipo MATRICULA = ^inteiro; Ao declararmos uma varivel GEOGRAFIA do tipo MATRICULA estaremos criando um ponteiro para uma clula de memria. Nesta clula possvel armazenar um nmero inteiro, entretanto esta clula ainda no existe; existe somente um ponteiro que poder apontar para esta clula. A alocao do espao para a varivel geografia compreende duas partes: 1 - Parte de valor: para armazenar um valor do tipo inteiro; 2 - Parte de posio: para armazenar uma indicao da localizao da parte de valor. A simples declarao da varivel GEOGRAFIA como sendo do tipo MATRICULA ocasiona a alocao de espao para a parte de posio, mas no ainda para a parte de valor. Esta ltima ocasionada pelo comando: aloque (GEOGRAFIA); que, alm disso, faz com que na parte de posio seja colocada uma indicao da localizao onde est alocada a parte de valor.

- 1 - Declarao de geografia:

Parte de posio Figura 1.1 Situao aps ser declarada a varivel GEOGRAFIA

- 2 - Aps o comando aloque (GEOGRAFIA):

Parte de posio

Parte de valor

Figura 1.2 Situao aps ser executado o comando aloque

6 O espao alocado pelo comando aloque pode ser liberado pela execuo de: desaloque (GEOGRAFIA); cujo efeito fazer a situao da Figura 1.2 retornar situao da Figura 1.1. J que a varivel GEOGRAFIA tem, por assim dizer, duas partes, vamos usar a seguinte notao: GEOGRAFIA designa a parte de posio (que criada pela declarao da varivel); GEOGRAFIA^ designa a parte de valor (que apontada pela parte de posio).

A indicao da localizao contida na parte de posio no um objeto que possa ser manipulado por operaes. Podemos apenas testar sua igualdade ou no por meio de = ou . Um caso especial a constante nil; se o contedo de GEOGRAFIA nil ento no aponta para nenhuma parte de valor. Os mecanismos de agregao (vetor, matriz e registro) geralmente pressupem que suas componentes sejam armazenadas de maneira contgua em memria. O mecanismo de referncia permite armazenamento disperso dos seus componentes.

e) Enumerao: A enumerao permite definir tipos de dados por meio dos valores que os dados daquele tipo podem assumir. A definio feita indicando-se um conjunto ordenado de identificadores que denotam os valores que os dados daquele tipo devem assumir. O formato geral de uma enumerao : tipo nome_do_conjunto = (valor1, valor2, valor3, ..., valorn); Como exemplo, suponha a definio do tipo ms, que feita por enumerao dos valores vlidos que uma varivel deste tipo pode ter: tipo MES = (jan, fev, mar, abr, maio, jun, jul, ago, set, out, nov, dez); Assim, seria vlida a seguinte construo: var MES_NASC : MES; se MES_NASC = dez ento ... Note que, pelo fato de os valores de definio do tipo formarem um conjunto ordenado, existe uma relao de ordem entre eles. Portanto, vlido o uso de operadores de relao entre eles, isto , vlido afirmarmos que jan < fev < ... < nov < dez, para o tipo antes definido.

7 1.4 PROCEDIMENTOS E FUNES PROCEDIMENTOS Os procedimentos so utilizados quando um conjunto de comandos repete-se ao longo do algoritmo. Ento, para no escrevermos vrias vezes o mesmo bloco de comandos, usamos os procedimentos. Sintaxe: procedimento IDENTIFICADOR (parmetros); Comandos; fim procedimento IDENTIFICADOR; Onde: - IDENTIFICADOR o nome de referncia do procedimento; - parmetros a lista de variveis que sero passadas ao procedimento para serem manipuladas no seu interior. Na definio dos parmetros tambm devem ser declarados seus tipos. Nem todo procedimento utiliza-se de parmetros, portanto um item opcional.

Exemplo: procedimento LINHA (COMPRIMENTO : inteiro); var I : inteiro; para I 1 at COMPRIMENTO faa escreva -; fim para; fim procedimento LINHA;

FUNES As funes constituem um tipo especial de subrotina, bastante semelhante ao procedimento, que tem a caracterstica especial de retornar ao algoritmo chamador um valor associado ao nome da funo. Esta caracterstica permite uma analogia com o conceito de funo na matemtica. A utilizao de outras funes no algoritmo como por exemplo, seno, tangente ou qualquer outra funo especial, pode ser feito declarando-se um procedimento funo. A declarao de uma funo semelhante a de um procedimento. Difere somente na especificao do tipo da mesma, ou seja, do tipo de valor que ser retornado. Apesar de terem sido citadas apenas funes numricas, elas podem ser lgicas ou literais.

8 Sintaxe: funo IDENTIFICADOR (parmetros):tipo de retorno; comandos; fim funo IDENTIFICADOR;

Onde: - IDENTIFICADOR o nome de referncia da funo; - parmetros a lista de variveis que sero passadas funo para serem manipuladas no seu interior. Na definio dos parmetros tambm devem ser declarados seus tipos. Nem toda funo utiliza-se de parmetros, portanto um item opcional. - tipo de retorno o tipo de dado que a funo retornar (inteiro, real, lgico, caractere);

Exemplo: funo E_PAR (N: inteiro): lgico; se (N mod 2 = 0) ento E_PAR Verdadeiro; seno E_PAR Falso; fim se; fim funo E_PAR;

1.4.1 PASSAGEM DE PARMETROS A transferncia de informaes de e para subrotinas utilizando-se variveis globais no constitui uma boa disciplina de programao. Estas transferncias precisam ser mais formalizadas e documentadas a bem da legitimidade, documentao e organizao do programa elaborado. Em algoritmos, a transferncia de informaes de e para subrotinas pode ser feita com a utilizao de parmetros. Esta utilizao formaliza a comunicao entre mdulos. Alm disso, permite que um mdulo seja utilizado com operandos diferentes, dependendo do que se deseja do mesmo. Parmetros de definio so objetos utilizados dentro das subrotinas que em cada ativao representam objetos de nvel mais externo. A forma de se utilizar parmetros em subrotinas foi apresentada anteriormente. A chamada de uma subrotina aparece numa expresso e tem a seguinte forma: NOME DA SUBROTINA (Lista de parmetros de chamada);

9 Exemplo: incio var A, B, C : real; procedimento EXEMPLO (VALOR_1, VALOR_2, VALOR_3 : real); MAIOR_VALOR : real; MAIOR_VALOR VALOR_1; se (VALOR_2 > MAIOR_VALOR) ento MAIOR_VALOR VALOR_2; fim se; se (VALOR_3 > MAIOR_VALOR) ento MAIOR_VALOR VALOR_3; fim se; escreva O maior valor : , MAIOR_VALOR; fim procedimento EXEMPLO; {corpo do programa principal} leia Digite 3 nmeros:; A, B, C; EXEMPLO (A, B, C); fim. 1.4.2 PASSAGEM DE PARMETROS POR VALOR Na passagem de parmetros por valor (ou por cpia) o parmetro real calculado e uma cpia de seu valor fornecida ao parmetro formal, no ato da invocao da subrotina. A execuo da subrotina prossegue normalmente e todas as modificaes feitas no parmetro formal no afetam o parmetro real, pois trabalha-se apenas com uma cpia do mesmo. Exemplo: incio var X : inteiro; procedimento PROC (Y : inteiro); Y Y + 1; escreva Durante = , Y; fim procedimento PROC;

10 X 1; escreva Antes = , X; PROC (X); escreva Depois = , X; fim. O algoritmo anterior fornece o seguinte resultado: Antes = 1; Durante = 2; Depois = 1; Este tipo de ao possvel porque, neste mecanismo de passagem de parmetros, feita uma reserva de espao em memria para os parmetros formais, para que neles seja armazenada uma cpia dos parmetros reais. 1.4.3 PASSAGEM DE PARMETROS POR REFERNCIA Neste mecanismo de passagem de parmetros no feita uma reserva de espao em memria para os parmetros formais. Quando uma subrotina com parmetros passados por referncia chamada, o espao de memria ocupado pelos parmetros reais compartilhado pelos parmetros formais correspondentes. Assim, as eventuais modificaes feitas nos parmetros formais tambm afetam os parmetros reais correspondentes. Uma mesma subrotina pode utilizar diferentes mecanismos de passagem de parmetros, para parmetros distintos. Para diferenciar uns dos outros, convencionou-se colocar o prefixo var antes da definio dos parmetros formais passados por referncia. Se por exemplo uma subrotina tem o seguinte cabealho: procedimento PROC (X, Y : inteiro; var Z : real; J : real) Ento: - X e Y so parmetros formais do tipo inteiro e so passados por valor; - Z um parmetro formal real passado por referncia; - J um parmetro formal real passado por valor. O exemplo do item anterior, alterado para que o parmetro Y do procedimento seja passado por referncia, torna-se: incio var X : inteiro; procedimento PROC (var Y : inteiro); Y Y + 1; escreva Durante = , Y; fim procedimento PROC;

11 X 1; escreva Antes = , X; PROC (X); escreva Depois = , X; fim. O resultado do algoritmo modificado : Antes = 1; Durante = 2; Depois = 2;

12

UNIDADE 2 MATRIZES2.1 INTRODUO J estamos bastante familiarizados com a idia de matriz. Em matemtica, usual se trabalhar com matrizes de elementos reais como:

1,0 2,0 5,7 M = 1,3 0,0 0,9que uma matriz real 2x3, isto , com 2 linhas e 3 colunas. Estamos acostumados a somar e multiplicar matrizes como esta acima. Porm, do ponto de vista de estrutura de dados, estamos mais interessados na maneira como temos acesso informao armazenada em uma matriz. Isto feito pela indicao de uma linha e uma coluna, o que identifica a posio onde que elas se cruzam. Assim, atravs dos ndices 1 e 3, identificamos a posio [1, 3], que no caso do exemplo acima contm o valor 5,7. Isto costuma ser indicado por M[1, 3] = 5,7. Para localizarmos um elemento particular precisamos fornecer dois ndices: sua linha e sua coluna. Por esta razo, elas so chamadas matrizes bidimensionais. Quando apenas um ndice suficiente, temos uma matriz unidimensional, que costuma ser chamada de vetor coluna, ou de vetor linha, conforme a representamos:

7 ,0 Vetor Coluna: 0,1 2,3 Vetor Linha: [7,0

0,1 2,3]

comum tambm o caso de matrizes tridimensionais. Por exemplo, uma matriz cujo elemento [i, j, k] d a temperatura em um ponto de um cubo slido. 2.2 MATRIZES: CASO GERAL Os lugares de um teatro costumam ser identificados atravs da fila e da coluna de cada um. O servio de reservas mantm um mapa que indica os lugares ocupados e os ainda vagos. Para fixar idias, vamos considerar um teatro com 15 filas, numeradas de 1 a 15, cada fila com 20 cadeiras; 10 cadeiras esquerda e 10 cadeiras direita. Vamos imaginar o mapa de reservadas como uma matriz bidimensional RES, da maneira seguinte:-10 1 2 M 15 L Lado Esquerdo -1 0 1 L Lado Direito 100

13 Um par de ndices como [7, -3] identifica a poltrona nmero 3 do lado esquerdo da fila 7; caso esteja reservada, teremos RES [7, -3] = V. Analogamente, RES [2, +5] = F quer dizer que a 5a poltrona do lado direito da 2a fila est livre. Pares com j = 0 (RES [i, 0]) indicam posies no corredor e so casos especiais. A matriz RES poderia ser declarada da seguinte forma: tipo TEATRO = matriz [1..15; -10..10] de lgico; var RES : TEATRO; O procedimento usual da reserva de um lugar pode ento ser descrito atravs de operaes de consulta e de atribuio matriz RES: desejando-se a poltrona i da fila j, faz-se uma consulta. caso RES [i, j] = V, deve-se escolher outro lugar; caso RES [i, j] = F, a reserva concedida e o mapa alterado atravs da atribuio RES [ i, j] V.

Supondo que haja dois espetculos por dia, fcil imaginar os dois mapas de reserva correspondentes como uma matriz RESERVA tridimensional, na qual o novo ndice k 1 ou 2, conforme a sesso desejada. Agora o dado necessrio para se fazer uma reserva uma tripla [i, j, k] e as operaes passam a ser: consulta: RESERVA [i, j, k]; atribuio: RESERVA [i, j, k] Valor; A matriz RESERVA poderia ser declarada da seguinte forma: tipo THEATER = matriz [1..3; 1..15; -10..10] de lgico; var RESERVA : THEATER;

Exerccios: 1) Escreva um procedimento para realizar a reserva de um local no teatro. Considere o caso em que h somente um espetculo ao dia. O procedimento ser executado at que o usurio deseja sair do procedimento. 2) Escreva um procedimento para atender o pedido de cancelamento de reservas. 3) Escreva um procedimento otimizado para realizar a transposio de uma matriz de ordem m x m. 2.3 MATRIZES ESPECIAIS A representao vista at agora guarda todos os elementos da matriz. Freqentemente ocorrem matrizes de nmeros apresentando uma boa parte de elementos nulos. Deixando de guardar estes, podemos fazer uma economia razovel de memria. Se os elementos nulos esto concentrados em uma regio da

14 matriz, como por exemplo acima da diagonal principal, ento podemos lanar mo de representaes especiais, simples e compactas. 2.3.1 MATRIZES DIAGONAIS Consideremos a matriz M [3, 4], de reais abaixo:

3,5 0,0 0,0 0,0 M = 0,0 1,6 0,0 0,0 0,0 0,0 2,5 0,0 Matrizes como M, em que todos os elementos com i j so nulos, so chamadas de matrizes diagonais. Uma vez que somente os elementos da diagonal principal so diferentes de zero, podemos utilizar um vetor para armazenar este tipo de matriz. Uma matriz diagonal com m linhas e n colunas contm m.n elementos. O vetor para armazenar esta matriz ser composto por p elementos, sendo p o menor valor entre m e n. Assim, para a matriz M acima, podemos utilizar um vetor com 3 posies, uma vez que ela possui m = 3 linhas e n = 4 colunas, implicando em p = 3 tipo MATDIAG = vetor [1..3] de real; var M_DIAG : MATDIAG; A consulta a uma matriz diagonal M_DIAG bastante simples, como vemos a seguir (Implementar a consulta atravs de uma funo): funo CONSULTA (M: MATDIAG; I, J : inteiro):real; se (i = j) ento CONSULTA M[I] seno CONSULTA 0,0; fim se; fim funo CONSULTA; A atribuio de valores numa matriz diagonal somente possvel para elementos em que i = j. Qualquer valor, diferente de zero, atribudo a uma posio com i j implica na matriz deixar de ser diagonal. 2.3.2 MATRIZES TRIANGULARES Um outro caso em que se pode economizar memria por meio de uma representao especial o das matrizes triangulares. Por exemplo, as matrizes 5x4

15 abaixo so triangulares, sendo M triangular superior, e N triangular inferior.

0,0 7,1 0,3 5,1 4,3 8,3 0,0 0,0 1,0 2,1 7,7 7,1 2,4 0,0 M = 0,0 0,0 3,5 2,4 N = 3,5 4,2 4,7 0,0 0,0 0,0 4,2 4,5 6,7 0,1 0,0 0,0 0,0 0,0 3,1 4,0 5,1

0,0 0,0 0,0 2,3 5,1

Dos 5 x 4 = 20 elementos da matriz M, apenas os 10 elementos no abaixo da diagonal principal so diferentes de 0,0. Assim, poderamos representar M por: M = [7,1 0,3 5,1 4,3 -1,0 2,1 7,7 3,5 2,4 4,2] J no caso de N, bastaria guardar os 14 elementos no acima da diagonal principal. Vamos nos restringir a matrizes quadradas. Uma matriz dita triangular inferior quando P[i, j] = 0 sempre que i < j. Ento, na 1a linha de P, todos os elementos so nulos, exceto talvez P[1, 1]. Na 2a linha, s P[2, 1] e P[2, 2] que podem ser no nulos. Na linha i, s podem ser diferentes de 0 todos os elementos P[i, 1], P[i, 2], ..., P[i, i]. Assim, o nmero de elementos no nulos em P pode ser obtido por:

m=

n (n + 1) 2

onde: m = nmero de elementos no nulos em P; n = nmero de linhas e colunas em P (matriz quadrada). Veja o exemplo:

1 3 MAT = 1 8 7

0 0 0 0 5 0 0 0 5 (5 + 1) = 15 4 7 0 0 m = 2 3 2 1 0 4 5 2 3

Poderamos, ento, armazenar esta matriz num vetor com m = 15 posies, da seguinte maneira: MAT_V = [1, 3, 5, 1, 4, 7, 8, 3, 2, 1, 7, 4, 5, 2, 3] Mas como vamos saber que o elemento MAT[4, 3] = MAT_V[9] = 2? Para acessar um elemento da matriz, atravs de suas coordenadas i e j,

16 numa matriz triangular inferior, podemos utilizar a seguinte regra: MAT[i, j] = MAT_V[j + (i . (i - 1)) div 2] Assim: MAT[4, 3] = MAT_V[3 + (4 . (4 - 1)) div 2] = MAT_V[3 + 6] = MAT_V[9] = 2

Exerccios: 2) Escreva um algoritmo para inserir elementos numa matriz triangular inferior de ordem 5. tipo MATTI = vetor [1..15] de real; procedimento INSERE_MAT_DIAG_INF (var MATRIZ : MATTI); var I, J : inteiro; para I = 1 at 5 faa para J = 1 at 5 faa se (I >= J) ento escreva Digite o elemento , I, J; leia (MATRIZ[J + (I * (I - 1)) div 2]); fim se; fim para fim para fim procedimento INSERE_MAT_DIAG_INF; 3) Escreva um algoritmo para inserir elementos numa matriz triangular superior de ordem 5. [i, j] k k =

2n(i 1) + i(1 i ) + j , onde n = ordem da matriz 2

2.4 MATRIZES ESPARSAS Matrizes esparsas so aquelas matrizes onde h uma quantidade elevada de elementos nulos. Por exemplo:

6 0 3 0 0 0 ME = 0 0 0 2 0 3 0 1 0 0 0 0

17 Podemos otimizar o uso de memria armazenando somente os elementos no nulos desta matriz num vetor. Mas ao armazenarmos somente o valor perdemos a posio do elemento na matriz. Portanto, devemos armazenar, junto ao valor, seu respectivo ndice. VETME = [(1, 1, 6) (1, 3, 3) (2, 4, 2) (2, 6, 3) (3, 2, 1)] Note que cada elemento deste vetor constitudo por 3 valores: o primeiro valor a linha do elemento, o segundo a coluna e o terceiro o valor armazenado na matriz. VETME trata-se de um vetor onde cada elemento uma tupla e a construo desta tupla pode ser realizada atravs de um registro. Assim, teremos: tipo ELEMENTO = registro LINHA : inteiro; COLUNA : inteiro; VALOR : inteiro; fim registro; tipo VETOR = vetor [1..5] de ELEMENTO; var VETME : VETOR; Na estrutura acima possvel armazenar apenas cinco elementos, pois o vetor esttico. O que acontecer que inserirmos mais um elemento no nulo na matriz? Ser impossvel fazermos isso. Pensando nestes casos, devemos declarar o vetor com um nmero maior de elementos, por exemplo 8 elementos: tipo VETOR = vetor [1..8] de ELEMENTO; Desta maneira teremos 3 posies de folga no vetor que representa a matriz esparsa, sendo possvel inserir at trs elementos novos na mesma. A consulta numa matriz esparsa se d pela busca de um elemento que contenha o ndice procurado. Caso este ndice no exista significa que aquele elemento possui valor 0. Exerccios: 5) Escreva o algoritmo de uma funo para buscar um elemento numa matriz esparsa. incio CONST NUM = 8; tipo ELEMENTO = registro LINHA : inteiro; COLUNA : inteiro; VALOR : inteiro; fim registro;

18 tipo MATESP = vetor [1..NUM] de ELEMENTO; funo BUSCA (I, J : inteiro; MATRIZ : MATESP): inteiro; var D : inteiro; para D = 1 at NUM faa se ((MATRIZ[D].linha = I) e (MATRIZ[D].coluna = J)) ento BUSCA MATRIZ[D].VALOR; exit; seno BUSCA 0; fim se; fim para fim funo BUSCA; fim. Quando inserirmos um novo elemento numa matriz esparsa devemos manter os elementos ordenados pelos seus ndices, de forma que o processo de busca seja o mais rpido possvel. Da mesma maneira, ao atribuirmos o valor nulo para um elemento, devemos remov-lo da matriz, pois a mesma somente armazena os elementos no nulos. Na insero devemos nos preocupar em verificar trs fatos: Nulidade do novo valor; Existncia de espao no vetor, que representa a matriz, para o novo elemento; Ordem dos elementos. Quando um novo valor ser inserido na matriz, e este valor for nulo, simplesmente no ser inserido no vetor. O exemplo a seguir mostra a insero de um elemento no nulo na matriz esparsa. A matriz ME que contm os valores

6 0 3 0 0 0 ME = 0 0 0 2 0 3 0 1 0 0 0 0 pode ser representada por um vetor VETME = [(1, 1, 6) (1, 3, 3) (2, 4, 2) (2, 6, 3) (3, 2, 1) ( , , ) ( , , ) ( , , )]

19 Insero de um novo elemento:

Na insero de um novo elemento devemos, primeiro, verificar se mesmo um novo elemento, ou seja, se no h um elemento no nulo na matriz com o mesmo ndice. Supondo que temos de inserir o novo elemento ME[3, 4] = 5. Este ndice no existe no vetor VETME, portanto trata-se de um novo elemento. Sua insero ser feita anexando-se o elemento na primeira posio livre de VETME, pois h espao no vetor e o elemento a ser inserido tem ndice que o posiciona aps o ltimo elemento: ME[3, 2] = 1. VETME = [(1, 1, 6) (1, 3, 3) (2, 4, 2) (2, 6, 3) (3, 2, 1) (3, 4, 5) ( , , ) ( , , )] Analisemos, agora, a insero de um novo elemento ME[2, 5] = 4. O ndice j existe em VETME? No, ento trata-se de um elemento novo. H espao livre no vetor? Sim, portanto o elemento poder ser inserido. Mas onde? O ndice do elemento [2, 5] mostra que a insero deve ser feita entre os elementos ME[2, 4] = 2 e ME[2, 6] = 3. Mas ali no h posio livre; e nem deveria haver. Ento, para que a insero seja possvel, devemos deslocar os elementos subseqentes a ME[2, 4] = 2 uma posio para a direita, a fim de criar uma posio livre onde ser inserido o elemento ME[2, 5] = 4. VETME = [(1, 1, 6) (1, 3, 3) (2, 4, 2) ( , , ) (2, 6, 3) (3, 2, 1) (3, 4, 5) ( , , )] VETME = [(1, 1, 6) (1, 3, 3) (2, 4, 2) (2, 5, 4) (2, 6, 3) (3, 2, 1) (3, 4, 5) ( , , )]

Alterao de um elemento:

Analisemos, agora, a insero do elemento ME[1, 3] = 0. O ndice j existe em VETME? Caso a resposta seja sim, ento trata-se de uma alterao. Assim, temos de nos preocupar com dois possveis casos: O elemento ser alterado para um valor nulo; O elemento ser alterado para um valor no nulo. O novo valor ser nulo equivale a remover o elemento de VETME. Assim, todos os elementos no nulos direita do elemento devem ser movidos uma posio para a esquerda, sendo que o ltimo dever receber valores nulos. VETME = [(1, 1, 6) (1, 3, 3) (2, 4, 2) (2, 5, 4) (2, 6, 3) (3, 2, 1) (3, 4, 5) ( , , )] VETME = [(1, 1, 6) (2, 4, 2) (2, 5, 4) (2, 6, 3) (3, 2, 1) (3, 4, 5) (3, 4, 5) ( , , )] VETME = [(1, 1, 6) (2, 4, 2) (2, 5, 4) (2, 6, 3) (3, 2, 1) (3, 4, 5) ( , , ) ( , , )] Se o elemento ser alterado para um valor no nulo, basta localiz-lo em VETME e alterar o terceiro elemento da tupla. Por exemplo, o elemento VETME[2, 5] = 8.

20 VETME = [(1, 1, 6) (2, 4, 2) (2, 5, 8) (2, 6, 3) (3, 2, 1) (3, 4, 5) (3, 4, 5) ( , , )] Exerccios: 6) Escreva o algoritmo de uma funo para inserir/alterar elementos numa matriz esparsa.incio CONST NUM = 8; tipo ELEMENTO = registro LINHA : inteiro; COLUNA : inteiro; VALOR : inteiro; fim registro; tipo MATESP = vetor [1..NUM] de ELEMENTO; funo INSERCAO (I, J, VALOR : inteiro; MATRIZ : MATESP) : lgico; var K : inteiro; se (BUSCA(I, J, K, MATRIZ) = 0) ento se (VALOR 0) ento se (HAESPACO(MATRIZ)) ento MATRIZ[K] VALOR; INSERCAO Verdadeiro; seno DESLOCADIREITA (K, MATRIZ); {Abre espao no vetor} MATRIZ[K] VALOR; INSERCAO Verdadeiro; fim se; seno INSERCAO Falso; fim se; seno INSERCAO Verdadeiro; fim se; seno se (VALOR 0) ento MATRIZ[K] VALOR; seno DESLOCAESQUERDA (K, MATRIZ); {desloca para esquerda e} {excluiu o ltimo da direita} fim se; fim se; fim funo INSERCAO; {posio est ocupada} {alterar o valor da posio} {posio est vazia} {H o que incluir!} { possvel incluir!}

{K retornar a posio onde est ou ser inserido o elemento}

se (EHULTIMO(I, J, MATRIZ)) ento {Inclui-ltima posio}

fim.

21

UNIDADE 3 LISTAS, FILAS E PILHAS3.1 LISTAS LINEARES Freqentemente nos deparamos, na soluo de determinados problemas, com conjuntos de dados que se relacionam entre si de alguma forma, refletindo algumas propriedades que estes dados apresentam no problema real. Naturalmente, desejamos que este relacionamento seja preservado, com o objetivo de se poder fazer uso do mesmo, quando estes forem representados no computador. Considere, por exemplo, o caso de um problema que envolva a manipulao de dados de precipitaes pluviomtricas dirias de um perodo de um ms. Se o problema consistir em obter-se, digamos, a precipitao pluviomtrica mdia do perodo, no h nenhuma necessidade de se conhecer o relacionamento existente entre os dados dirios. Se, entretanto, o problema consistir em determinar uma funo que expresse o fenmeno (por exemplo, precipitao x tempo) no perodo, ento necessrio conhecer-se a relao de ordem cronolgica dos dados. 3.1.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 L vazia; caso contrrio, so vlidas as seguintes propriedades: a1 o primeiro elemento de L; an o ltimo elemento de L; ak, com 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 elementos 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 da lista; apagar uma lista outras ... Considerando-se somente as operaes de acesso, insero e remoo,

22 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 da lista. Listas com esta caracterstica so tambm denominadas listas LIFO (Last-In/First-Out, ou em portugus: 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. Filas so tambm denominadas de listas FIFO (First-In/First-Out, ou em portugus: primeiro que entre/primeiro que sai); Fila Dupla: lista linear onde as inseres, remoes ou acessos so realizados em qualquer extremo. Filas duplas so tambm denominadas DEQUE (DoubleEnded QUEue, ou em portugus: fila de extremidade dupla). Uma Fila Dupla pode ainda gerar dois casos especiais: Fila Dupla de Entrada Restrita (se a insero for restrita a um nico extremo) e Fila Dupla de Sada Restrita (se a remoo for restrita a um nico extremo). LISTA

PILHA

FILA

FILA DUPLA

FDER FIGURA 3.1 Casos especiais de listas lineares 3.1.2 REPRESENTAES

FDSR

Existem vrias formas possveis de se representar internamente uma lista linear. A escolha de uma destas formas depender da freqncia com que determinadas operaes sero executadas sobre a lista, uma vez que algumas representaes so favorveis a algumas operaes, enquanto que outras no o so, no sentido de exigir um maior esforo computacional para a sua execuo. A seguir, sero discutidas as duas formas mais freqentes usadas para representar listas lineares: por alocao seqencial e por encadeamento dos ns. 3.1.3 REPRESENTAO DE LISTAS POR ALOCAO SEQENCIAL A representao por alocao seqencial explora a seqencialidade da memria do computador, de tal forma que os ns de uma lista sejam armazenados em endereos contguos, ou igualmente distanciados um do outro. Neste caso, a

23 relao de ordem entre os ns da lista representada pelo fato de que se o endereo do n xi conhecido, ento o endereo do n xi+1 pode ser determinado. Esquematicamente, a representao de uma lista linear por alocao seqencial tem a forma mostrada na Figura 3.2, abaixo: x1 x2 x3 xn-1 xn

... FIGURA 3.2 Esquema de representao por alocao seqencial Esta estrutura a mesma do agregado homogneo (vetor). Assim uma lista de N ns x1, x2, ..., xn definida da seguinte forma: tipo LISTA = vetor [1..N] de tipo; onde tipo o tipo de dado a ser representado em cada n da lista. Para, finalmente, declararmos uma lista do tipo acima definido, escrevemos: var X : LISTA; O comprimento de uma lista (quantidade de ns) pode se modificar durante a execuo do programa; assim, entenderemos uma lista como sendo um vetor com N elementos dentro de um vetor com M elementos, onde M N. A seguir so apresentados os algoritmos para implementar algumas das operaes mencionadas na seo 3.1.1, sobre listas lineares representadas por alocao seqencial. Todas as operaes sero baseadas em listas do tipo anteriormente definido. 3.1.4 ACESSAR O K-SIMO N DE UMA LISTA Esta operao pode ser implementada atravs de uma nica construo: X[K], que pode aparecer em uma expresso qualquer. Nesta operao pode ocorrer, entretanto, que K > FIM ou K 0; isto , uma tentativa de acessar um n que no existe na lista. Para prevenir esta situao, podemos implementar a operao atravs de uma funo que testa a validade de K e retorne um sinal que indique a ocorrncia destas situaes anmalas, da seguinte forma: i) se o retorna da funo F ento K 0 ou K > FIM; (neste caso a operao no foi executada); ii) se o retorna da funo V, ento 0 < K FIM; (neste caso a operao foi executada e VAL contm o dado do k-simo n). Esta conveno tambm ser usada nas demais operaes.funo ACESSAR (X : LISTA; K, FIM : inteiro; var VAL : tipo):lgico; se ((K FIM)) ento ACESSAR Falso; seno

24VAL X[K]; ACESSAR Verdadeiro; fim se; fim funo ACESSAR;

3.1.5 ALTERAR O VALOR DO K-SIMO N DE UMA LISTA funo ALTERAR (var X : LISTA; K, FIM :inteiro; VAL: tipo):lgico; se ((K FIM)) ento ALTERAR Falso; seno X[K] VAL; ALTERAR Verdadeiro; fim se; fim funo ALTERAR; 3.1.6 INSERIR UM NOVO N ANTES DO K-SIMO N DE UMA LISTA Neste procedimento suporemos que no vetor X haja pelo menos um elemento disponvel para acomodar o novo n; ou seja, assumiremos que FIM < M, onde M o comprimento do vetor X. funo INSERIR (var X : LISTA; K, var FIM :inteiro; VAL: tipo):lgico; var I : inteiro; se ((K FIM)) ento INSERIR Falso; seno para I = FIM at K faa {contador decrescente} X[I + 1] X[I]; fim para; FIM FIM + 1; X[K] VAL; INSERIR Verdadeiro; fim se; fim funo INSERIR;

25 3.1.7 REMOVER O K-SIMO N DE UMA LISTA funo REMOVER (var X : LISTA; K, var FIM :inteiro):lgico; var I : inteiro; se ((K FIM)) ento REMOVER Falso; seno para I = K at (FIM 1) faa X[I] X[I + 1]; fim para; FIM FIM - 1; REMOVER Verdadeiro; fim se; fim funo REMOVER; 3.1.8 REPRESENTAO DE LISTAS POR ENCADEAMENTO DOS NS Ao invs de manter os elementos agrupados numa rea contnua de memria, isto , ocupando clulas consecutivas, na alocao encadeada os elementos ocupam 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 de memria denominados ns, sendo que cada n composto por dois campos: um para armazenar dados e outro para armazenar endereo.. Dois endereos especiais devem ser destacados: o endereo do primeiro elemento da lista (L); o endereo do elemento fictcio que segue o ltimo elemento da lista (nil). L = 3FFA 1C34 BD2F 1000 3A7B 14F6 5D4A a1 1C34 a2 BD2F a3 1000 a4 3A7B a5 14F6 a6 5D4A a7 nil ltimo elemento da cadeia, o endereo nulo nil indica que o elemento a7 no tem um sucessor Cada n armazena um elemento e o endereo do prximo elemento da lista Primeiro elemento, acessvel a partir de L. Note que o segundo elemento no ocupa um endereo consecutivo quele ocupado por a1

26 A alocao apresenta como maior vantagem a facilidade de inserir ou remover elementos do meio da lista. Como os elementos no precisam estar armazenados em posies consecutivas de memria, nenhum dado precisa ser movimentado, bastando atualizar o campo de ligao do elemento que precede aquele inserido ou removido. Por exemplo, para remover o elemento a2 da lista representada anteriormente, basta mudar o n no endereo 3FFA de (a1, 1C34) para (a1, BD2F). Como apenas o primeiro elemento acessvel diretamente atravs do endereo L, a grande desvantagem da alocao encadeada surge quando desejamos acessar uma posio especfica dentro da lista. Neste caso, devemos partir do primeiro elemento e ir seguindo os campos de ligao, um a um, at atingir a posio desejada. Obviamente, para lista extensas, esta operao pode ter um alto custo em relao a tempo. 3.1.9 ALOCAO DINMICA DE MEMRIA As estruturas de dados vistas at o momento so organizadas de maneira fixa. Isto , criamos as variveis e estas contam com um tamanho fixo em memria. Arquivos permitem uma estrutura com um nmero indeterminado de elementos, porm sempre arrumados na forma de uma seqncia. O que devemos fazer quando tanto o nmero de elementos quanto sua forma de organizao variam dinamicamente? Para resolver este problema temos necessidade de um mecanismo que permita: criar espao para novas variveis em tempo de execuo; definir ligaes entre estas variveis, de uma forma dinmica. Variveis dinmicas no possuem nome prprio, portanto no so referenciadas por seus nomes. A referncia a uma varivel dinmica feita por ponteiros. Um ponteiro uma varivel cujo contedo o endereo de uma posio de memria. Um ponteiro declarado fornecendo-se o tipo de varivel por ele apontada. Ou seja, P um ponteiro para um tipo T se houver a declarao: P : ^T; Ao iniciar o programa, o valor de P estar indefinido. Existe uma constante predefinida, do tipo ponteiro, chamada nil que no aponta para objeto algum. Note que o significado de nil: no apontar para objeto algum diferente de indefinido que significa varivel no inicializada. P nil;

smbolo usado para representar o nil Figura 3.3 O valor nil A criao de uma varivel dinmica do tipo T feita pelo operador

27 aloque. Assim, o procedimento padro: aloque (P) cria uma varivel do tipo T, sem nome, e coloca em P o endereo desta varivel. Graficamente podemos indicar a gerao de uma varivel dinmica da seguinte forma:

P valor de P o endereo

P^ varivel do tipo T. (denota-se por P^)

Figura 3.4 Gerao de uma varivel dinmica Note que a varivel dinmica anterior referenciada como P^, que significa varivel apontada por P. A remoo de uma varivel criada dinamicamente, apontada por P, pode ser realizada atravs do seguinte comando: desaloque (P); 3.1.10 ENTENDENDO LISTAS REPRESENTADAS ATRAVS DE ALOCAO ENCADEADA DOS NS A estrutura de dados mais simples que pode ser obtida ligando elementos com ponteiros a lista. Na sua forma mais simples, uma lista uma seqncia de elementos encadeados por ponteiros. Esta seqncia pode ter um nmero indeterminado de elementos, cujo primeiro est sendo apontado por uma varivel apontador do incio da lista. Assim, podemos representar graficamente uma lista como: 9 PRIM Figura 3.5 Representao grfica de uma lista Neste exemplo podemos identificar: o apontador para o incio da lista que a varivel PRIM, cujo contedo o endereo do primeiro elemento; uma seqncia com 3 elementos: (9, 6, 12); cada elemento da lista tem dois campos: o primeiro um inteiro e o segundo um apontador para o elemento seguinte na lista; o ltimo elemento da lista no aponta para nenhum elemento e seu campo de 6 12

28 apontador tem o valor nil. 3.1.11 ROTINAS BSICAS DE TRATAMENTO DE LISTAS Vamos supor uma lista como a mostrada no exemplo acima. Ela pode ser definida como: tipo PONTEIRO = ^ELEMENTO; tipo ELEMENTO = registro CHAVE : inteiro; PROX : PONTEIRO; fim registro; var P, PRIM : PONTEIRO; Existem algumas operaes que podem ser realizadas com listas. Destacam-se a criao de uma lista, a procura de um elemento, a insero de um elemento, a retirada de um elemento e a varredura na lista, processando seus elementos. Adotaremos que nesta lista os elementos so inseridos na ordem inversa em que so obtidos. Para melhor compreendermos o problema vamos analisar um exemplo. Suponha a lista: 9 PRIM Figura 3.6 Exemplo de uma lista e desejamos acrescentar um elemento com chave = 5. Para isso devemos fazer: 6 12

1) criar um novo elemento apontado por P: aloque (P)

2) atualizar o campo chave: P^.CHAVE 5;

3) colocar o elemento no incio da lista: P^.PROX PRIM;

29

9 PRIM

6

12

5 P Figura 3.7 Insero de um novo elemento na lista 4) atualizar o incio da lista: PRIM P; 9 PRIM 6 12

5 P Figura 3.8 Atualizao do ponteiro PRIM completando a insero do novo elemento na lista Assim uma rotina para gerar uma lista de inteiros a partir de um arquivo de entrada, que contm nmeros inteiros, pode ser: procedimento CRIALISTA (MEUARQUIVO : ARQINTEIROS; var PRIM: PONTEIRO); var P : PONTEIRO; NUMERO : inteiro; abra (MEUARQUIVO); PRIM nil; enquanto no fda(MEUARQUIVO) faa aloque (P); copie (MEUARQUIVO, NUMERO); P^.CHAVE NUMERO; P^.PROX PRIM; PRIM P; avance (MEUARQUIVO); fim enquanto; fim procedimento CRIALISTA;

30 A funo abaixo procura um elemento, cujo campo chave seja igual ao dado passado como parmetro, retornando um ponteiro que aponta para aquele dado em memria. funo BUSCA (PRIM: PONTEIRO; DADO: inteiro): PONTEIRO; P: PONTEIRO; NAOACHOU: lgico; P PRIM; NAOACHOU V; enquanto (P nil) e (NAOACHOU) faa se P^.CHAVE = DADO ento NAOACHOU F seno P P^.PROX; fim se; fim enquanto; BUSCA P; fim funo BUSCA; Caso o elemento no exista na lista a funo BUSCA retornar nil. Para inserirmos um elemento na lista devemos considerar dois casos: a) a insero deve ser feita aps o elemento aponta por P; b) a insero deve ser feita antes do elemento apontado por P. Caso a - Insero feita aps o elemento apontado por P: Esta representao mostra a varivel P apontando para o elemento cujo campo de informao vale 3 e a varivel AUX, do mesmo tipo de P, apontando para um elemento a ser inserido. A insero implica fazer o campo PROX da varivel apontada por AUX apontar para o sucessor de P^ e fazer P^ apontar para o novo elemento. Ou seja: AUX^.PROX P^.PROX; P^.PROX AUX; Antes: P 5 3 1

PRIM

AUX

2 Figura 3.9 Insero feita aps um elemento apontado por P (Antes)

31 Depois: P 5 3 1

PRIM

AUX

2 Figura 3.10 - Insero feita aps um elemento apontado por P (Depois)

Caso b - Insero feita antes do elemento apontado por P: Procedemos da seguinte maneira: criamos um novo elemento, apontado por AUX. Inserimos esse elemento aps o apontado por P. Copiamos o campo de informao do elemento apontado por P para o novo elemento da lista. Colocamos a nova informao, que est na varivel DADO no elemento apontado por P. Ou seja: AUX^.PROX P^.PROX; P^.PROX AUX; AUX^.CHAVE P^.CHAVE; P^.CHAVE DADO; Antes: P 5 3 1

PRIM

AUX Figura 3.11 - Insero feita antes de um elemento apontado por P (Antes) Depois: P 5 4 1

PRIM

AUX

3

Figura 3.12 - Insero feita antes de um elemento apontado por P (Depois)

32 Para remover um elemento apontado por P procedemos de maneira similar ao que foi feito anteriormente. Seja a lista: Antes: PRIM 5 3 1 0

P P1 Figura 3.13 Remoo de um elemento apontado por P (Antes)

Depois: 5 3 1 0

PRIM

P P1 Figura 3.14 Remoo de um elemento apontado por P (Depois) Retirar o elemento cujo campo de informao vale 3 o mesmo que copiar para ele o campo de informao do elemento seguinte e retir-lo da lista. Usando-se uma varivel P1, do mesmo tipo de P, temos o seguinte trecho de algoritmo: P1 P^.PROX; P^.CHAVE P1^.CHAVE; P^.PROX P1^.PROX; desaloque (P1); Para percorrer a lista, processando os elementos, vamos considerar que o processamento de cada elemento feito pelo procedimento PROCESSA, que recebe como parmetro o campo de informaes do elemento. Fazemos P apontar para o incio da lista e enquanto houver elemento na lista, chama-se PROCESSA e atualiza-se o valor de P, que passa a apontar para o prximo elemento. P PRIM; enquanto P nil faa PROCESSA (P); P P^.PROX; fim enquanto;

33 3.1.12 LISTAS COM DESCRITOR Podemos simplificar a representao de uma lista se reunirmos, em um nico elemento, as referncias ao primeiro e ltimo elemento da lista. primeiro ltimo

Figura 3.15 Um n descritor A este elemento que rene as referncias ao incio e ao fim da lista damos a denominao de n descritor. O acesso aos elementos da lista ser sempre efetuado atravs do seu descritor. O n descritor de uma lista pode conter outras informaes sobre a lista, a critrio do projetista, tais como: quantidade de ns na lista, descrio dos dados contidos nos ns, etc. A figura, a seguir, mostra esquematicamente uma lista encadeada com n descritor, no qual foi includo um campo que indica a quantidade de ns existentes na lista. Nesta nova estrutura, a varivel PRIM aponta para o n descritor e no para o primeiro n da lista. PRIM

N

Figura 3.16 Uma lista encadeada com n-descritor

O n descritor, neste caso, um dado com a seguinte definio:

tipo DESCRITOR = registro I : PONTEIRO; N : inteiro; F : PONTEIRO; fim registro;

34 Uma lista vazia passa a ser representada, agora, da seguinte forma: PRIM

0

Figura 3.17 Representao de uma lista vazia, implementada com n descritor Usando esta nova estrutura de lista encadeada, passa a ser necessria a existncia de uma nova operao: criar uma lista vazia. Esta operao consiste em alocar um n do tipo descritor (PRIM), tornar seus dois ponteiros nulos e atribuir o valor zero ao campo N, gerando-se desta forma a situao mostrada na Figura 3.17. O procedimento criar, abaixo, implementa esta operao: procedimento CRIAR (var PRIM : ^DESCRITOR) aloque (PRIM); PRIM^.I nil; PRIM^.N 0; PRIM^.F nil; fim procedimento CRIAR; A seguir so apresentados outros procedimentos para manipulao de listas encadeadas com descritor. O primeiro procedimento, INSERE_ESQ, implementa a operao de insero de um n com o dado VALOR esquerda da lista cujo descritor apontado pela varivel PRIM. Procedimento INSERE_ESQ (PRIM: ^DESCRITOR; VALOR : inteiro); var P : ^ELEMENTO; aloque (P); P^.CHAVE VALOR; se PRIM^.N PRIM^.I PRIM^.F PRIM^.N P^.PROX seno P^.PROX PRIM^.I PRIM^.N fim se; = 0 ento {testa se a lista est vazia} P; P; 1; nil; PRIM^.I; P; PRIM^.N + 1;

fim procedimento INSERE_ESQ;

35 O procedimento seguinte, INSERE_DIR, implementa a operao de insero de um n com o dado VALOR direita da lista cujo descritor apontado por PRIM. Procedimento INSERE_DIR (PRIM: ^DESCRITOR; VALOR : inteiro); var P, Q : ^ELEMENTO; aloque (P); P^.CHAVE VALOR; P^.PROX nil; se PRIM^.N = 0 ento {testa se a lista est vazia} PRIM^.I P; PRIM^.F P; PRIM^.N 1; seno Q PRIM^.F; PRIM^.F P; Q^.PROX P; PRIM^.N PRIM^.N + 1; fim se; fim procedimento INSERE_DIR; Para remover o n da esquerda de uma lista, podemos utilizar o procedimento REMOVE_ESQ, a seguir apresentado. Este procedimento remove o primeiro n da lista, se houver, e retorna o dado que o n removido continha atravs do parmetro VALOR.Procedimento REMOVE_ESQ (PRIM: ^DESCRITOR; var VALOR : inteiro); var P : ^ELEMENTO; se PRIM^.N = 0 ento {testa se a lista est vazia} ERRO(3); {chama rotina que trata o erro} seno P PRIM^.I; VALOR P^.CHAVE; PRIM^.I P^.PROX; PRIM^.N PRIM^.N 1; desaloque (P); se PRIM^.N = 0 ento {testa se a lista ficou vazia} PRIM^.F nil; fim se; fim se; fim procedimento REMOVE_ESQ;

36 A operao de remoo do n direita da lista com descritor envolve o mesmo problema apresentado para o caso de lista sem descritor: a necessidade de percorrer todos os ns da lista, seqencialmente, a partir do primeiro (da esquerda), at atingir o n da direita. A fim de evitar a necessidade deste caminhamento, podemos estruturar uma lista linear encadeada de forma que o caminhamento sobre a mesma possa ser feito em ambos os sentidos. Uma organizao que implementa este recurso denominada de lista linear duplamente encadeada. 3.1.13 LISTAS DUPLAMENTE ENCADEADAS Uma lista linear duplamente encadeada aquela em que cada n possui dois ponteiros, ao invs de um s. O primeiro usado para indicar o n predecessor, enquanto que o segundo aponta para o n sucessor. A Figura 3.18 mostra esquematicamente uma lista linear duplamente encadeada com 3 ns. PRIM

3

5

13

19

Figura 3.18 Uma lista linear duplamente encadeada com 3 ns A definio do tipo dos ns de uma lista duplamente encadeada feita da seguinte forma: tipo ELEMENTO = registro ESQ : PONTEIRO; CHAVE : inteiro; DIR : PONTEIRO; fim registro; Se unirmos as duas extremidades livres da lista, obteremos uma lista circular , conforme mostrado na Figura 3.19. Em uma lista circular, cada n satisfaz a seguinte condio: (P^.DIR)^.ESQ = P = (P^.ESQ)^.DIR onde P a referncia a um n qualquer da lista. Agora, com a nova organizao proposta, a implementao da operao de remoo do n da direita fica simplificada, no sentido de no ser mais necessrio

37 o caminhamento linear sobre a lista. PRIM

3

5

13

19

Figura 3.19 Representao de um lista circular com 3 ns O procedimento REMOVE_DIR, apresentado a seguir, implementa esta operao, sobre uma lista circular.procedimento REMOVE_DIR (PRIM: ^DESCRITOR; var VALOR : inteiro); var P, Q, R : ^ELEMENTO; se PRIM^.N = 0 ento {testa se a lista est vazia} ERRO(3); {chama rotina que trata o erro} seno P PRIM^.F; {se lista no vazia, remove} VALOR P^.CHAVE; se PRIM^.N = 1 ento {testa se a lista tem s um n} PRIM^.I nil; PRIM^.F nil; seno Q P^.ESQ; R PRIM^.I; Q^.DIR P^.DIR; R^.ESQ Q; PRIM^.F Q; fim se; PRIM^.N PRIM^.N 1; desaloque (P); fim se; fim procedimento REMOVE_DIR;

38 3.2 PILHAS A pilha uma das estruturas 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 todas 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 de estas listas serem tambm denominadas LIFO (Last-In/First-Out). O exemplo mais comum do quotidiano uma pilha de pratos, onde o ltimo prato colocado o primeiro a ser usado (removido). Uma pilha suporta trs operaes bsicas, tradicionalmente denominadas como: Top: acessa o elemento posicionado no topo da pilha; Push: insere um novo elemento no topo da pilha; Pop: remove um elemento do topo da pilha.

Push

Pop Top

Figura 3.20 Uma pilha de pratos 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 no 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. Observe a seguir, como estas operaes interagem para alterar o estado de uma pilha P, inicialmente vazia, cuja extremidade esquerda foi escolhida como topo. Operao Push (P, a) Push (P, b) Push (P, c) Pop (P) Pop(P) Push (P, d) Push (P, e) Estado da Pilha P: [ ] P: [a] P: [b, a] P: [c, b, a] P: [b, a] P: [a] P: [d, a] P: [e, d, a] P: [e, d, a] Resultado c b

39 Operao Top (P) Pop (P) Pop(P) Estado da Pilha P: [d, a] P: [a] Resultado e e d

Para melhor visualizao, ao invs de utilizar a notao de lista linear, geralmente as pilhas so representadas na forma de um grfico, crescendo na vertical, de baixo para cima, conforme o esquema a seguir:

an an-1 P: [an, an-1, ..., a2, a1] ... a2 a1

topo

base

Figura 3.21 Notao linear e grfica de pilhas Temos a seguir, representados na forma grfica, os sucessivos estados que uma pilha assume quando novos elementos so nela colocados ou dela retirados.

c b a a b a Push (P, c) b a Pop (P) a Pop (P)

Push (P, a) Push (P, b)

e d a d a d a Pop (P) a Pop (P) Pop (P)

Push (P, d) Push (P, e)

Figura 3.22 Sucessivos estados de uma pilha na notao grfica Note que a representao de pilha na forma de lista linear sugere que a posio de topo seja fixa. Isto torna necessrio empurrar os elementos para baixo sempre que um novo elemento for colocado na pilha. Ao remover o elemento do topo, os demais elementos devem ser puxados de volta para cima. J na representao grfica, fica implcito que, na verdade, a posio de topo que se

40 movimenta toda vez que a pilha cresce ou diminui. 3.2.1 INIT, ISEMPTY E ISFULL Imagine uma pilha de pratos sobre uma mesa, dentro de uma sala. Seria possvel colocar novos pratos indefinidamente sobre ela? Obviamente, no! Em algum momento o prato do topo da pilha tocaria o teto da sala. E o contrrio: seria possvel remover pratos do topo da pilha indefinidamente? Tambm no! Em algum momento a pilha tornar-se-ia vazia. A mesa e o teto so limites fsicos que impedem que a pilha cresa ou diminua indefinidamente. teto altura da parede

pratos mesa cho

Figura 3.23 Limitaes fsicas de uma pilha No exemplo da Figura 3.23, antes de comearmos a empilhar os pratos sobre a mesa, devemos garantir que a mesa estar limpa, isto , que no existem panelas, talheres ou qualquer outro objeto no local onde colocaremos os pratos. Para adicionar um prato pilha, primeiro verificamos se ainda existe espao entre o topo da pilha e o teto da sala. Finalmente, para remover, precisamos nos certificar de que ainda existem pratos sobre a mesa. Assim, precisamos de mais trs operaes essenciais para manipular pilhas: Init: inicializa a pilha no estado vazia; IsEmpty: verifica se a pilha est vazia; IsFull: verifica se a pilha est cheia. Sempre que uma varivel criada, ela permanece com contedo indefinido, at que um determinado valor seja a ela atribudo. Geralmente, a criao de uma varivel se restringe apenas alocao da rea de memria necessria para represent-la; nenhum valor inicial armazenado nesta rea, at que uma instruo especfica para esta finalidade seja executada. No caso de uma varivel do tipo pilha, isto no diferente. A operao Init(P) tem como objetivo definir um estado inicial para a pilha P. Por uma questo de bom senso, uma pilha sempre inicializada no estado vazia. Toda vez que criamos uma varivel pilha, antes de qualquer coisa, devemos inicializ-la para garantir que no haver nenhuma sujeira no local onde ela ser montada! Para verificarmos se uma pilha P est vazia, podemos usar a funo lgica IsEmpty(P), que toma como argumento a pilha em que estamos interessados e retorna verdadeiro somente se ela estiver vazia, sem nenhum elemento armazenado. A funo IsFull(P) usada para verificar se uma pilha est cheia, isto , ela retorna verdadeiro somente quando no h mais espao para armazenar

41 elementos na pilha. 3.2.2 UM PRIMEIRO EXEMPLO DO USO DE PILHAS Este primeiro exemplo objetiva mostrar como um programa completo, que utiliza o tipo pilha, pode ser escrito. O programa simplesmente pede ao usurio que digite um nmero inteiro positivo em decimal e, em seguida, mostra o nmero no sistema binrio. Para entender a lgica do programa, lembre-se de que para converter um nmero inteiro da base decimal para a binria, devemos dividi-lo sucessivamente por 2, at obtermos um quociente igual a 0. Neste momento, os restos obtidos nas divises devem ser tomados em ordem inversa. Veja o exemplo: 13 -12 1 2 6 6 -6 0 2 3 3 -2 1 2 1 1 -0 1 2 0

Assim, o valor 13 decimal fica 1101 em binrio. No programa a seguir, a pilha utilizada para armazenar os restos obtidos, de modo que depois eles possam ser recuperados no ordem inversa em que foram gerados.

program DEC_BIN; uses PILHAS; var P : Pilha; x, n : integer; begin writeln (Digite um inteiro decimal positivo: ); readln(n); Init(P); {torna a pilha vazia} repeat x := n mod 2; Push(P, x); n := n div 2; until (n = 0);

{calcula o {empilha o {calcula o {quociente

resto} resto} quociente} 0, pra}

write (Correspondente ao binrio: ); while (not IsEmpty(P)) do begin {pilha vazia, pra} x := Pop(P); {desempilha o resto} write (x); {imprime o resto} end; end.

42 Exerccios: 7) Escreva os algoritmos para compor uma biblioteca para manipulao de pilhas. Esta biblioteca deve conter os seguintes procedimentos e funes: Init, IsEmpty, IsFull, Top, Push e Pop. 3.2.3 IMPLEMENTAO SEQENCIAL DE PILHAS Como os elementos da pilha so armazenados em seqncia, um sobre o outro, e a incluso ou excluso de elementos no requer movimentao de dados, o esquema de alocao seqencial de memria mostra-se bastante apropriado para implement-las. Neste esquema, a forma mais simples de se representar um a pilha na memria consiste em: um vetor, que serve para armazenar os elementos contidos na pilha; um ndice, utilizado para indicar a posio corrente de topo da pilha. Para agrupar estas duas partes e formar a estrutura coesa que a pilha, usaremos o registro: const MAX = 50; tipo ELEM = caractere; PILHA = registro TOPO : inteiro; MEMO : vetor [1..MAX] de ELEM; fim registro; var P : PILHA; As duas primeiras linhas de cdigo tm como objetivo tornar a implementao o mais independente possvel dos tipos e quantidades de dados manipulados. Por exemplo, se desejssemos uma pilha com capacidade de armazenar at 200 valores reais, bastaria fazer duas pequenas alteraes nas linhas 1 e 2, e nenhum algoritmo precisaria ser alterado: const MAX = 200; tipo ELEM = real;

P.MEMO armazena os elementos da pilha

P: 3

a 1

b 2

c 3 4 5 6

... 7 ... MAX

P.TOPO indica a posio do ltimo elemento inserido

Figura 3.24 Estrutura de armazenamento da Pilha P: [c, b, a]

43 Como uma pilha um registro, podemos acessar seus componentes individuais, usando o operador ponto (.) e o nome do campo em que estamos interessados. 3.2.4 ALGORITMOS PARA MANIPULAO DE PILHAS Como sabemos, implementar um tipo de dados no se resume apenas em especificar a estrutura sob a qual os dados sero mantidos na memria, mas requer tambm o desenvolvimento dos algoritmos que descrevem o funcionamento das operaes bsicas sobre aquela estrutura.

Inicializando a Pilha:

Inicializar a pilha atribuir 0 TOPO, pois 0 uma posio inexistente no vetor e, alm disto, pode ser mudado facilmente para 1 quando o primeiro elemento for inserido na pilha; ou retornar a 0 quando o ltimo elemento for dela removido. procedimento INIT (var P: PILHA); P.TOPO 0; fim procedimento INIT;

Verificando Limites:

Na implementao seqencial, a checagem de pilha vazia ou cheia extremamente simples! Se acabamos de inicializar uma pilha P, usando a operao Init(P), claro que a operao que testa a pilha vazia, IsEmpty(P), dever retornar um valor lgico verdadeiro. funo ISEMPTY(P: PILHA): lgico; se (P.TOPO = 0) ento ISEMPTY Verdadeiro; seno ISEMPTY Falso; fim se; fim funo ISEMPTY; A operao que testa se a pilha est cheia ou no, tambm bastante simples. Se o campo TOPO usado para registrar a posio do elemento que ocupa o topo da pilha, claro que a pilha estar cheia quando o elemento do topo estiver armazenado na ltima posio do vetor MEMO, representada pela constante MAX.

44 funo ISFULL(P: PILHA): lgico; se (P.TOPO = MAX) ento ISFULL Verdadeiro; seno ISFULL Falso; fim se; fim funo ISFULL; Empilhando um Elemento:

Para inserir um novo elemento na pilha, primeiro verificamos se ainda existe espao. Existindo, temos que colocar o novo elemento no topo da pilha. procedimento PUSH (var P: PILHA; X: ELEM); se (no ISFULL(P)) ento P.TOPO P.TOPO + 1; P.MEMO[P.TOPO] X; seno escreva Stack Overflow! fim se; fim procedimento PUSH; Desempilhando um Elemento:

Para retirar um elemento de uma pilha, primeiro temos que nos certificar de que ela no se encontra vazia. Caso a pilha no esteja vazia, ento o elemento que est no topo dever ser retornado como resultado da operao e a varivel P.TOPO dever ser decrementada. funo POP (var P: PILHA): ELEM; se (no ISEMPTY(P)) ento POP P.MEMO[P.TOPO]; P.TOPO P.TOPO - 1; seno escreva Stack Underflow! fim se; fim funo POP; Obtendo o Valor do Elemento do Topo:

Algumas vezes, temos a necessidade de apenas observar o elemento que se encontra no topo da pilha. A operao TOP(P) nos permite fazer tal acesso,

45 sem alterar o estado da pilha. funo TOP (P: PILHA): ELEM; se (no ISEMPTY(P)) ento TOP P.MEMO[P.TOPO]; seno escreva Stack Underflow! fim se; fim funo TOP;

3.3 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 onde so removidos denominado comeo da fila. As filas so tambm denominadas listas FIFO (FirstIn/First-Out). 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 de dados abstrato no suporta insero nem remoo no meio da lista. Incio final sai entra Figura 4.25 Uma fila de caixa bancrio A palavra queue, da lngua inglesa, significa fila. Por tradio, as duas operaes bsicas que uma fila suporta so denominadas como a seguir: Enqueue: insere um elemento no final da fila; Dequeue: 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 a fila diminuir, j que remove e retorna o elemento posicionado no seu comeo. Operao Enqueue (F, a) Enqueue (F, b) Enqueue (F, c) Enqueue (F, d) Dequeue (F) Dequeue (F) Estado da Fila F: [ ] F: [a] F: [a, b] F: [a, b, c] F: [a, b, c, d] F: [b, c, d] F: [c, d] Resultado a

46 Operao Enqueue (F, e) Enqueue (F, f) Enqueue (F, Dequeue (F)) Dequeue (F) Dequeue (F) Dequeue (F) Estado da Fila F: [c, d, e] F: [c, d, e, f] F: [d, e, f] F: [d, e, f, c] F: [e, f, c] F: [f, c] F: [c] Resultado b c d e f

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

Figura 3.26 Representao grfica da fila F: [a, b, c, d] Intuitivamente, a partir da representao grfica, percebemos que possvel implementar uma fila tendo trs recursos bsicos: Espao de memria para armazenar os elementos; Uma referncia ao primeiro elemento da coleo; Uma referncia primeira posio livre, aps o ltimo elemento da fila. O espao de memria seqencial pode ser alocado por meio de um vetor. Para referenciar o primeiro elemento e a primeira posio disponvel no final da fila, podemos utilizar duas variveis inteiras que sirvam como ndice aos elementos do vetor. const MAX = 50; tipo ELEM = caractere; FILA = registro COMECO : inteiro; FINAL: inteiro; MEMO : vetor [1..MAX] de ELEM; fim registro; var F : FILA; Observe que somente na oitava linha que uma varivel do tipo Fila foi realmente criada. Veja, na Figura 3.27, como ficaria a estrutura de armazenamento da fila F: [a, b, c]. A primeira operao a ser definida, deve ser aquela que inicializa a fila, indicando que ela se encontra vazia. Considerando a forma escolhida para

47 representar a fila (figura anterior), podemos verificar que, medida que novos elementos vo sendo inseridos, o ndice F.FINAL vai se deslocando para a direita. Analogamente, quando um elemento removido, o ndice F.COMECO persegue F.FINAL. A conseqncia disto que, conforme os elementos vo entrando e saindo da fila, ela vai se movendo gradativamente para a direita. Particularmente, quando a fila no tiver mais nenhum elemento, o ndice F.COMECO ter alcanado o ndice F.FINAL, isto , teremos F.COMECO igual a F.FINAL. F.FINAL F.COMECO F: 1 4 a 1 b 2 F.MEMO c 3 4 MAX

Figura 3.27 Estrutura de armazenamento da fila F: [a, b, c] Como desejamos uma fila inicialmente vazia e F.COMECO igual a F.FINAL indica esta situao, bastaria atribuir qualquer valor inteiro k aos dois ndices, tornando-os iguais. Entretanto, convencionamos que F.FINAL apontaria a posio livre onde deveria ser armazenado um elemento que estivesse entrando na fila. Desta forma, a melhor escolha k = 1. Indicamos que a fila est vazia e, ao mesmo tempo, que a posio disponvel para insero a primeira posio do vetor F.MEMO. procedimento QINIT (var F: FILA); F.COMECO 1; F.FINAL 1; fim procedimento QINIT; Note que a fila estar vazia sempre que F.COMECO for igual a F.FINAL, mesmo que o valor destes ndices seja k 1. Adicionalmente, a fila estar cheia quando tivermos F.FINAL > MAX, isto , quando no existir uma posio livre aps o ltimo elemento da fila. funo QISEMPTY(var F: FILA): lgico; QISEMPTY (F.COMECO = F.FINAL); fim funo QISEMPTY;

funo QISFULL(var F: FILA): lgico; QISFULL (F.FINAL > MAX); fim funo QISFULL;

48 Para adicionar um elemento fila, primeiramente precisamos verificar se existe espao suficiente; isto feito facilmente com a funo QISFULL(). Caso exista, devemos lembrar que o ndice F.FINAL aponta justamente a posio onde dever entrar o novo elemento. Aps o elemento ter sido armazenado no final da fila, o ndice F.FINAL deve ser atualizado para que passe a indicar a prxima posio disponvel. Como estamos utilizando uma rea seqencial para armazenar os elementos, basta incrementar o valor de F.FINAL para que ele aponte a prxima posio livre no vetor. procedimento ENQUEUE (var F: FILA; X: ELEM); se (no QISFULL(F)) ento F.MEMO[F.FINAL] X; F.FINAL F.FINAL + 1; seno escreva Fila cheia; fim se; fim procedimento ENQUEUE; Para remover, lembramos que F.COMECO aponta o elemento que deve ser atendido primeiro, caso exista um. Aps o elemento ter sido removido, o ndice F.COMECO deve ser atualizado para apontar o prximo elemento a ocupar o incio da fila. funo DEQUEUE (var F: FILA): ELEM; se (no QISEMPTY(F)) ento DEQUEUE F.MEMO[F.COMECO]; F.COMECO F.COMECO + 1; seno escreva Fila vazia; fim se; fim funo DEQUEUE; 3.3.2 PROBLEMAS NA IMPLEMENTAO SEQENCIAL DE FILAS Suponha uma fila F: [a, b, c, d, e]. De acordo com a nossa implementao, e admitindo que a fila pode armazenar no mximo 5 elementos (MAX = 5), podemos representar a fila F como a seguir: F: 1 6 a 1 b 2 c 3 d 4 e 5

Figura 3.28 Uma fila cheia

49 Vimos que cada vez que um elemento removido, o ndice que aponta o comeo da fila desloca-se uma posio direita. Se inicialmente ele vale 1, como observamos na figura anterior, aps a remoo de todos os elementos da fila F, teremos a situao esquematizada a seguir:

F:

1

6

a 1

b 2

c 3

d 4

e 5

Figura 3.29 Uma fila cheia ou vazia? Se tentarmos inserir um novo elemento na fila, no conseguiremos, pois a funo QISFULL() indica que no existe mais espao disponvel (F.FINAL > MAX). Por outro lado, remover tambm no possvel, pois QISEMPTY() indica que a fila est vazia (F.COMECO = F.FINAL). Resumindo, chegamos a uma situao extremamente indesejvel. Temos uma fila que est 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. 3.3.3 SOLUCIONANDO OS PROBLEMAS DA IMPLEMENTAO SEQENCIAL Eliminar o erro lgico, que sinaliza fila vazia e cheia ao mesmo tempo, bastante simples. Basta acrescentar uma varivel contadora para indicar quantos elementos esto armazenados na fila. Esta varivel deve estar inicialmente zerada. Quando um elemento for inserido, ela ser incrementada; quando for removido, ela ser decrementada. Desta forma, o impasse pode ser resolvido simplesmente consultando tal varivel. F: 3 2 5 a 1 b 2 c 3 d 4

5

Figura 3.30 A fila F: [b, c, d] com varivel contadora Para eliminar o desperdcio de espao, o ideal seria que cada posio liberada por um elemento removido se tornasse prontamente disponvel para receber um novo elemento inserido. Para isto, teramos de dispor de uma rea seqencial de memria tal que a posio 1 estivesse imediatamente aps a posio MAX. Assim, QISFULL() somente indicaria fila cheia quando realmente todo o espao de armazenamento estivesse esgotado. Se fosse possvel alocar uma rea de memria como ilustra a Figura 3.31; ento um ndice i, com valor MAX + 1, estaria apontando para a primeira posio do vetor. Da mesma forma, um ndice j, com valor MAX + 2, estaria apontando a segunda posio e assim por diante... Entretanto, as clulas de memria seguem uma organizao linear e, para obter esta circularidade, deveremos lanar mo de

50 um artifcio: sempre que um ndice for incrementado e seu valor ultrapassar a constante MAX, restabelecemos seu valor a 1. A rotina ADC(), definida a seguir, serve para simular esta circularidade. MAX a b c e 5 d 4 2 1 comeo

6 final

3

Figura 3.31 Uma fila na representao circular procedimento ADC (var I: inteiro); I I + 1; se I > MAX ento I 1; fim procedimento ADC; 3.3.4 IMPLEMENTAO CIRCULAR PARA FILAS Temos, a seguir, a implementao circular completa para o tipo de dados Fila. As rotinas apresentadas so basicamente idnticas quelas apresentadas na implementao seqencial, exceto pelos detalhes discutidos na seo anterior. const MAX = 50; tipo ELEM = caractere; FILA = registro TOTAL : inteiro; COMECO : inteiro; FINAL: inteiro; MEMO : vetor [1..MAX] de ELEM; fim registro; var F : FILA; procedimento QINIT (var F: FILA); F.TOTAL 0; F.COMECO 1;

51 F.FINAL 1; fim procedimento QINIT; funo QISEMPTY(var F: FILA): lgico; QISEMPTY (F.TOTAL = 0); fim funo QISEMPTY; funo QISFULL(var F: FILA): lgico; QISFULL (F.TOTAL = MAX); fim funo QISFULL; procedimento ADC (var I: inteiro); I I + 1; se I > MAX ento I 1; fim procedimento ADC; procedimento ENQUEUE (var F: FILA; X: ELEM); se (no QISFULL(F)) ento F.MEMO[F.FINAL] X; ADC(F.FINAL); F.TOTAL F.TOTAL + 1; seno escreva Fila cheia; fim se; fim procedimento ENQUEUE; funo DEQUEUE (var F: FILA): ELEM; se (no QISEMPTY(F)) ento DEQUEUE F.MEMO[F.COMECO]; ADC(F.COMECO); F.TOTAL F.TOTAL - 1; seno escreva Fila vazia; fim se; fim funo DEQUEUE;

52 3.4 RECURSIVIDADE 3.4.1 INTRODUO Um algoritmo que para resolver um problema divide-o em subproblemas mais simples, cujas solues requerem a aplicao dele mesmo, chamado recursivo. Em termos de programao, uma rotina recursiva quando ela chama a si mesma, seja de forma direta ou indireta. Em geral, uma rotina recursiva R pode ser expressa como uma composio formada por um conjunto de comandos C (que no contm chamadas a R) e uma chamada (recursiva) rotina R:

R [C , R ]Figura 3.32 - Recurso Direta Entretanto, pode-se ter tambm uma forma indireta de recurso, na qual as rotinas so conectadas atravs de uma cadeia de chamadas sucessivas que acaba retornando primeira que foi chamada:

R1 [C1 , R 2 ]

R2 [C 2 , R3 ] R3 [C 3 , R4 ]

M Rn [C n , R1 ]Figura 3.33 - Recurso Indireta Assim, diz-se que uma rotina R indiretamente recursiva se ela contm uma chamada a outra rotina S, que por sua vez contm uma chamada direta ou indireta a R. Note que a recurso nem sempre explcita e, s vezes, pode ser difcil perceb-la atravs de uma simples leitura da rotina. A recurso uma tcnica particularmente poderosa em definies matemticas. Alguns exemplos familiares so: nmeros naturais e certas funes: Nmeros naturais: (a) 0 um nmero natural; (b) o sucessor de um nmero natural um nmero natural. A funo fatorial n! (para inteiros no negativos): (a) 0! = 1 (b) n > 0: n! = n * (n - 1)!

O poder da recurso deve-se, evidentemente, possibilidade de se definir um conjunto infinito de objetos atravs de uma formulao finita. Do mesmo modo,

53 um nmero infinito de clculos pode ser definido por um programa recursivo finito, ainda que este no contenha repeties explcitas. Algoritmos recursivos, no entanto, so especialmente adequados quando o problema a ser resolvido, a funo a ser calculada ou a estrutura de dados a ser processada j estejam definidos sob uma forma recursiva. 3.4.2 USO DE RECURSO NA SOLUO DE PROBLEMAS Todo algoritmo deve ser executado em tempo finito, isto , deve terminar aps ter executado uma quantidade finita de passos. Para garantir que uma chamada recursiva no criar um looping que ser executado infinitamente, necessrio que ela esteja condicionada a uma expresso lgica que, em algum instante, tornar-se- falsa e permitir que a recurso termine. Assim, uma rotina recursiva melhor representada por R [C , T R ] , onde T R indica que a rotina R somente ser chamada se o teste T for satisfeito. A tcnica bsica para garantir o trmino da execuo de um algoritmo recursivo consiste em: Exprimir T em termos de uma funo f(x), tal que f(x) 0 implica uma condio de parada; mostrar que f(x) decresce a cada passo de repetio, isto , que temos a forma R( x ) [C , ( f ( x) > 0) R(x 1)] , onde x decresce a cada chamada. Na prtica, ao definir uma rotina recursiva, dividimos o problema da seguinte maneira: Soluo Trivial: dada por definio; isto , no necessita da recurso para ser obtida. Esta parte do problema resolvida pelo conjunto de comandos C. Soluo Geral: parte do problema que em essncia igual ao problema original, sendo porm menor. A soluo, neste caso, pode ser obtida por uma chamada recursiva R(x-1) Para decidir se o problema ter soluo trivial ou geral; isto , se sua soluo ser obtida pela execuo do conjunto de instrues C ou pela chamada recursiva R(x-1), usamos um teste. Por exemplo, vamos definir uma funo recursiva para calcular o fatorial de um nmero natural: Soluo Trivial: 0! = 1 {dada por definio} Soluo Geral: n! = n * (n - 1)! {requer reaplicao da rotina para (n - 1) !} Considerando f(n) = n, ento n = 0 implica numa condio de parada do mecanismo recursivo, garantindo o trmino do algoritmo que calcula o fatorial: funo FAT (N: inteiro): inteiro; se (N = 0) ento FAT 1 seno FAT N * FAT (N - 1); fim se; fim funo FAT; Em termos matemticos, a recurso uma tcnica que, atravs de

54 substituies sucessivas, reduz o problema a ser resolvido a um caso de soluo trivial. Veja o clculo de f(4) na figura a seguir: FAT(4) 4*FAT(3) 4*3*FAT(2) 4*3*2*FAT(1) 4*3*2*1*FAT(0) 4*3*2*1*1

Problema a resolver

Substituies sucessivas

Caso de soluo trivial obtido !

Figura 3.34 - Recurso vista como substituies sucessivas 3.4.3 QUANDO APLICAR RECURSO? Embora a recurso seja uma ferramenta bastante interessante para resoluo de problemas, nem sempre ela poder ser empregada. Enquanto alguns problemas tm soluo imediata com o uso de recurso, outros so praticamente impossveis de se resolver de forma recursiva. preciso analisar o problema e verificar se realmente vale a pena tentar encontrar uma soluo recursiva. A recurso, se bem utilizada, pode tornar um algoritmo muito elegante; isto , claro, simples e conciso. Porm, na maioria das vezes, uma soluo iterativa (no recursiva) ser mais eficiente. Cada chamada recursiva implica em um custo tanto de tempo quanto de espao, pois cada vez que a rotina chamada, todas as variveis locais so recriadas. Assim, determinar precisamente se devemos usar recurso ou no, ao resolver um problema, uma questo bastante difcil de ser respondida. Para isto teramos que comparar as possveis solues recursiva e iterativa. 3.4.4 ELIMINANDO A RECURSO DE CAUDA Dizemos que uma rotina apresenta recurso de cauda se a chamada recursiva est no final do seu cdigo, tendo como nica funo criar um looping que ser repetido at que a condio de parada seja satisfeita. Vejamos, por exemplo, a funo para clculo de fatorial:

funo FAT (N: inteiro): inteiro; se (N = 0) ento FAT 1 seno FAT N * FAT (N - 1); fim se; fim funo FAT;

Looping!

Observe que a principal funo da chamada FAT (N - 1) criar um looping que se repete at que a condio de parada seja satisfeita. Infelizmente, este exemplo clssico de recurso (clculo de fatorial) um dos casos onde a recurso menos eficiente que a iterao, justamente porque apresenta recurso de cauda.

55 Geralmente, se uma rotina R(x) tem como ltima instruo a chamada recursiva R(y), ento podemos trocar R(y) pela atribuio x y, seguida de um desvio para o incio do cdigo de R. Isto funciona porque reexecutar R, para o novo valor de x, tem o mesmo efeito de chamar R(y). A recurso de cauda pode ser eliminada se empregarmos no seu lugar, uma estrutura de repetio que esteja condicionada expresso de teste usada na verso recursiva. Veja como isto pode ser feito na funo que calcula o fatorial: funo FAT_ITERATIVO (N: inteiro): inteiro; var F : inteiro; F 1; enquanto (N > 0) faa F F * N; N N - 1; fim enquanto; FAT_ITERATIVO F; fim funo FAT; Embora a funo fatorial seja mais eficientemente implementada de forma iterativa, ela ainda um excelente exemplo para se entender o que recurso. 3.4.5 PILHAS E ROTINAS RECURSIVAS O controle de chamadas e retornos de rotinas feito atravs de uma pilha criada e mantida, automaticamente, pelo sistema. Na verdade, quando uma rotina chamada, no apenas o endereo de retorno empilhado, mas todas as suas variveis locais so tambm recriadas na pilha. Por exemplo, ainda na verso recursiva da funo fatorial, para a chamada FAT(4) teramos: 0 1 2 3 4 FAT(0)

n n n n n 4 3 4 FAT(3) 2 3 4 FAT(2) 1 2 3 4 FAT(1)

FAT(4)

Figura 3.35 - Varivel local n sendo recriada na pilha a cada chamada recursiva Quando a rotina comea a ser executada, a varivel n criada no topo da pilha e inicializada com o valor que foi passado no momento da chamada; quando termina, a ltima cpia criada para a varivel deixa de existir. A qualquer instante durante a execuo, o valor assumido para a varivel aquele existente no topo da

56 pilha. Assim, utilizamos a memria para armazenar todos os argumentos intermedirios e valores de retorno na pilha interna do sistema. Isto pode causar problemas se h uma grande quantidade de dados, levando a estouro da pilha. Basicamente, qualquer tipo de recurso pode ser eliminado se utilizarmos no seu lugar comandos de repetio e, eventualmente, pilhas. Normalmente, as rotinas assim modificadas sero mais rpidas que suas correspondentes em verso recursiva. Entretanto, se o uso de muitos comandos de repetio e vrias pilhas for necessrio para realizar a converso da verso re