Você está na página 1de 34

- ESCON -

ESCOLA DE CURSOS ONLINE


CNPJ: 11.362.429/0001-45
Av. Antônio Junqueira de Souza, 260 - Centro
São Lourenço - MG - CEP: 37470-000
MATERIAL DO CURSO
LÓGICA DE PROGRAMAÇÃO

APOSTILA
LISTAS LINEARES
INTRODUÇÃO

A lista linear é uma forma de representação, como estrutura linear, onde o


objetivo é que a representação de dados de um problema no computador seja
de tal maneira, que o algoritmo que os utilize seja auxiliado em sua tarefa de
obter uma solução de forma confiável e eficiente.

Para a compreensão do funcionamento da lista, é necessário compreendermos


o funcionamento da memória do computador.

A memória do computador é um recurso que pode ser acessado de duas formas


por um programa:

 Primeiro, um algoritmo pode alocar estruturas de dados no que chamaremos


de memória estática, alocação esta que deve ser feita antes de sua execução;

 Segundo, um algoritmo pode utilizar-se da memória dinâmica do computador,


alocando estruturas na medida do necessário durante a execução do algoritmo.

Em ambos os casos, podemos imaginar que a memória é organizada como uma


série seqüencial de células. Associados a cada célula estão dois atributos: o
endereço do inicio da célula e seu tamanho. As células não precisam ser de
tamanho uniforme.

O tamanho de uma célula é dado numa unidade peculiar ao equipamento em


questão, mas geralmente a unidade fundamental é o byte, que é o espaço
necessário para representar um caractere, por exemplo, a letra “a”. Um byte é
composto de 8 bits, sendo que um bit é um digito binário (0 ou 1). Tudo que é
representável num computador é fundamental uma seqüência de bits.

A maioria dos programadores está acostumada a trabalhar com a alocação


estática de memória, que nos obriga a definir com antecedência o tamanho
máximo das estruturas de dados. A segunda forma nos desobriga dessa
definição e, portanto, é muito mais flexível, embora esteja sujeita a perigos que
lhe são característicos.

LISTAS LINEARES

A lista linear é a mais simples estrutura de dados que existe na maioria das
linguagens de programação. É conhecida mais comumente como vetor ou array
(em inglês), e é implementada na memória estática.

Formalmente representamos a lista A, contendo n elementos, da seguinte


forma:

A = (a1,...,an), n  0.

Chamamos de ak‟s de nós, e geralmente impomos a condição de que todos os


nós de uma lista devem pertencer ao mesmo conjunto, por exemplo, todos os
ak‟s devem ser números inteiros. Os nós podem ser listas também, produzindo
assim o que conhecemos por matrizes de duas ou mais dimensões. A lista A
pode ser também vazia, correspondendo ao caso especial em que n=0. Nós
representamos esse caso especial assim:
A = ( ).

Em virtude da maneira como a maioria das linguagens de programação os


implementa, os nós de uma lista linear são armazenados de forma contígua. Ou
seja, na memória do computador, o primeiro nó é imediatamente seguido pelo
segundo, o segundo pelo terceiro e assim por diante, até o final da lista. Essa
forma de armazenamento é obviamente compacta, mas, mais importante,
permite acesso rápido a qualquer elemento, pois o processo de endereçamento
é extremamente simples: o próprio compilador da linguagem que utilizamos se
encarrega de fornecer o endereço correto. Por exemplo, para indicarmos que o
quinto caixa está ocupado num dado instante da simulação de um banco,
utilizaríamos o seguinte segmento de código:

caixa_ocupado[5]  verdadeiro;

Similarmente, o segmento de código abaixo encontraria o primeiro caixa


disponível:

var i : int;
i  1;
enquanto caixa_ocupado[i]  falso faça
i  i+1;
fim enquanto;

Em nenhum desses dois casos foi necessário preocuparmo-nos com o acesso


aos nós da lista. O compilador da linguagem se encarregaria de gerar os
endereços das células contendo os membros de caixa_ocupado.
Caso desejemos adicionarmos um nó a este tipo de lista, deveremos deslocar
os nós de tal forma que possamos encaixá-lo entre dois nós já existentes.
Supomos que deseja-se colocar o dado M na posição 3 da lista abaixo:

Posiçã 1 2 3 4 5 6 7 8
o
Dado A B C D E F G

Nota-se que a lista começa na posição 1 e termina na posição 7. Para


encaixarmos o M entre B e C, ou seja, na posição 3, é necessário que
desloquemos de C até G uma posição para a direita. Logo, G passa para a
posição 8, F para a posição 7, e assim sucessivamente até C para a posição 4.
É necessário que façamos um algoritmo que troque estes valores desde G até
C, pois da forma contrária quando pusermos C na posição 4, ao lermos o dado
da posição 4 para colocarmos na 5 posição, acharemos o próprio C, ao invés do
D. E na posição 5 acharemos C, e assim até o final da lista. Abaixo será
apresentado o algoritmo que faz este deslocamento:

fim  7;
para i de fim até 3 faça
dado[i+1]  dado[i];
fim para;
fim  fim +1;

Após a execução desta rotina, a lista ficará da seguinte forma:

Posiçã 1 2 3 4 5 6 7 8
o
Dado A B C C D E F G

Agora poderemos colocar o dado M na posição 3, tendo a certeza que o


restante da lista foi preservado. Para isto:

dado[3]  „M‟;

Logo, teremos:
Posiçã 1 2 3 4 5 6 7 8
o
Dado A B M C D E F G

Nota-se também, que quanto maior a lista e quanto menor for o valor da posição
em que desejamos fazer a inserção, mais tempo será necessário para o
processamento. Este problema pode ser solucionado se colocarmos o início
como sendo uma variável. Assim, podemos deslocar tanto os elementos da
esquerda como os da direita. Este procedimento está esquematizado abaixo:

Pos. 1 2 3 4 5 6 7 8 9 10 11
Dad A B C D E F G
o

início  3;
fim  9;

Assim, poderíamos incluir M entre B e C, deslocando somente A e B para a


esquerda. Para esta, verifica-se também a necessidade de começarmos com A,
na posição de 3 para 2, por 2 se tratar de uma posição vazia.

Para excluirmos um nó, o procedimento é mais fácil, pois basta deslocarmos


todos os nós a sua direita, até o fim, uma posição para a esquerda.
Suponhamos que desejemos retirar a letra D da lista abaixo:

Posiçã 1 2 3 4 5 6 7 8
o
Dado A B M C D E F G

O procedimento é:

fim  8;
para i de 6 até fim faça
dado[i-1]  dado[i];
fim para;
fim  fim - 1;

A lista ficará:
Posiçã 1 2 3 4 5 6 7 8
o
Dado A B M C E F G G
Podemos observar que a posição 8 ficará “marcada” com o dado G, porém nós
remarcamos o final da lista de 8 para 7.

LISTAS ENCADEADAS

A lista encadeada é uma generalização da lista linear que introduz várias


alterações:

 Os nós da lista não estão necessariamente armazenados seqüencialmente;

 Dependendo da forma de implementação, pode não haver um tamanho


máximo predefinido da lista;

 E os nós podem ser de tipos heterogêneos. Por exemplo, alguns podem


conter números inteiros e outros podem ser listas de números inteiros.

A perda da contigüidade de armazenamento nos força a armazenar junto a cada


nó a informação de onde acessar o nó seguinte. Devemos também armazenar,
separadamente da lista, o endereço do primeiro nó da lista. Graficamente, o nó
de uma lista encadeada homogênea será uma dupla contendo o dado
armazenado e um ponteiro ao nó seguinte:

Esta caracterização do nó de uma lista encadeada é bastante flexível, embora


sejamos forçados a ter um tipo de nó diferente declarado para cada tipo de
dado que desejarmos armazenar e um conjunto de rotinas para manipular cada
tipo de lista.

A figura abaixo mostra a estrutura típica de uma lista encadeada homogênea.


Nela vemos um ponteiro ao inicio da lista, bem como a conectividade entre nós
da lista por meio dos ponteiros. Note que o último elemento da lista é distinguido
dos outros pelo fato de seu ponteiro ter o valor especial “ / ”, que adotaremos
como convenção gráfica para indicar que o ponteiro não aponta para lugar
algum. O valor “ / ” é equivalente ao NIL, do Pascal.

No caso de uma lista encadeada heterogênea, mostrada na figura acima, sua


estrutura parece-nos bem mais complexa, mas apenas estamos manipulando
um agregado de listas, nada mais.

O DESCRITOR

O elemento descritor de uma lista encadeada armazena as referencias ao início


e ao fim da lista. Todo acesso a lista será sempre efetuado através do descritor.

O nó descritor de uma lista pode conter outras informações sobre a lista como a
quantidade de nós na lista (como nos modelos apresentados neste trabalho),
descrição dos dados contidos nos nós, etc.

LISTAS ENCADEADAS SIMPLES

Uma lista encadeada simples é aquela que em cada nó possui apenas uma
referência. Esta referência serve para indicar seu nó sucessor.
LISTAS DUPLAMENTE ENCADEADAS

Uma lista duplamente encadeada é aquela que em cada nó possui duas


referências. Estas referências servem para indicar seu nó sucessor e seu
predecessor.

Uma lista duplamente encadeada tem a seguinte propriedade:

(p^.predecessor)^.sucessor = (p^.sucessor)^.predecessor = p

OS OPERADORES DE LISTAS

Imaginemos que as operações abaixo fossem de interesse na utilização de


listas encadeadas:

 Visitar todos os nós da lista;


 inserir um novo dado no fim (ou inicio da lista);
 remover o primeiro (ou último) nó da lista;
 buscar um nó na lista.

Chamaremos as funções e os procedimentos que manipulam uma estrutura de


dados de operadores daquela estrutura, em analogia aos operadores
disponíveis para os tipos de dados básicos de uma linguagem de programação.

A definição completa de uma estrutura de dados inclui tanto sua forma estrutural
como a disciplina de acesso, o que define sua coleção de operadores válidos.

Por questão de disciplina e boa forma de programação, uma estrutura de dados


deve ser manipulada apenas por meio de seus operadores. Esse é, em parte, o
grande segredo do valor do uso de estrutura de dados para promover o correto
funcionamento de programas.

Operações tais como impressão ou somatório de todos os elementos de uma


lista envolvem o manuseio de todos os nós de uma lista da lista encadeada. O
processo de “visitação” de um nó é tão particular a uma dada aplicação que é
difícil generalizá-lo na forma de um procedimento. Esquematicamente, porém,
visitar os nós de uma lista é muito esclarecedor quanto à manipulação dessa
estrutura de dados.

A seguir demonstraremos e comentaremos os principais operadores de


listas simples e duplamente encadeadas através de programas modelos em
Pascal*.

* A escolha da linguagem Pascal foi feita pelo motivo de ser uma linguagem
simples, que possui a manipulação de variáveis tipo ponteiro e também por ser
conhecida amplamente. Esses modelos (Modelo1.pas e Modelo2.pas) estão
em linguagem Pascal no disquete do trabalho e mais dois exemplos de
aplicações de listas (Exemplo1.pas - arranjar em ordem alfabética um grupo de
palavras; e Exemplo2.pas - um pequeno exemplo de manipulação de banco de
dados).

PROGRAM modelo_lista_linear_simples;

TYPE pont = ^no;


{Define o tipo pont como um ponteiro para nó da lista}

no = RECORD

{Definição do tipo de dado do nó. Exemplo: nome


STRING[20];}
{É nesta linha que o programador define a estrutura de dados do nó}

proximo : pont;
{Como a lista é simplesmente encadeada, só poderá haver ponteiro em uma
direção}

END;

TYPE tipo_descritor = RECORD

nos : INTEGER;
inicio, fim : pont;
{Definição do registro do descritor, onde nos é a quantidade de nós da lista;
inicio e fim, respectivamente são os ponteiros para início e fim da lista}

END;

VAR descritor : tipo_descritor;


{Declaração global da variável descritor}
PROCEDURE inicializa;
{Esta PROCEDURE serve para inicializar o descritor da lista}

BEGIN

descritor.nos:=0;
{Como a lista está vazia o número de nós deverá ser 0(zero)}

descritor.inicio:=NIL;
descritor.fim:=NIL;
{Se a lista está vazia, obviamente o início e fim da lista não existem}

END;

PROCEDURE insere_direita;
{Inserir a direita significa, inserir o nó na última posição da lista}

VAR p : pont;
{Declara p como um ponteiro para o novo nó da lista}

BEGIN
NEW(p);
{Aloca memória para o nó apontado por p}

{Rotina para entrada de dados, exemplo READLN(p^.nome);}

p^.proximo:=NIL;
{Como o novo nó está sendo inserido na última posição da lista, não existe
nenhum nó a sua frente}

IF descritor.nos=0 THEN descritor.inicio:=p


{Se não existia nenhum nó antes da inserção então isso significa que este nó é
também o primeiro da lista}

ELSE
(descritor.fim)^.proximo:=p;
{Senão, esta linha faz com que o nó próximo ao último nó da lista aponte para o
novo}

descritor.fim:=p;
{Já esta linha define que o novo nó inserido é o último da lista}

INC(descritor.nos);
{Aumenta o número de nós existentes no descritor em 1}

END;

PROCEDURE insere_esquerda;
{Inserir a esquerda significa, inserir o nó na primeira posição da lista}

VAR p : pont;
{Declara p como um ponteiro para o novo nó da lista}

BEGIN

NEW(p);
{Aloca memória para o nó apontado por p}

{Rotina para entrada de dados, exemplo READLN(p^.nome);}

p^.proximo:=descritor.inicio;
{Faz com que o nó seguinte ao novo nó (apontado por p) aponte para o antigo
primeiro nó da lista}

descritor.inicio:=p;
{Define o novo nó como o primeiro da lista}

IF descritor.nos=0 THEN descritor.fim:=p;


{Se não existia nenhum nó antes da inserção então isso significa que este nó é
também o último da lista}

INC(descritor.nos);
{Aumenta o número de nós existentes no descritor em 1}

END;

PROCEDURE insere_posicao (n : INTEGER);


{Inserir em uma posição n qualquer significa inserir um nó em uma posição
intermediária da lista}

VAR p,q : pont;


{Declara p e q como ponteiros para nó. Onde p apontará para o novo nó da
lista; e q percorrerá a lista}

BEGIN

NEW(p);
{Aloca memória para o nó apontado por p}

IF ((n<1) OR (n>descritor.nos+1)) THEN {Rotina de erro};


{Esta linha prevê um erro na entrada da posição. O limite superior é uma
unidade acima do total de nós existentes da lista porque o usuário poderá inserir
um nó na última posição}

IF n=1 THEN BEGIN


insere_esquerda;
{Caso especial onde o usuário estará inserindo o nó no início da lista, por isso
chama-se a PROCEDURE insere_esquerda}

EXIT;
{Sai da PROCEDURE }

END;

IF n=descritor.nos+1 THEN BEGIN


insere_direita;
{Caso especial onde o usuário estará inserindo o nó no fim da lista, por isso
chama-se a PROCEDURE insere_direita}

EXIT;
{Sai da PROCEDURE}

END;

{Rotina de entrada de dados, exemplo: READLN(p^.nome);}

q:=descritor.inicio;
{Faz com que o ponteiro q aponte para o primeiro nó da lista}

FOR n:=n-1 DOWNTO 2 DO q:=q^.proximo;


{Esta linha faz com que o ponteiro q percorra a lista até chegar ao nó anterior a
posição do novo nó a ser inserido}
p^.proximo:=q^.proximo;
{O nó seguinte ao novo nó(p) aponta para o nó seguinte do anterior(q)}

q^.proximo:=p;
{O nó seguinte ao nó apontado pelo ponteiro q aponta para o novo nó (apontado
por p)}

INC(descritor.nos);
{Aumenta o número de nós existentes no descritor em 1}

END;

PROCEDURE remove_direita;
{Remover a direita significa remover o último nó da lista}

VAR p,q : pont;


{Declara p e q como ponteiros para nó. Onde q apontará para o nó que irá ser
removido; e p percorrerá a lista}

i : INTEGER;
{Índice para a instrução FOR}

BEGIN

CASE descritor.nos OF

0 : {Rotina de erro};
{Se não existirem nós na lista, não será possível a remoção, daí chama-se uma
rotina de manipulação do erro}
1 : BEGIN
{Se existir apenas um nó na lista então:}

p:=descritor.inicio;
{O ponteiro p aponta para o primeiro nó da lista}

inicializa;
{Já que a lista ficará vazia podemos fazer o mesmo procedimento de inicializar o
descritor}

DISPOSE(p);
{Desaloca a memória criada do nó apontado por p}

EXIT;
{Sai do PROCEDURE}
END;
END;

p^.proximo:=descritor.inicio;
{O proximo do ponteiro p aponta para o primeiro nó da lista}

FOR i:=1 TO descritor.nos-1 DO p:=p^.proximo;


{Esta linha faz com que p percorra a lista até chegar a um nó anterior ao que vai
ser removido}

q:=p^.proximo;
{Agora q aponta para o nó seguinte em relação a p, ou seja, aponta para o nó
que vai ser removido}

p^.proximo:=NIL;
{Como a lista é linear o ponteiro p(o último) aponta para NIL}

descritor.fim:=p;
{Indica que o nó apontado por p será o último da lista}

DISPOSE(q);
{Desaloca a memória criada de nó apontado por q}

DEC(descritor.nos);
{Diminui a variável do descritor de quantidade de nós em uma unidade}

END;

PROCEDURE remove_esquerda;
{Remover a esquerda significa remover o primeiro nó da lista}

VAR p : pont;
{Declara p como ponteiro para o nó que vai ser removido}

BEGIN
CASE descritor.nos OF

0 : {Rotina de erro};
{Se não existirem nós na lista, não será possível a remoção, daí chama-se uma
rotina de manipulação do erro}
1 : BEGIN
{Se existir apenas um nó na lista então:}

p:=descritor.inicio;
{O ponteiro o aponta para o primeiro nó da lista}

inicializa;
{Já que a lista está vazia podemos fazer o mesmo procedimento de inicializar o
descritor}

DISPOSE(p);
{Desaloca a memória criada do nó apontado por p}

EXIT;
{Sai do PROCEDURE}

END;
END;

p:=descritor.inicio;
{O ponteiro p aponta para o primeiro nó da lista, este será removido}

descritor.inicio:=p^.proximo;
{O primeiro nó da lista será o nó seguinte a p}

p^.proximo:=NIL;
{Por questões de segurança fazemos com que o nó seguinte a p aponte para
NIL}

DISPOSE(p);
{Desaloca a memória criada para armazenar o primeiro nó}

DEC(descritor.nos);
{Diminui o número de nós da lista em 1}

END;
PROCEDURE remove_posicao(n : INTEGER);
{Remover uma posição n qualquer significa remover um nó em uma posição
intermediária da lista}

VAR p,q : pont;


{Declara p e q como ponteiros para nó. Onde q apontará para o nó que será
removido da lista; e p que percorrerá a lista}

i : INTEGER;
{Índice para ser utilizado na instrução FOR}

BEGIN

IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};


{Esta linha prevê um erro na entrada da posição}

IF n=1 THEN BEGIN


remove_esquerda;
{Caso especial onde o usuário estará removendo o primeiro nó da lista, por isso
chama-se a PROCEDURE remove_esquerda}

EXIT;
{Sai do PROCEDURE}

END;

IF n=descritor.nos THEN BEGIN


remove_direita;
{Caso especial onde o usuário estará removendo o último nó da lista, por isso
chama-se a PROCEDURE remove_direita}

EXIT;
{Sai do PROCEDURE}

END;

p^.proximo:=descritor.inicio;
{Nesta linha o próximo do ponteiro p aponta para o primeiro nó da lista}

FOR i:=1 TO n-1 DO p:=p^.proximo;


{Esta rotina faz com que p percorra a lista até o nó anterior à lista}

q:=p^.proximo;
{O ponteiro q aponta para o nó que vai ser removido}

p^.proximo:=q^.proximo;
{O nó seguinte ao ponteiro p aponta para o nó seguinte do nó seguinte
apontado por q}

q^.proximo:=NIL;
{Por questões de segurança fazemos q apontar para NIL}

DISPOSE(q);
{Desaloca a memória criada do nó apontado por q}

DEC(descritor.nos);
{Diminui o número de nós da lista em 1}

END;

PROCEDURE consulta(n : INTEGER);


{Rotina para a consulta dos dados armazenados no nó da posição n}

VAR p : pont;
{Declaração do ponteiro que irá percorrer a lista}

BEGIN

IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};


{Esta linha prevê um erro na entrada da posição}

p^.proximo:=descritor.inicio;
{O próximo do ponteiro p aponta para o primeiro nó da lista}

FOR n:=n DOWNTO 1 DO p:=p^.proximo;


{Esta rotina faz com que p percorra a lista até o nó desejado}

{Rotina de impressão dos dados do nó na tela, , exemplo:


WRITELN(p^.nome);}

END;
PROCEDURE modifica(n : INTEGER);
{Rotina para a modificação dos dados armazenados no nó da posição n}

VAR p : pont;
{Declaração do ponteiro que irá percorrer a lista}

BEGIN

IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};


{Esta linha prevê um erro na entrada da posição}
p^.proximo:=descritor.inicio;
{O próximo do ponteiro p aponta para o primeiro nó da lista}

FOR n:=n DOWNTO 1 DO p:=p^.proximo;


{Esta rotina faz com que p percorra a lista até o nó desejado}

{Rotina de entrada dos dados do nó na tela, , exemplo:


READLN(p^.nome);}

END;

PROGRAM modelo_lista_linear_duplamente_encadeada;

TYPE pont = ^no;


{Define o tipo pont como um ponteiro para nó da lista}

no = RECORD

{Definição do tipo de dado do nó. Exemplo: nome


STRING[20];}
{É nesta linha que o programador define a estrutura de dados do nó}

proximo, anterior : pont;


{Como a lista é duplamente encadeada cada nó deverá apontar para um nó
anterior e um posterior}

END;

TYPE tipo_descritor = RECORD


nos : INTEGER;
inicio, fim : pont;
{Definição do registro do descritor, onde nos é a quantidade de nós da lista;
início e fim, respectivamente são ponteiros para início e fim da lista}

END;

VAR descritor : tipo_descritor;


{Declaração global da variável descritor}

PROCEDURE inicializa;
{Esta PROCEDURE serve para inicializar o descritor da lista}

BEGIN
descritor.nos:=0;
{Como a lista está vazia o número de nós deverá ser 0(zero)}

descritor.inicio:=NIL;
descritor.fim:=NIL;
{Se a lista está vazia obviamente o início e o fim da lista não existem}

END;

PROCEDURE insere_direita;
{Inserir a direita significa inserir o nó na última posição da lista}

VAR p : pont;
{Declara p como um ponteiro para o novo nó da lista}

BEGIN

NEW(p);
{Aloca memória para o nó apontado por p}

{Rotina de entrada de dados, exemplo: READLN(p^.nome);}

p^.proximo:=NIL;
{Como a lista é linear , o último nó da lista tem que apontar para NIL}

p^.anterior:=descritor.fim;
{Como o novo nó será o último ele deverá ser precedido do último nó antigo}
IF descritor.nos<>0 THEN BEGIN
(descritor.fim)^.proximo:=p;
{Se a lista não estiver vazia então o nó seguinte ao último nó antigo deverá
apontar para o novo}

END ELSE BEGIN


descritor.inicio:=p;
{Senão, estará sendo inserido o primeiro nó da lista, daí a necessidade de
defini-lo também como o primeiro nó da lista}

END;

descritor.fim:=p;
{Esta linha define que o novo nó inserido estará na última posição da lista}

INC(descritor.nos);
{Aumenta o número de nós existentes em 1}

END;

PROCEDURE insere_esquerda;
{Inserir a esquerda significa, inserir o nó na primeira posição da lista}

VAR p : pont;
{Declara p como um ponteiro para o novo nó da lista}

BEGIN

NEW(p);
{Aloca memória para o nó apontado por p}

{Rotina de entrada de dados, exemplo: READLN(p^.nome);}

p^.proximo:=descritor.inicio;
{Faz com que o nó seguinte ao novo nó (apontado por p aponte para o antigo
primeiro nó da lista}

p^.anterior:=NIL;
{Como o nó está sendo inserido na direita, ele não estará sendo precedido por
nenhum outro nó}
IF descritor.nos<>0 THEN
(descritor.inicio)^.anterior:=p
{Se a lista não estiver vazia o nó anterior ao antigo primeiro será o novo}

ELSE
descritor.fim:=p;
{Senão, como a lista está vazia o novo nó também será o último da lista}

descritor.inicio:=p;
{Define o novo nó como o primeiro da lista}

INC(descritor.nos);
{Aumenta o número de nós existentes em 1}

END;

PROCEDURE insere_posicao (n : INTEGER);


{Inserir em uma posição n significa inserir um nó em uma posição intermediária
da lista}

VAR p,q : pont;


{Declara p e q como ponteiros para nó. Onde p apontará para o novo nó da
lista; e q percorrerá a lista}

BEGIN

NEW(p);
{Aloca memória para o nó apontado por p}

IF ((n<1) OR (n>descritor.nos+1)) THEN {Rotina de erro}


{Esta linha prevê um erro na entrada da posição. O limite superior é uma
unidade acima do total de nós existentes da lista porque o usuário poderá inserir
um nó na última posição}

IF n=1 THEN BEGIN


insere_esquerda;
{Caso especial onde o usuário estará inserindo o nó no início da lista, por isso
chama-se a PROCEDURE insere_esquerda}

EXIT;
{Sai da PROCEDURE }
END;

IF n=descritor.nos+1 THEN BEGIN


insere_direita;
{Caso especial onde o usuário estará inserindo o nó no fim da lista, por isso
chama-se a PROCEDURE insere_direita}

EXIT;
{Sai da PROCEDURE }

END;

{Rotina de entrada de dados, exemplo: READLN(p^.nome);}

q^.proximo:=descritor.inicio;
{Faz com que o próximo do ponteiro q aponte para o primeiro nó da lista}

FOR n:=n DOWNTO 2 DO q:=q^.proximo;


{Esta linha faz com que q percorra a lista até chegar ao nó anterior a posição do
novo nó a ser inserido}

p^.proximo:=q^.proximo;
{O nó seguinte ao novo nó(p) aponta para onde o nó seguinte do anterior(q)
aponta}

p^.anterior:=q;
{O nó anterior ao nó apontado por p aponta para q}

q^.proximo:=p;
{O nó seguinte ao nó apontado pelo ponteiro q aponta para o novo nó(apontado
por p)}

(p^.proximo)^.anterior:=p;
{Para fechar a cadeia, o nó seguinte ao novo deverá estar conectado com o
novo nó}

END;

PROCEDURE remove_direita;
{Remover a direita significa remover o último nó da lista}
VAR p,q : pont;
{Declara p e q como ponteiros para nó. Onde q apontará para o nó que irá ser
removido; e p percorrerá a lista}

BEGIN

CASE descritor.nos OF

0 : {Rotina de erro};
{Se não existirem nós na lista, não será possível a remoção, daí chama-se a
rotina de manipulação do erro}

1 : BEGIN
{Se existir apenas um nó da lista então:}

p:=descritor.inicio;
{O ponteiro aponta para o primeiro nó da lista}

inicializa;
{Já que a lista ficará vazia, podemos fazer o mesmo procedimento de inicializar
o descritor}

DISPOSE(q);
{Desaloca a memória criada do nó apontado por p}

EXIT
{Sai da PROCEDURE}

END;

END;

p:=(descritor.fim)^.anterior;
{O ponteiro p aponta para o penúltimo nó da lista}

p^.proximo:=NIL;
{Como a lista é linear o ponteiro p(o último) aponta para NIL}

(descritor.fim)^.anterior:=NIL;
{Como agora o penúltimo nó será o último, não existe nenhum outro nó
seguinte, daí porque proximo aponta para NIL}

q:=descritor.fim;
{Faz com que q aponte para o nó a ser removido, ou seja, o último da lista}

DISPOSE(q);
{Desaloca a memória criada do nó apontado por q}

descritor.fim:=p;
{Indica que o nó apontado por p será o último da lista}

DEC(descritor.nos);
{Diminui o número de nós da lista em 1 }

END;

PROCEDURE remove_esquerda;
{Remover a esquerda significa remover o primeiro nó da lista}

VAR p : pont;
{Declara p que será o nó removido}

BEGIN

CASE descritor.nos OF

0 : {Rotina de erro};
{Se não existirem nós na lista, não será possível a remoção, daí chama-se uma
rotina de manipulação do erro}

1 : BEGIN
{Se existir apenas um nó na lista então:}

p:=descritor.inicio;
{O ponteiro p aponta para o primeiro nó da lista, este será removido}

p^.anterior:=NIL;
p^.proximo:=NIL;
{Por questões de segurança, fazemos com que o nó apontado por p esteja
completamente isolado}
inicializa;
{Já que a lista está vazia podemos fazer o mesmo procedimento de inicializar o
descritor}

DISPOSE(p);
{Desaloca a memória criada do nó apontado por p}

EXIT
{Sai da PROCEDURE}

END;
END;

p:=descritor.inicio;
{O ponteiro p aponta para o primeiro nó da lista}

descritor.inicio:=p^.proximo;
{Agora o segundo nó da lista torna-se o primeiro}

(descritor.inicio)^.anterior:=NIL;
{O nó anterior do primeiro nó de uma lista linear não existe, por isso seu anterior
é nulo}

p^.proximo:=NIL;
{Por segurança aterra-se o ponteiro p}

DISPOSE(p);
{Desaloca a memória criada para armazenar o antigo primeiro nó}

DEC(descritor.nos);
{Diminui o número de nós da lista em 1 }

END;

PROCEDURE remove_posicao(n : INTEGER);

VAR p: pont;
i : INTEGER;

BEGIN
IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};
{Esta linha prevê um erro na entrada da posição}

IF n=1 THEN BEGIN


remove_esquerda;
{Caso especial onde o usuário estará removendo o primeiro nó da lista, por isso
chama-se a PROCEDURE remove_esquerda}

EXIT;
{Sai do PROCEDURE}

END;

IF n=descritor.nos THEN BEGIN


remove_direita;
{Caso especial onde o usuário estará removendo o último nó da lista, por isso
chama-se a PROCEDURE remove_direita}

EXIT;
{Sai do PROCEDURE}

END;

p^.proximo:=descritor.inicio;
{Nesta linha o próximo do ponteiro p aponta para o primeiro nó da lista}

FOR i:=1 TO n DO p:=p^.proximo;


{Esta rotina faz com que p percorra a lista até o nó que será removido}

(p^.anterior)^.proximo:=p^.proximo;
{Esta rotina faz com que o nó seguinte ao anterior do apontado por p aponte
para o seguinte de p}

(p^.proximo)^.anterior:=p^.anterior;
{Esta rotina faz com que o nó seguinte ao anterior do apontado por p aponte
para o anterior a p}

p^.proximo:=NIL;
p^.anterior:=NIL;
{Aterramos o nó apontado por p por questões de segurança}
DISPOSE(p);
{Desaloca a memória cria do nó apontado por p}

DEC(descritor.nos);
{Diminui o número de nós da lista em 1}

END;

PROCEDURE consulta(n : INTEGER);


{Rotina para a consulta dos dados armazenados no nó da posição n}

VAR p : pont;
{Declaração do ponteiro que irá percorrer a lista}

BEGIN

IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};


{Esta linha prevê um erro na entrada da posição}
p^.proximo:=descritor.inicio;
{O próximo do ponteiro p aponta para o primeiro nó da lista}

FOR n:=n DOWNTO 1 DO p:=p^.proximo;


{Esta rotina faz com que p percorra a lista até o nó desejado}

{Rotina de impressão dos dados do nó na tela, , exemplo:


WRITELN(p^.nome);}

END;

PROCEDURE modifica(n : INTEGER);


{Rotina para a modificação dos dados armazenados no nó da posição n}

VAR p : pont;
{Declaração do ponteiro que irá percorrer a lista}

BEGIN

IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};


{Esta linha prevê um erro na entrada da posição}
p^.proximo:=descritor.inicio;
{O próximo do ponteiro p aponta para o primeiro nó da lista}
FOR n:=n DOWNTO 1 DO p:=p^.proximo;
{Esta rotina faz com que p percorra a lista até o nó desejado}

{Rotina de entrada dos dados do nó na tela, , exemplo:


READLN(p^.nome);}

END;

APÊNDICE - LISTAS CIRCULARES

Mesmo não sendo parte do conteúdo, é bom se comentar um pouco


sobre listas circulares. Nas páginas seguintes se encontra um modelo de lista
circular simples (sem restrições de tamanho) que também se encontra no
disquete (Modelo3.pas).

PROPRIEDADES

A propriedade de uma lista circular simples é a seguinte:

(descritor.fim)^.sucessor = descritor.início;

Já as propriedades de uma lista circular duplamente encadeada são as


seguinte:

(descritor.fim)^.sucessor = descritor.início;
(descritor.início)^.predecessor = descritor.início;
PROGRAM modelo_lista_circular_simples;
TYPE pont = ^no;
no = RECORD
{Definição do tipo de dado do nó. Exemplo: nome
STRING[20];}
proximo : pont;
END;
TYPE tipo_descritor = RECORD
nos : INTEGER;
inicio, fim : pont;
END;
VAR descritor : tipo_descritor;

PROCEDURE inicializa;
BEGIN
descritor.nos:=0;
descritor.inicio:=NIL;
descritor.fim:=NIL;
END;

PROCEDURE insere_direita;
VAR p : pont;
BEGIN
NEW(p);
{Rotina para entrada de dados, exemplo READLN(p^.nome);}
p^.proximo:=NIL;
IF descritor.nos=0 THEN
descritor.inicio:=p
ELSE
(descritor.fim)^.proximo:=p;
descritor.fim:=p;
p^.proximo:=descritor.inicio;
INC(descritor.nos);
END;

PROCEDURE insere_esquerda;
VAR p : pont;
BEGIN
NEW(p);
{Rotina para entrada de dados, exemplo READLN(p^.nome);}
p^.proximo:=descritor.inicio;
descritor.inicio:=p;
IF descritor.nos=0 THEN descritor.fim:=p;
(descritor.fim)^.proximo:=p;
INC(descritor.nos);
END;

PROCEDURE insere_posicao (n : INTEGER);


VAR p,q : pont;
BEGIN
NEW(p);
IF ((n<1) OR (n>descritor.nos+1)) THEN {Rotina de erro}
IF n=1 THEN BEGIN
insere_esquerda;
EXIT;
END;
IF n=descritor.nos+1 THEN BEGIN
insere_direita;
EXIT;
END;
{Rotina para entrada de dados, exemplo READLN(p^.nome);}
q^.proximo:=descritor.inicio;
FOR n:=n DOWNTO 2 DO q:=q^.proximo;
p^.proximo:=q^.proximo;
q^.proximo:=p;
END;

PROCEDURE remove_direita;
VAR p,q : pont;
i : INTEGER;
BEGIN
CASE descritor.nos OF
0 : {Rotina de erro};
1 : BEGIN
p:=descritor.inicio;
inicializa;
DISPOSE(p);
EXIT;
END;
END;
p^.proximo:=descritor.inicio;
FOR i:=1 TO descritor.nos-1 DO p:=p^.proximo;
q:=p^.proximo;
p^.proximo:=descritor.inicio;
descritor.fim:=p;
DISPOSE(q);
DEC(descritor.nos);
END;

PROCEDURE remove_esquerda;
VAR p : pont;
BEGIN
CASE descritor.nos OF
0 : {Rotina de erro};
1 : BEGIN
p:=descritor.inicio;
inicializa;
DISPOSE(p);
EXIT;
END;
END;
p:=descritor.inicio;
descritor.inicio:=p^.proximo;
(descritor.fim)^.proximo:=descritor.inicio;
p^.proximo:=NIL;
DISPOSE(p);
DEC(descritor.nos);
END;

PROCEDURE remove_posicao(n : INTEGER);


VAR p,q : pont;
i : INTEGER;
BEGIN
IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};
IF n=1 THEN BEGIN
remove_esquerda;
EXIT;
END;
IF n=descritor.nos THEN BEGIN
remove_direita;
EXIT;
END;
p^.proximo:=descritor.inicio;
FOR i:=1 TO n-1 DO p:=p^.proximo;
q:=p^.proximo;
p^.proximo:=NIL;
DISPOSE(q);
DEC(descritor.nos);
END;

PROCEDURE consulta(n : INTEGER);


VAR p : pont;
BEGIN
IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};
p^.proximo:=descritor.inicio;
FOR n:=n DOWNTO 1 DO p:=p^.proximo;
{Rotina para impressão dos dados do nó na tela, exemplo:
WRITELN(p^.nome);}
END;

PROCEDURE modifica(n : INTEGER);


VAR p : pont;
BEGIN
IF ((n<1) OR (n>descritor.nos)) THEN {Rotina de erro};
p^.proximo:=descritor.inicio;
FOR n:=n DOWNTO 1 DO p:=p^.proximo;
{Rotina de entrada dos dados do nó na tela, exemplo:
READLN(p^.nome);}
END;

FUNCTION procura(p : pont) : INTEGER;


VAR q : pont;
i : INTEGER;
BEGIN
IF descritor.nos=0 THEN {Rotina de erro};
q:=descritor.inicio;
i:=1;
WHILE q<>NIL DO BEGIN
{Rotina que checa a igualdade, exemplo:
IF p^.nome=q^.nome THEN ...}
{Caso a expressão acima for verdadeira, a variável i
fornece em que posição a informação foi encontrada e
sai da FUNCTION retornando o valor da variável i};
q:=q^.proximo;
INC(i);
END;
{Rotina que diz que não foi encontrada a informação};
END;
Produção, Edição, Elaboração e Revisão de Texto:
ESCON - Escola de Cursos Online
Proibida a reprodução total ou parcial sem permissão expressa da ESCON. (Lei 9.610/98)

Você também pode gostar