Recursão - Universidade Castelo Branco

Propaganda
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
1. Recursão
1.1 Definição
Um objeto é dito recursivo se ele consistir parcialmente ou for definido em termos
de si próprio. Recursões ocorrem na matemática, informática, no dia a dia...
Exemplos de recursão em definições matemáticas:
– Números naturais:
• 0 é um número natural;
• O sucessor de um número natural é um número natural;
– Função Fatorial (n!) para inteiros positivos:
• 0! = 1
• n>0: n! = n * (n - 1)!
1.2 Algoritmos Recursivos
Um algoritmo que para resolver um problema divide-o em subproblemas mais
simples, cujas soluções requerem a aplicação dele mesmo, é chamado recursivo. Em
programação, uma subrotina (procedimento ou função) é recursiva quando ela chama a si
mesma. Suponha uma rotina recursiva R formada por um conjunto de comandos C (que
não contém chamadas a R) e uma chamada (recursiva) à R:
1.3 Tipos de Recursão
 Recursão Direta
É quando em uma subrotina existe uma chamada para a própria subrotina,
independentemente dos valores dos parâmetros:
Prof. Cláudio H. S. Grecco
1
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
 Recursão Indireta
As subrotinas são conectadas através de uma cadeia de chamadas sucessivas que acaba
retornando à primeira que a desencadeou:
1.4 Funcionamento da Recursão
A subrotina permite que seja dado um nome a um conjunto de comandos. Um
desses comandos pode ser a chamada à própria subrotina. As subrotinas possuem objetos
locais sem significado fora dela (variáveis, parâmetros, constantes, tipos e subrotinas).
Toda vez que tal subrotina for executada recursivamente, um novo conjunto de variáveis
locais e parâmetros são criados. Ainda que variáveis e parâmetros tenham o mesmo nome
os identificadores se referem ao conjunto criado mais recentemente (seus valores são
diferentes).
Como ocorre nos comandos repetitivos, as chamadas recursivas possibilitam a não
terminação (looping). Para tal, deve ser condicionado uma expressão lógica que, em algum
instante, tornar-se-á false e permitirá que a recursão termine:
R [C,TR], sendo T um teste lógico
Técnica básica para garantir o término:


definir uma função f(x), sendo x um conjunto de variáveis, tal que f(x) 0 implica em
uma condição de terminação;
garantir que f(x) descresce a cada passo da repetição.
R [C, (f(x)>0)R(x-1)], onde x decresce a cada chamada
Na prática, ao definir rotinas recursivas, o problema é dividido da seguinte forma:
– solução trivial: dada por definição, isto é, não necessita de recursão (resolvida pelo
conjunto de comandos C);
– solução geral: parte do problema que em essência é igual ao problema original, porém
menor (resolvida pela chamada recursiva R).
Prof. Cláudio H. S. Grecco
2
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
Um teste define, a cada momento, se o problema terá solução trivial ou geral. Em
termos matemáticos, a recursão é uma técnica que, através de substituições sucessivas,
reduz o problema a um caso de solução trivial
Exemplo de Recursão: fatorial
 Cálculo da Fatorial (números naturais positivos):
– solução trivial: 0! = 1 (condição de parada)
– solução geral: n! = n*(n-1)!

Algoritmo em Pascal para cálculo da fatorial
FUNCTION fat(n: INTEGER):INTEGER;
BEGIN
IF n= 0 THEN fat:= 1
ELSE fat:= n * fat (n-1);
END;
1.5 Profundidade da Recursão
Profundidade é o número de vezes que uma rotina recursiva chama a si própria, até
obter o resultado. Muitas vezes a profundidade de uma recursão não é tão clara, até mesmo
para definições simples.
Exemplo: profundidade da fatorial de 4:
– Valores de n: da chamada é 4, do 1o nível é 3, do 2o nível é 2 do 3o nível é 1 e do 4o nível é
0.
Prof. Cláudio H. S. Grecco
3
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
1.6 Exemplo de recursão: Torres de Hanoi
Enunciado do problema
“Há muito tempo atrás, no alto das montanhas de Hanoi, havia um mosteiro onde
habitavam sacerdotes brâmanes; entre eles, era praticado um ritual para predizer o fim do
mundo. Conta a lenda, que no mosteiro havia três torres, sendo que na primeira delas
estavam empilhados 64 discos de ouro em tamanhos decrescentes. Os sacerdotes
acreditavam que quando eles terminassem de transferir todos os discos da primeira torre
para a terceira (usando a segunda), sem nunca colocar um disco maior sobre um menor,
então, neste dia, o mundo acabaria!”
Para facilitar, vamos supor a existência de três discos:
O objetivo é transferir os três discos da torre A para a torre C, usando a torre B
como auxiliar. Somente o primeiro disco de uma torre pode ser deslocado para outra, e um
disco maior nunca pode ser colocado sobre outro menor. Processo para transferir os três
discos:
Matematicamente é possível demonstrar que o tempo necessário para mover n
discos é da ordem de n! (ou maior).
Prof. Cláudio H. S. Grecco
4
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Solução usando recursão:
– solução trivial: Se n=1, transfira o disco da torre A para a torre C;
– solução geral:
a) transfira n-1 discos da torre A para a torre B, usando C como auxiliar;
b) transfira o último disco da torre A para a torre C;
c) transfira n-1 discos da torre B para torre C, usando A como auxiliar.
Algoritmo em Pascal para a solução do problema
PROCEDURE Hanoi(n:INTEGER; Org,Aux,Dst:CHAR);
BEGIN
IF n= 1 THEN
writeln(‘Mova disco 1 da torre ’,Org,
‘ para ‘, Dst)
ELSE
BEGIN
Hanoi(n-1,Org,Dst,Aux);
writeln(‘Mova disco ‘,n,’ da torre ‘,Org,
‘ para ‘, Dst);
Hanoi(n-1,Aux,Org,Dst);
END;
END;
Quando a chamada Hanoi (3,’A’,’B’,’C’) for executada, será produzida a seguinte saída:
Mova disco 1 da torre A para C
Mova disco 2 da torre A para B
Mova disco 1 da torre C para B
Mova disco 3 da torre A para C
Mova disco 1 da torre B para A
Mova disco 2 da torre B para C
Mova disco 1 da torre A para C
1.7 Uso da Recursão
Nem sempre é recomendado o uso de recursão. Alguns problemas são impossíveis de
solucionar com recursão. Problemas:
– Custo de tempo e espaço: Cada vez que a subrotina é chamada, todas as variáveis locais
são recriadas (gerenciamento do registro de ativação e espaço por ele ocupado);
– Dificuldade na depuração de programas, particularmente se a recursão for profunda.
Prof. Cláudio H. S. Grecco
5
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Por que usar recursão?
Se bem empregada, torna o algoritmo muito elegante, isto é, claro, simples e
conciso. Algoritmos recursivos são apropriados quando o problema for definido em termos
recursivos. Programas onde a recursão deve ser evitada podem ser evitados por um
esquema que exiba o padrão da sua composição:
R [TC,R]
R [C,TR]
– a característica desses programas é possuir uma só chamada a R no final (chamada de
recursão de cauda).
– os programas que se encaixam nesse esquema, como o exemplo do cálculo da fatorial,
possuem uma solução iterativa (não recursiva) mais eficiente.
1.8 Recursão de Cauda
Na recursão de cauda a chamada recursiva está no final do código, tendo como
função criar um laço que será repetido até a condição de parada.
Para eliminar a recursão de cauda:
– Se uma rotina R(x) tem como última instrução uma chamada recursiva R(y), então
troca-se R(y) pela atribuição xy, seguido de um desvio para o início de R.
– Utiliza-se uma repetição condicionada à expressão de teste usada na versão recursiva.
1.9 Versão Iterativa do Cálculo de fatorial
FUNCTION fat(n:INTEGER):INTEGER;
VAR result:INTEGER;
BEGIN
result:=1;
WHILE n>0 DO
BEGIN
result:= result*n;
n:= n-1;
END;
fat := result;
END;
–
Embora fatorial seja mais eficiente de forma iterativa, é um excelente exemplo para se
entender o que é recursão.
Prof. Cláudio H. S. Grecco
6
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
2. Listas Lineares
São estruturas dinâmicas caracterizadas por manter uma seqüência ordenada de
elementos. São compostas por elementos que podem ser dados primitivos ou estruturados.
Os elementos são chamados de nodos.
– Seja uma lista X, formada por nodos X1,X2, ... Xn, tal que:
1) Existem “n” nodos na lista (n>=0);
2) X1 é o primeiro nodo;
3) Xn é o último nodo;
4) Para todo i,j entre 1 e n, se i<j, então o elemento Xi antecede Xj;
5) Caso i = j -1, Xi é o antecessor de Xj e Xj é o sucessor de Xi.
6) Quando a lista não possui nodos (n=0) a lista é dita vazia.
• Exemplos de listas: lista de clientes de uma banco, lista de chamada, lista de compras, etc.
2.1 Operações sobre Listas
 Percurso
Operação que permite utilizar cada um dos elementos de uma lista linear. A lista pode
ser percorrida de várias formas, do início ao fim, de trás para frente, etc.
 Busca
Operação que procura um elemento específico. A busca pode ser feita de duas formas:
ou o elemento é identificado por sua posição relativa ou pelo seu conteúdo.

Inserção de Elementos por Posição

Retirada de Elementos por Posição
Prof. Cláudio H. S. Grecco
7
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
2.2 Alocação de Memória
• Estática
– Quantidade total de memória utilizada pelos dados é definida em tempo de compilação
de forma imutável;
– Durante toda execução a quantidade de memória utilizada não varia;
– Ideal para uso com listas seqüencias.
• Dinâmica
– O programa é capaz de criar novas variáveis enquanto executa;
– Áreas de memória passam a existir durante a execução do programa;
– Ideal para uso com listas encadeadas.
2.3 Lista Seqüencial
Serve para armazenar uma lista no computador colocando seus elementos em células de
memória consecutivas, um após o outro (contigüidade física). Com isso, cada célula tem
um endereço único e ocupa o mesmo espaço de memória. Dado o endereço inicial da área
alocada e o índice de um elemento qualquer, é possível acessá-lo imediatamente. Quando
são necessárias inserções ou remoções no meio da lista é necessária a movimentação de
elementos. Características de uma lista seqüencial:
–
–
–
–
Contém nodos representados por endereços contíguos (de igual distância);
Armazenados na memória um ao lado do outro;
Lista pode ser implementada através de um vetor;
Número de elementos da lista pode variar durante a execução (deve-se armazenar a
quantidade de elementos na lista);
– Com o uso de alocação estática, a quantidade máxima de elementos é determinada no
código-fonte;
– Representação física e lógica são iguais.
2.4 Estrutura Física e Lógica
• Estrutura Lógica é como os dados são vistos pelos programadores.
• Estrutura Física é como os dados são efetivamente armazenados na memória.
Prof. Cláudio H. S. Grecco
8
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
2.5 O Tipo Lista em Pascal
• Componentes da lista:
– Número de nodos da lista (total)
– Vetor de nodos (memo)
– Tamanho total da Lista (MAX)
• Declaração em Pascal:
CONST MAX = 50;
SUCESSO = 1;
LISTACHEIA = 2;
LISTAVAZIA = 3;
POSICAOINVALIDA = 4;
TYPE Elem = INTEGER;
Lista= RECORD
total: integer;
memo: ARRAY [1..MAX] OF Elem;
END;
2.6 Trabalhando com Listas em Pascal
• Criando Listas:
PROCEDURE CriaLista (VAR l: Lista);
BEGIN
l.total :=0;
END;
Prof. Cláudio H. S. Grecco
9
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Inserindo no Fim de Listas:
FUNCTION IncluiFim (VAR l: Lista; no: Elem):BYTE;
BEGIN
IF l.total = MAX THEN
IncluiFim := LISTACHEIA
ELSE
BEGIN
l.total:= l.total+1;
l.memo[l.total] := no;
IncluiFim := SUCESSO;
END;
END;
Inserindo no Início de Listas:
FUNCTION IncluiInicio (VAR l: Lista; no: Elem):BYTE;
VAR i: integer;
BEGIN
IF l.total = MAX THEN
IncluiInicio := LISTACHEIA
ELSE
BEGIN
l.total:= l.total+1;
FOR i:=l.total DOWNTO 2 DO
l.memo[i] := l.memo[i-1];
l.memo[1]:=no;
IncluiInicio := SUCESSO;
END;
END;
Inserindo em Posição Específica:
FUNCTION IncluiPos(VAR L:Lista;no:Elem; pos:INTEGER):BYTE;
VAR i:integer;
BEGIN
IF l.total=MAX THEN
IncluiPos:=LISTACHEIA
ELSE
IF (pos>0) AND (pos<=l.total+1) THEN
BEGIN
l.total:=l.total+1;
FOR i:=l.total DOWNTO pos+1 DO
l.memo[i]:=l.memo[i-1];
l.memo[pos]:=no;
IncluiPos:=SUCESSO;
END
ELSE
IncluiPos:=POSICAOINVALIDA;
END;
Prof. Cláudio H. S. Grecco
10
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
Removendo do Fim:
FUNCTION RetiraFim (VAR l: Lista; VAR no:Elem):BYTE;
BEGIN
IF l.total = 0 THEN
RetiraFim := LISTAVAZIA
ELSE
BEGIN
no := l.memo[l.total];
l.total:= l.total-1;
RetiraFim := SUCESSO;
END;
END;
Removendo do Início:
FUNCTION RetiraInicio (VAR l: Lista; VAR no: Elem):BYTE;
VAR i: integer;
BEGIN
IF l.total = 0 THEN
RetiraInicio := LISTAVAZIA
ELSE
BEGIN
no:=l.memo[1];
l.total:= l.total-1;
FOR i:=1 TO l.total DO
l.memo[i] := l.memo[i+1];
RetiraInicio := SUCESSO;
END;
END;
Removendo em Posição Específica:
FUNCTION RetiraPos(VAR L:Lista;VAR no:Elem; pos:INTEGER):BYTE;
VAR i:integer;
BEGIN
IF l.total=0 THEN
RetiraPos:=LISTAVAZIA
ELSE
IF (pos>0) AND (pos<=l.total) THEN
BEGIN
no:=l.memo[pos];
l.total:=l.total-1;
FOR i:=pos TO l.total DO
l.memo[i]:=l.memo[i+1];
RetiraPos:=SUCESSO;
END
ELSE
RetiraPos:=POSICAOINVALIDA;
END;
Prof. Cláudio H. S. Grecco
11
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
2.7 Alternativa de Implementação
Para facilitar as inserções e remoções no início da lista, pode-se inserir a partir do
meio do vetor.
CONST MAX = 50;
TYPE Elem = INTEGER;
Lista= RECORD
inicio,fim: integer;
memo: ARRAY [1..MAX] OF Elem;
END;
–
–
–
–
–
Caso início = 0 e fim = 0 a lista está vazia;
Caso início = 1 e fim = MAX a lista está cheia;
Na inicialização o fim e o início são zerados;
Na primeira inserção: início é igual a MAX DIV 2 e fim é igual a início;
Mesmo assim, pode ser necessária a movimentação.
2.8 Conclusão sobre Lista Seqüencial
A representação física da lista pode variar conforme o extremo das
inserções/remoções. Uma posição específica para consulta pode ser calculada (acesso
aleatório).
Uma lista seqüencial facilita a transferência de dados, pois armazena tudo em área
contígüa de dados, além disso, inserções e remoções podem exigir movimentação de dados.
A lista seqüencial mantém um espaço de memória ocioso, devido a alocação estática e os
limites devem ser testados constantemente.
2.9 Disciplinas de Acesso
Considerando-se somente operações de acesso, inserção e remoção, restritas aos
extremos da listas, temos os seguintes casos especiais:



Pilha: lista linear onde os acessos, inserções e remoções são realizados em um único
extremo. Listas do tipo LIFO (Las-tin Fist-out, último a entrar é o primeiro a sair);
Fila: lista linear onde as inserções são feitas em um extremo e os acessos e remoções no
outro. Listas do tipo FIFO (First-in First-out, primeiro a entrar é o primeiro a sair);
Fila Dupla ou DEQUE (Double Ended Queue): lista linear onde as inserções,
remoções e acessos são feitos em qualquer extremo. Casos especiais: Entrada Restrita e
Saída Restrita.
Prof. Cláudio H. S. Grecco
12
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
2.10 Pilhas (Stacks)
Nas pilhas, as inserções e remoções ocorrem em um mesmo extremo da lista,
denominado topo. Os elementos são removidos em ordem inversa àquela em que foram
inseridos (LIFO - Last-in First-out).
• Operações Básicas:
– Inicializa Pilha
– Insere Pilha (empurra / push)
– Remove Pilha (puxa / pop)
– Consulta Topo (top)
– Vazia (isempty)
– Cheia (isfull)
2.11 Implementando Pilhas em Pascal
• Componentes da Pilha:
– um vetor, para armazenar os elementos contidos;
– um índice, para indicar a posição do topo.
Prof. Cláudio H. S. Grecco
13
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Registro em Pascal:
CONST MAX = 50;
TYPE Elem = CHAR;
Pilha= RECORD
topo: INTEGER;
memo: ARRAY[1..MAX] OF Elem;
END;
• Inicializando a Pilha:
PROCEDURE Inicializa(VAR p:Pilha);
BEGIN
p.topo:=0;
END;
• Verificando Limites:
FUNCTION Vazia(VAR p:Pilha):BOOLEAN;
BEGIN
IF p.topo=0 THEN
Vazia := TRUE
ELSE
Vazia := FALSE;
END;
FUNCTION Cheia(VAR p:Pilha):BOOLEAN;
BEGIN
IF p.topo=MAX THEN
Cheia := TRUE
ELSE
Cheia := FALSE;
END;
• Empilhando:
PROCEDURE Insere(VAR p:Pilha; no:Elem);
BEGIN
IF NOT Cheia(P) THEN
BEGIN
p.topo:=p.topo+1;
p.memo[p.topo] := no;
END
ELSE
WRITELN(‘Pilha Cheia!’); {Stack Overflow}
END;
• Desempilhando:
FUNCTION Remove(VAR p:Pilha):Elem;
BEGIN
IF NOT Vazia(P) THEN
BEGIN
Remove:= p.memo[p.topo];
p.topo:=p.topo-1;
END
ELSE
WRITELN(‘Pilha Vazia!’); {Stack Underflow}
END;
Prof. Cláudio H. S. Grecco
14
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Consulta o Topo:
FUNCTION Topo(VAR p:Pilha):Elem;
BEGIN
IF NOT Vazia(P) THEN
Topo:= p.memo[p.topo]
ELSE
WRITELN(‘Pilha Vazia!’); END;
2.12 Exemplos do Uso de Pilha
• Exemplo 1: Controle de Subrotinas
– sempre que uma subrotina é chamada, uma pilha é utilizada para armazenar o valor de
retorno.
• Exemplo 2: Imprimir número decimal em binário
– dividir um inteiro sucessivamente por dois, até obter um quociente igual a zero. Então
tomar os restos da divisão em ordem inversa.
Exemplo 2
PROCEDURE DEC2BIN(n:INTEGER);
VAR resto, quoc:INTEGER;
p:pilha;
BEGIN
inicializa(p);
REPEAT
resto:= n MOD 2;
insere(P,resto);
n := n DIV 2;
UNTIL N=0;
WHILE NOT vazia(P) DO
BEGIN
resto := remove(P);
WRITE(resto);
END;
WRITELN;
END;
Prof. Cláudio H. S. Grecco
15
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
2.12 Filas (Queues)
As inserções ocorrem em um extremo ficando as remoções restritas a outro. Os elementos
são removidos na mesma ordem em que foram inseridos (FIFO-First-in First-out).
• Operações Básicas:
–
–
–
–
–
Inicializa Lista
Insere Lista (enfileira / enqueue)
Remove Lista (desenfileira / dequeue)
Vazia (isempty)
Cheia (isfull)
2.13 Implementação de Filas
• Componentes da Fila:
– vetor para armazenar os elementos;
– variável apontando para o primeiro elemento;
– variável apontando para à primeira posição livre.
• Registro em Pascal:
TYPE Fila= RECORD
começo, fim: INTEGER;
memo: ARRAY[1..MAX] OF Elem;
END;
Prof. Cláudio H. S. Grecco
16
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Problema de Implementação:
– Cada vez que um elemento é removido, o começo desloca-se para à direita. Após a
remoção de todos os elementos não será mais possível inserir na lista.
Solução para o problema
– Uso de lista circular: o fim do vetor emenda no início;
– Cada posição liberada por um elemento disponível torna-se prontamente disponível;
– É necessário que logicamente a posição 1 esteja após a posição MAX;
2.14 Implementação de Filas em Pascal
• Componentes da Fila:
–
–
–
–
vetor para armazenar os elementos;
variável apontando para o primeiro da fila;
variável apontando para o último da fila;
quantidade de elementos da fila.
• Registro em Pascal:
CONST MAX= 50;
TYPE Elem= REAL;
TYPE Fila= RECORD
comeco,
final,
total: INTEGER;
memo: ARRAY[1..MAX] OF Elem;
END;
• Inicializando Fila:
PROCEDURE Inicializa(VAR f:Fila);
BEGIN
f.total:=0;
f.comeco:=1;
f.final:=1;
END;
Prof. Cláudio H. S. Grecco
17
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Verificando Limites:
FUNCTION
BEGIN
Vazia :=
END;
FUNCTION
BEGIN
Cheia :=
END;
Vazia(VAR f:Fila):BOOLEAN;
(f.total = 0);
Cheia(VAR f:Fila):BOOLEAN;
(f.total = MAX);
Insere Elemento na Fila:
PROCEDURE Insere(VAR f:Fila; no:Elem);
BEGIN
IF NOT Cheia(f) THEN
BEGIN
f.memo[f.final] := no;
f.total :=f.total +1;
f.final:=(f.final MOD MAX)+1;
END
ELSE
WRITELN(‘Fila Cheia!’);
END;
Remove Elemento da Fila:
FUNCTION Remove(VAR f:Fila):Elem;
BEGIN
IF NOT Vazia(f) THEN
BEGIN
Remove:=f.memo[f.comeco];
f.total:=f.total -1;
f.comeco:=(f.comeco MOD MAX)+1;
END
ELSE
WRITELN(‘Fila Vazia!’);
END;
2.15 Exemplo do Uso de Filas
• Colorindo Regiões Gráficas
– Algoritmos para colorir regiões de desenhos (matrizes de pontos);
– Região é um conjunto de pontos conectados entre si e que têm a mesma cor;
– Diz-se que dois pontos Pi e Pj estão conectados entre si, se e somente se, é possível partir
de Pi incrementando (ou decrementando) sua abcissa (ou ordenada) e chegar ao ponto Pj.
quatro pontos conectados a P0
Prof. Cláudio H. S. Grecco
18
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
2.16 Algoritmo para Colorir Regiões
• Para colorir uma região R, faça:
– obtenha um ponto inical P0, de cor C0, seguramente pertencente à região R;
– obtenha uma nova cor C1 para a região R;
– coloque P0 em uma fila V, inicialmente vazia;
– enquanto a fila não esvaziar:
• Remova um ponto P da fila V;
• Insira em V todos os pontos conectados a P, cuja cor seja C0;
• altere a cor de P para C1.
• O desenho será representado por uma matriz NxN
– cada elemento da matriz representa um ponto;
– cada pixel é discriminado pelas coordenadas da sua posição na matriz;
– cada elemento da matriz armazena um número correspondente à cor do ponto.
TYPE Ponto = RECORD
ABCISSA: INTEGER;
ORDENADA: INTEGER;
END;
Elem = PONTO;
Ou então
PROCEDURE InsPonto (VAR V:Fila; X,Y:INTEGER);
BEGIN
insere(V,X);
insere(V,Y);
END;
PROCEDURE RemPonto (VAR V:Fila; VAR X,Y:INTEGER);
BEGIN
X:=remove(V);
Y:=remove(V);
END;
Prof. Cláudio H. S. Grecco
19
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
PROCEDURE Colorir(VAR M:Imagem; X,Y, Cor:INTEGER);
VAR V:Fila;
C0: INTEGER;
BEGIN
C0:=M[X,Y];
inicializa(V);
InsPonto(V,X,Y);
WHILE NOT vazia(V) DO
BEGIN
RemPonto (V,X,Y);
IF M[X+1,Y]=C0 THEN InsPonto(V,X+1,Y);
IF M[X-1,Y]=C0 THEN InsPonto(V,X-1,Y);
IF M[X,Y+1]=C0 THEN InsPonto(V,X,Y+1);
IF M[X,Y-1]=C0 THEN InsPonto(V,X,Y-1);
M[X,Y]:=Cor;
END;
END;
2.17 Filas Duplas (Deques)
São filas de duas extremidades, onde as inserções, remoções e consultas são permitidas
apenas nos extremos (Double-Ended-Queue). Além do tipo convencional existem dois
tipos:
– Entrada Restrita: a inserção só pode ser efetuada em uma das extremidades (no início
ou no final);
– Saída Restrita: a remoção só pode ser efetuada em um dos extremos (início ou final).
2.18 Implementação de Fila Dupla
A Fila Dupla é representada por uma lista circular.
• Registro em Pascal
CONST MAX= 50;
TYPE Elem= REAL;
TYPE Deque= RECORD
comeco,
final,
total: INTEGER;
memo: ARRAY[1..MAX] OF Elem;
END;
• Além do insere da fila (InsereFinal) e do remove da fila (RemoveInício) é possível criar o
InsereInicio e o RemoveFinal
• As funções Vazia e Cheia são idênticas
Prof. Cláudio H. S. Grecco
20
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
3. Lista Encadeada
3.1 Alocação de Memória
• Estática
A quantidade total de memória utilizada pelos dados é definida em tempo de compilação de
forma imutável e durante toda execução a quantidade de memória utilizada não varia. Ideal
para uso com listas seqüenciais.
• Dinâmica
– O programa é capaz de criar novas variáveis enquanto executa;
– Áreas de memória passam a existir durante a execução do programa;
– Ideal para uso com listas encadeadas.
3.2 Alocação Dinâmica em Pascal
Tipo de alocação onde cada variável possui um número indefinido de bytes
alocados na memória. Durante a execução é alocada ou desalocada memória para a variável
com qualquer tamanho. As variáveis locais em Pascal são alocadas dinamicamente, porém
de forma automática. É necessário o uso de uma variável do tipo Ponteiro, para a utilização
de alocação dinâmica.
– Um ponteiro é uma variável que ocupa 4bytes e aponta para um endereço de memória;
– Na definição de um ponteiro é indicado o tipo que irá conter no endereço.
• Definição de um ponteiro:
– Var nome_do_ponteiro:^tipo_base;
– Exemplo: VAR pint: ^INTEGER; {aponta
p/ inteiro}
• Alocar um ponteiro (reservar espaço de memória):
– New(p);
– Procedimento que aloca memória do tamanho do tipo_base.
• Desalocar um ponteiro (liberar espaço alocado):
– Dispose(p);
– Procedimento que desaloca memória alocada pelo procedimento New(p).
Prof. Cláudio H. S. Grecco
21
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
3.3 Ponteiros em Pascal
Quando um ponteiro não aponta para endereço algum (não foi alocada memória para ele
ainda) contém o valor NIL ( palavra reservada que representa um ponteiro nulo). Caso seja
definido em um programa um ponteiro P, as seguintes definições são válidas:
– P contém o endereço de memória apontado pelo ponteiro;
– P^ representa o conteúdo do ponteiro.
Caso se utilize @n, sendo n uma variável qualquer diferente de ponteiro, retorna o
endereço de n.
3.4 Exemplos do uso de Ponteiro
• Exemplo 1:
TYPE PTR=^INTEGER;
VAR p: PTR;
BEGIN
NEW(p);
IF p=NIL THEN
WRITELN(‘ERRO: Falta de Memória’)
ELSE
BEGIN
p^:= 1500;
WRITELN(p^);
DISPOSE(p);
END;
END.
• Exemplo 2:
VAR n:INTEGER;
p:^INTEGER;
BEGIN
n:= 486;
p:= @n;
WRITELN(‘Conteúdo:’,p^);
END.
Prof. Cláudio H. S. Grecco
22
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
3.5 Ponteiros em Pascal
• Outras subrotinas em Pascal para alocação dinâmica:
– GetMem(p, nbytes): procedimento que aloca uma área de memória com tamanho
nbytes;
– FreeMem(p,nbytes): procedimento que desaloca uma área de memória com tamanho
nbytes;
– MemAvail: retorna o número de bytes disponível na memória;
– MaxAvail: retorna o número de butes do maior bloco disponível na memória;
– SEG(variável): função que retorna o segmento da variável passada;
– OFS(variável): função que retorna o deslocamento (offset) da variável passada;
3.6 Implementando um Lista Encadeada
Ao invés de manter os elementos agrupados numa área contígua de memória, na
lista encadeada os elementos podem ocupar quaisquer células. Para manter a relação da
ordem linear, junto com cada elemento é armazenado o endereço do próximo (um ponteiro
para o elemento seguinte). Os elementos são armazenados em blocos de memória
denominados nodos que contêm:
– um elemento da lista;
– um ponteiro para o próximo elemento;
Para acessar a lista encadeada basta saber o endereço do primeiro elemento; o
último elemento da lista aponta para NIL, indicando o fim da lista. Característica de uma
lista encadeada:
– Facilidade de inserção e remoção no meio da lista, pois não necessita realizar
movimentações;
– A lista cresce indeterminadamente, enquanto houver memória livre;
– Para acessar um elemento específico da lista, deve-se percorrer todos os anteriores;
– Espaço adicional é perdido para guardar os ponteiros;
Prof. Cláudio H. S. Grecco
23
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
3.7 Estrutura Física e Lógica
3.8 O Tipo Lista Encadeada em Pascal
• Componentes da lista:
– Nodos da lista (composto por um elemento e um ponteiro para o próximo);
– Endereço do primeiro nodo.
• Declaração em Pascal:
CONST SUCESSO = 1;
FALTADEMEMORIA = 2;
LISTAVAZIA = 3;
TYPE Elem = INTEGER;
PtrNodo = ^Nodo;
Nodo = RECORD
memo: Elem;
elo: PtrNodo;
END;
ListaEnc = ^Nodo;
3.9
Trabalhando com Lista Encadeada
• Criando Listas Encadeadas:
FUNCTION CriaLista (VAR l:ListaEnc);
BEGIN
l:=NIL;
END;
Prof. Cláudio H. S. Grecco
24
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Inserindo no Início da Lista:
FUNCTION InsereInicio (VAR l:ListaEnc; no:Elem):BYTE;
Var pn : PtrNodo;
BEGIN
NEW (pn);
IF pn = NIL THEN
InsereInicio:=FALTADEMEMORIA
ELSE
BEGIN
pn^.memo:=no;
pn^.elo:=l;
l:= pn;
InsereInicio:=SUCESSO;
END;
END;
• Removendo do Início:
PROCEDURE RemoveInicio(VAR l:ListaEnc;VAR no:Elem):BYTE;
Var pn : PtrNodo;
BEGIN
IF l = NIL THEN
RemoveInicio:=LISTAVAZIA
ELSE
BEGIN
pn:=l;
l:= pn^.elo;
no:= pn^.memo;
DISPOSE (pn);
RemoveInicio:=SUCESSO;
END;
END;
• Removendo do Início:
PROCEDURE RemoveInicio(VAR l:ListaEnc;VAR no:Elem):BYTE;
Var pn : PtrNodo;
BEGIN
IF l = NIL THEN
RemoveInicio:=LISTAVAZIA
ELSE
BEGIN
pn:=l;
l:= pn^.elo;
no:= pn^.memo;
DISPOSE (pn);
RemoveInicio:=SUCESSO;
END;
END;
3.10 Lista Encadeada com Descritor
Algumas dificuldades da lista encadeada podem ser resolvidas com a adição de um
descritor. O uso do descritor serve para:
– determinar o número de elementos, pois, sem utilizar descritor, deve-se
percorrer toda a lista;
Prof. Cláudio H. S. Grecco
25
Universidade Castelo Branco
–
-
Algoritmos e Estruturas de Dados II
acessar o último elemento da lista, pois, sem empregar o descritor, deve-se
visitar todos os elementos intermediários;
• Descritor contém:
– endereço do primeiro elemento;
– endereço do último elemento;
– quantidade de elementos da lista.
3.11 Lista Encadeada com Descritor em Pascal
• Declaração em Pascal:
TYPE Elem = INTEGER;
PtrNodo = ^Nodo;
Nodo = RECORD
memo: Elem;
elo: PtNodo;
END;
ListEnc = RECORD
primeiro,
ultimo :PtrNodo;
total :Integer;
END;
3.12 Encadeamento com Descritor em Pascal
• Criando Listas Encadeadas com Descritor:
PROCEDURE CriaLista (VAR l:ListaEnc);
BEGIN
l.primeiro:=NIL;
l.ultimo:=NIL;
l.total:=0;
END;
• Determinando a quantidade de Elementos:
FUNCTION TamLista (l:ListaEnc):INTEGER;
BEGIN
TamLista:=l.total;
END;
Prof. Cláudio H. S. Grecco
26
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
• Inserindo no Início da Lista Encadeada com Descritor:
FUNCTION InsereInicio (VAR l:ListaEnc; no:Elem):BYTE;
Var pn : PtrNodo;
BEGIN
NEW (pn);
IF pn = NIL THEN
InsereInicio:=FALTADEMEMORIA
ELSE
BEGIN
pn^.memo:=no;
pn^.elo:=l.primeiro;
l.primeiro:= pn;
IF l.total= 0 THEN
l.ultimo := pn;
l.total:=l.total+1;
InsereInicio:=SUCESSO;
END;
END;
• Inserindo no Fim da Lista Encadeada com Descritor:
FUNCTION InsereFim (VAR l:ListaEnc; no:Elem):BYTE;
Var pn : PtrNodo;
BEGIN
NEW (pn);
IF pn = NIL THEN
InsereFim:=FALTADEMEMORIA
ELSE
BEGIN
pn^.memo:=no;
pn^.elo:=NIL;
IF l.total= 0 THEN
l.primeiro := pn
ELSE
l.ultimo^.elo:=pn;
l.ultimo:=pn;
l.total:=l.total+1;
InsereFim:=SUCESSO;
END;
END;
3.13 Lista Duplamente Encadeada
Algumas dificuldades da lista encadeada (com ou sem descritor) podem ser resolvidas com
o duplo encadeamento, neste caso, a lista só pode ser percorrida em um sentido, pois possui
um elo apenas para o próximo elemento.Na lista duplamente encadeada cada nodo possui:
– um elemento;
– um ponteiro para o próximo elemento;
– um ponteiro para o primeiro elemento.
Quando um nodo não possui próximo ou anterior o elo respectivo aponta para NIL.
Prof. Cláudio H. S. Grecco
27
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
3.14 Lista Duplamente Encadeada em Pascal
• Declaração em Pascal
TYPE Elem = INTEGER;
PtrNodo = ^Nodo;
Nodo = RECORD
memo: Elem;
anterior,
posterior: PtrNodo;
END;
ListaDEnc = RECORD
primeiro,
ultimo :PtrNodo;
total :Integer;
END;
• Inserindo no Início de um Lista Duplamente Encadeada
FUNCTION InsereInicio (VAR l:ListaDEnc; no:Elem):BYTE;
Var pn : PtrNodo;
BEGIN
NEW (pn);
IF pn = NIL THEN
InsereInicio:=FALTADEMEMORIA
ELSE
BEGIN
pn^.memo:=no;
pn^.anterior:=NIL;
IF l.total=0 THEN
l.ultimo:=pn
ELSE
l.primeiro^.anterior:=pn;
pn^.posterior:=l.primeiro;
l.primeiro:=pn;
l.total:=l.total+1;
InsereInicio:=SUCESSO;
END;
END;
• Inserindo no Fim da Lista Encadeada com Descritor
FUNCTION InsereFim (VAR l:ListaDEnc; no:Elem):BYTE;
Var pn : PtrNodo;
BEGIN
NEW (pn);
IF pn = NIL THEN
InsereFim:=FALTADEMEMORIA
ELSE
BEGIN
pn^.memo:=no;
pn^.posterior:=NIL;
IF l.total = 0 THEN
l.primeiro := pn
ELSE
l.ultimo^.posterior:=pn;
pn^.anterior:=l.ultimo;
l.ultimo:=pn;
l.total:=l.total+1;
InsereFim:=SUCESSO;
END;
END;
Prof. Cláudio H. S. Grecco
28
Universidade Castelo Branco
-
Algoritmos e Estruturas de Dados II
3.15 Outras formas de Encadeamento
• Encadeamento Circular
Em uma lista com encadeamento circular o campo de ligação do último nodo
armazena o endereço do primeiro. A variável ponteiro para lista pode apontar para o último
elemento, com isso, o acesso ao primeiro e o último é rápido.
• Encadeamento Duplo Compacto
Técnica que permite armazenar tanto o endereço do nodo anterior quanto do
posterior na mesma variável ponteiro. Realiza-se um Ou-Exclusivo entre os dois endereços.
Prof. Cláudio H. S. Grecco
29
Download