Apontamentos Data Strutures
Apontamentos Data Strutures
Apontamentos Data Strutures
APOLITÉCNICA
Escola Superior de Gestão, Ciência e Tecnologias
Notas de Algoritmos e Estrutura de Dados
Capítulo I
Listas
Uma lista, como array, pode conter uma sequência ordenada de registos
(records) com elementos disponíveis de forma consecutiva -Lista Estática
Sequencial- ou não consecutiva -Lista Estática Encadeada.
Além disso, a(1) é o primeiro elemento, a(i) precede a(i+1), e a(n) é o último
elemento.
2
Vantagem:
Desvantagem:
Quando usar:
listas pequenas;
inserção/remoção no fim da lista;
tamanho máximo bem definido.
1) Acesso a um elemento
Escrever(A[i]);
2) Actualização
A[i] 'Valor'
3) Tamanho da Lista
início
tamanho n;
Fim;
3
{ considerando que a posição foi obtida em um procedimento de consulta
executado préviamente }
Procedure Inserir()
início
Para j de n+1 até i+1 passo –1 faça
A[j] A[j-1];
Fim-para
A[i] 'novo valor';
n n+1;
Fim;
Procedure Remover()
Inicio
Para j de i até n-1 passo 1 faça
A[j]A[j+1];
Fim-para
nn-1;
Fim;
1.1.2 Exercícios
4
gere uma lista L2 onde cada registo contém dois campos de
informação: elem contém um elemento de L1, e count contém quantas
vezes este elemento apareceu em L1.
elimine de L1 todas as ocorrências de um elemento dado, L1
ordenada.
assumindo que os elementos da lista L1 são inteiros positivos, forneça
os elementos que aparecem o maior e o menor número de vezes
(forneça os elementos e o número de vezes correspondente)
Cada registo é:
ou
Encadeamento em matrizes
5
O registo 2 tornou-se disponível para as próximas inserções...
Vantagens:
6
apenas os ponteiros são alterados (lembre que cada registo pode
conter elementos muito complexos);
Desvantagens:
Quando usar:
quando for possível fazer uma boa previsão do espaço utilizado (lista
principal + Dispo) e quando o ganho dos movimentos sobre a perda
do acesso directo a cada elemento for compensador.
7
lig: endereço;
fim;
Lista = registo
A: matriz[1..N] de Rec;
Prim, Dispo: fimereco;
fim;
declarar L: lista;
Function Primeiro (L): T;
Início
Se L[prim] <> 0 então
Primeiro L[info];
Fim-se
Fim;
2) Qual é o último ?
Function Ultimo(L): T;
Declarar p: fimereco;
Se L[prim] = 0 então
{Retorna lista vazia }
Senão
p L[prim];
Enquanto Link[p] <> 0 faça
p Link[p]; {L[info] é o último elemento}
fim-enquanto
Fim-se
Ultimo L[info];
Fim;
8
Se L(Prim) = 0 então
Nelem 0 {lista vazia}
Senão
p L(Prim);
Nelem 1;
Enquanto Link[p] <> 0 faça
Inserção
Eliminação
9
1) Inserção após o registo de endereço k
Inicio
Se L(Dispo) <> 0 então {se a Dispo está vazia, nao
pode inserir!}
ObterNo(j);
L[info] valor;
Link[j] Link[k];
Link[k] j;
Senão
Escrever(`não pode inserir registo`)
Fim;
Procedure Remover(L,k,j);
Inicio
Link[k] Link[j];
DevolverNo(j);
Fim;
3) Casos especiais
10
Procedure Insere_prim(L, valor);
Declarar j: fimereco;
Inicio
ObterNo(j);
L[info] valor;
Link[j] Link(Prim);
Link(Prim) j;
Senão
Escrever(“não pode inserir”)
Fim-se;
Fim;
OBS: No caso da lista estar vazia, Prim passa a apontar o único elemento da
lista.
Inicio
Se L(Dispo) <> 0 então { lista vazia ou prim =
0 }
Obter_No(j);
L[info] valor;
Link[j] 0; {null}
L(Prim) j;
Fim-se;
Fim;
11
Procedure Remove_Primeiro(L);
Declarar j: fimereco;
Início
j Link(Prim);
Link(Prim) Link(link[Prim]);
DevolverNo(j);
Fim;
Senão
Acessa_iesimo Verdadeiro;
dadoL[info]; { L[info] é o elemento
procurado}
Fim-se;
Fim;
12
5) Eliminar o registo de conteúdo dado pela variável valor
pa p;
p Link[p];
acabou (p=0);
Senão
acabou Verdadeiro;
Fim-se;
Se p=0 então
Escrever(“não existe o conteúdo
`valor' na lista”)
Senão
Se pa = 0 então
Prim Link(Prim); { eliminar
o primeiro elemento }
Senão
Link[pa] Link[p];
DevolverNo(p);
Fim-se
Fim-se
Fim-enquanto
Fim;
13
1.2.2 Alocação de Nó em Lista Estática Encadeada
Inicialização da Lista
Procedure Init(L);
Inicio
L(Dispo) 1; {primeiro elemento}
L(Prim) 0; {lista principal está vazia}
Para i de 1 até n-1 passo 1 faça
Link[i] i+1;
Link[n] 0; {receber 0 corresponde ao
nil}
Fim-para;
Fim;
Procedure Obter_No(j);
Inicio
j Dispo; { A dispo passa a apontar para quem
ela apontava }
Dispo Link[Dispo];
Fim;
14
Devolver_No(j): devolve um registo de índice j à Dispo.
Procedure Devolver_No(j);
Inicio
Link[j] Dispo;
Dispo j;
Fim;
OBS:
1.2.3 Exercícios
15
2) Escreva os seguintes algoritmos que implementem Listas Encadeadas
Estáticas (em array) com sentinela:
criação de lista;
busca em lista ordenada e não ordenada
inserção e eliminação de elementos
Type Tp = ^T
Essa declaração expressa que valores do tipo Tp são ponteiros para dados do
tipo T.
Portanto, lemos o simbolo ^ como sfimo ponteiro para... e na declaração
acima lemos Tp é um ponteiro para variáveis do tipo T.
16
Idéia: O programador declara apenas o tipo de registo que contém um
elemento da lista, e avisa que a alocação será dinâmica. Sempre que
requisitar um registo novo para a inserção, ou liberar um registo a ser
eliminado, o programador lança mão de duas rotinas pré-definidas para este
fim.
Sfimo o registo:
alocação alocação
array L.A[p] dinâmica p^
ponteiro: p
objecto: p^
campos: p^(info)
link(p)
17
1: L nil;
2: Se (p = nil) então ...
18
1.3.1 Manipulação de Registos
1) Declaração de Variável
Declarar p: ^rec;
2) Criação de um registo
new(p);
A partir daí:
4) Liberação de um registo
19
1.3.2 Definição da ED
Tipo prec = ^rec;
Lista = prec;
rec = registo
info: T; {seu tipo preferido}
lig: Lista
Fim;
20
Procedure Insere_Prim(L, valor);
Declarar p: Lista;
Inicio
new(p);
p^(info) valor;
Link(p) nil;
L p;
Fim;
p^(info) valor;
link(p) L;
L p;
fim;
21
4) Acesso ao primeiro elemento da lista
Function Prim(L): T;
Inicio
Prim L^(info);
Fim;
Function Ultimo(L): T;
Inicio
Se L = nil então
Escrever(“lista vazia”)
Senão
p L;
Enquanto link(p) <> nil faça
p link(p);
Fim-enquanto
Ultimo p^(info); {p^(info) é último
elemento}
Fim-se; {p^ é o último registo}
Fim;
p L;
Nelem 1;
Enquanto link(p) <> nil faça
Nelem Nelem + 1;
22
p link(p);
Fim-enquanto;
Fim-se;
fim;
23
9) Remoção do primeiro elemento
Procedure Remove_Prim(L);
Inicio
p L;
L link(L);
dispose(p)
fim;
inicio
pa nil;
p L;
acabou (p = nil);
Enquanto (not acabou) faça
Se p^(info) < v então
pa p;
p link(p);
acabou (p = nil);
Senão
acabou true;
{Enquanto acaba aqui!}
Fim-enquanto
24
{ p = nil ou p^.info = v ou p^.info > v }
Se (p = nil) então
Escrever("lista não contém v")
Senão
Se p^(info) > v então
Escrever("lista não contém v")
Senão
Se pa = nil então
L link(p)
Senão
Link(pa) link(p);
Fim-se
Fim-se
Fim-se
dispose(p);
fim;
Procedure Cria_Lista_num(L);
Declarar L, q: Lista;
25
Inicio
L nil; { começa com lista vazia }
Enquanto n > 0 faça
new(q);
link(q) L;
q^(info) n;
L q;
n n-1;
Fim-enquanto;
fim;
Procedure Printlist(p);
inicio
Se (p <> nil) então
Escrever(p^(info));
Printlist(link(p));
Fim-se;
Fim;
26
1.3.4 Busca em Lista Dinâmica
No caso de p = nil o comando pode ser inválido pois p^.info não existe!
acabou false;
Enquanto (p <> nil) e (not acabou) faça
Se p^(info) = x então
acabou true
Senão
p link(p);
Supondo que inicializamos uma lista apontada por L com a lista vazia,
faremos:
Procedure Create(L);
Declarar sentinela: Lista;
Inicio
new(sentinela);
L sentinela;
27
Fim;
Inicio
p L;
sentinela^(info) x;
Enquanto p^(info) <> x faça
p link(p);
fim-enquanto
Se (p = sentinela) então
p nil; {elemento não encontrado}
Fim-se
Fim;
Inicio
p L;
sentinela^(info) x;
Enquanto p^(info) <> x faça
p link(p);
Fim-enquanto
Se (p = sentinela) Então
Inicio
{ inserção no começo da lista}
p L; {aponta primeiro}
new(L); {novo registo é primeiro}
L^(info) x;
Link(L) p;
28
Fim-se;
Fim;
Essa "ordenação" na realidade não tem custo algum, porque fazemos uso das
características da lista encadeada. Isso não ocorre com estruturas estáticas
como o array e os arquivos sequenciais.
O bloco de busca pelo elemento realiza dois testes para cada elemento da
lista. Esse esforço pode ser reduzido introduzindo o recurso de sentinela.
Declarar
p, pa: Lista;
acabou: booleana;
Inicio
pa nil;
p L;
acabou (p = nil);
29
Enquanto (not acabou) faça
Se p^(info) < v Então
pa p;
p link(p);
acabou (p = nil);
Senão
acabou true;
{ p = nil ou p^.info = v ou p^.info > v }
Se (p = nil) Então
Escrever("lista não contém v")
Senão
Se p^(info) > v Então
Escrever("lista não contém v")
Senão
Se pa = nil Então
L link(p)
Senão
Link(pa) link(p);
Fim-se
Fim-se
Fim-se
Fim-se
dispose(p);
Fim-enquanto;
Fim;
new(raiz);
new(sentinela);
link(raiz) sentinela;
Temos acima uma lista com dois elementos: o elemento apontado por raiz
vai ser sempre um dummy (elemento sem conteúdo), e o sentinela sempre
marca o final da lista.
30
O algoritmo abaixo realiza a busca de um elemento e o insere na posição
correcta caso ele não seja encontrado.
Inicio
w2 raiz;
w1 link(w2);
sentinel^(info) x;
Enquanto w1^(info) < x faça
w2 w1;
w1 link(w1);
fim-enquanto
Se (w1^(info) = x) e (w1 <> sentinela) Então
Escrever(“elemento já existe”)
Senão
1.3.6 Exercício
Características
31
Em alguns casos pode-se desejar percorrer uma lista nos dois sentidos
indiferentemente.
Nestes casos, o gasto de memória imposto por um novo campo de
ponteiro pode ser justificado pela economia em não reprocessar a lista
toda.
32
Fim;
j^((esq^)(dir)) j;
pont^(esq) j;
Fim;
33
dispose(pont);
Fim;
1.4.1 Exercício
34
Reescreva os algoritmos de inserção e eliminação de um elemento em uma
lista duplamente encadeada de forma a incluir uma chamada à Função
Busca_Dup_Ord(x).
w2w1;
w1link(w1);
Fim-enquanto
Se (w1^(chave) = x) e (w1 <> sentinela) então
Escrever(“elemento já existe”)
Senão
{inserir entre w1 e w2}
Fim;
35
Quando consideramos uma lista circular encadeada, podemos utilizar apenas
um registo auxiliar, como indicado no algoritmo de busca abaixo:
antpont;
pontlink(pont);
Fim-enquanto;
Se pont^(chave) = x and (pont<>ptlista) então
{elemento já existe}
Senão
{inserir entre ant e pont}
Fim;
1.5.1 Exercícios
3) Qual alteração devemos fazer no caso das listas não serem ordenadas ?
36
1.6 Listas Generalizadas
Uma lista generalizada é aquela que pode ter como elemento ou um átomo
ou uma outra lista (sub-lista).
Toda lista pode ser representada usando-se a estrutura de nó onde:
Exemplo:
L1 = (a, (b, c) )
L2 = (a, b, c)
37
1.6.1 Implementação de Listas Generalizadas
Type
pont_no = ^no;
no = record
cauda: pont_no;
case tag: boolean of
true: (pont: pont_no);
false: (atomo: tipo_elem);
Fim;
Lista = pont_no;
Listas Compartilhadas
Exemplo:
Listas Recursivas
Exemplo:
L5 = (a, L5)
38
1.6.3 Exercícios
A = B = ( ( a, b), (c, d) )
A = (a, (b, c) ) e B = (a, b, (c))
Inicio
respfalse;
Se (S=nil ) e (T=nil) Então
resptrue;
Senão
Se (S<>nil) e (T<>nil) Então
Se ((S^(tag) = T^(tag))
Se (S^(tag)=false) Então
resp (S^(atomo), T^(atomo))
Senão
resp igual(S^(pont), T^(pont));
Fim-se
Se resp Então
39
respigual(S^(cauda), T^(cauda));
Fim;
Fim-se
igualresp;
Fim-se
Fim;
Matriz Esparsa
Linha,
40
Coluna e
Valor
41
1.7.1 Operações
L[i] ^
42
Como consequência dessas operacões, alguns elementos podem deixar de
ser nulos, enquanto que outros podem se tornar nulos.
Esse exemplo ilustra que, quando um elemento a(i,j) deve ser eliminado, ele
deve ser eliminado, de fato, de duas listas: L[i] e L[j].
p^(valor) p^(valor) + k;
Se p^(valor) = 0 então
43
p p^(pc);
eliminar(i,j);
fim
senão p p^(pc);
fim;
fim;
fim;
1.7.2 Usabilidade
Factor Espaço
Matriz Esparsa
Representação bidimensional
44
Conclusão:
5n + nl + nc < nl * nc
ou seja, quando:
Factor Tempo
As operações sobre listas cruzadas podem ser mais lentas e complicadas que
para o caso bidimensional.
Portanto, para algumas aplicacões, deve ser feita uma reavaliação de tempo-
espaço.
1.7.3 Exercícios
45
6. multiplique duas matrizes esparsas
7. calcule a inversa de uma matriz esparsa
PILHA
Conceito
46
Figura 1: Representação dos vários momentos de uma
pilha
Isto acontece uma vez que a estrutura pilha não mantém nenhum
registo dos elementos que lhe são inseridos ou removidos. No caso
deste registo se fazer necessário, deve ser mantido a parte.
47
vai remover o elemento constante no topo da pilha p e armazená-lo
na variável x.
i pop(p);
push(p,i);
A operação TOPO_PILHA, assim como POP não pode ser aplicada em uma
pilha vazia. Caso se aplique uma destas 2 operações em uma pilha vazia,
incorre-se em um erro conhecido como underflow (tentativa de se retirar um
elemento de uma pilha vazia).
Pilha vazia
48
Insere(A)
Insere(B)
Retira(B)
Insre(C)
Retira(C)
Retira(A)
49
2.3 Exemplo do Uso de Pilhas
Chamadas de procedimentos
Operações
50
1. criar (P) - criar uma pilha P vazia
topo topo + 1;
P[topo] x;
Fim-se
fim;
51
procedure pop (P, topo);
inicio
Se topo = 0 então
"PILHA VAZIA"
senão
topo topo-1;
fim-se
fim;
52
Operações
53
function vazia (p): boolean;
inicio
vazia (p = nil);
fim;
inicio
aux p;
p link(p);
dispose (aux);
fim-se
fim.
54
A notação tradicional é ambígua e, portanto, obriga o pré-estabelecimento de
regras de prioridade.
Isso torna a tarefa computacional menos simples. Outras notacões são
apresentadas a seguir, considerando-se apenas operações binárias (com dois
operandos):
Exemplo:
tradicional: A * B - C / D
parentizada: ((A*B)-(C/D))
Exemplo:
tradicional: A * B - C / D
polonesa: - * A B / C D
Exemplo:
tradicional: A * B - C / D
polonesa reversa: A B * C D / -
55
retira o número de operandos; calcula e empilha o valor resultante
até que chegue ao final da expressão
Exemplo: A B / D E * + A -
x proxsimb(E);
Se x é operando então push(x,pilha)
senão
56
2.7 Exercícios
1) Seja a função esvazie( ) tal que, recebe uma pilha como entrada, esvazie a
pilha descartando todos os seus elementos.
Escreva a função esvazie( )
Fila
Conceito
57
A disciplina de acesso que rege a inserção e remoção de elementos
em uma fila é denominada FIFO ("First In First Out"), ou em sua
forma aportuguesada, PEPS ("Primeiro a Entrar Primeiro a Sair").
Operações associadas:
Sequencial ou
Encadeada ? Dinâmica ou Estática ?
58
3.2 Implementação Sequencial de Fila
F: fila;
Começo, {posição anterior ao primeiro elemento}
Fim: índice; {posição do último elemento}
Fim Fim + 1;
F[Fim] x;
Fim
Senão
{ OVERFLOW }
Fim;
59
Se Começo = Fim Então
{FILA VAZIA}
Fim;
60
{só se lista não vazia}
Inicio
Começo nil;
Fim nil;
Fim;
61
dispose(p);
fim
Fim;
Portanto, ficaria:
Começo Começo + 1;
Se Começo = Fim Então
Começo 0;
62
Fim 0;
Fim;
Fim;
Fim;
A lista estaria com no máximo dois elementos, mas ainda ocorreria overflow
com a lista quase
vazia.
Alternativa: Forçar Fim a usar o espaço liberado por Começo (Fila Circular)
63
Fila Circular Implementada como Anel
64
Começo (Começo + 1) mod m;
Fim;
Começo = Fim
3.6 Exercício
65
ÁRVORES
1. Introdução
Árvores são estruturas de dados não lineares que caracterizam uma relação de
hierarquia entre os dados (nós) que as compõem, onde um conjunto de dados
pode se apresentar hierarquicamente subordinado a outro conjunto de dados.
2. Terminologia
Por definição, cada nó da árvore é a raiz de uma subárvore. O no de subárvores
de um nó é o grau daquele nó. Um nó de grau 0 é denominado folha ou nó
terminal.
A altura de uma árvore é igual ao seu número de níveis. Assim sendo, a altura
do exemplo colocado acima é igual a 4.
66
A raiz de uma árvore é denominada pai das raízes de suas subárvores. As
raízes das subárvores de um mesmo nó são denominados irmãos, que, por sua
vez, são filhos de seu nó pai.
PAI RAIZ
NÍVEL 1
A
FOLHA ALTURA
NÍVEL 3
D E F
FOLHA
NÍVEL 4
G H I
A A
B C C B
D E F D F E
G H I G H I
67
3. Representações Básicas para Árvores
B C
D E F
G H I
68
Índice Informação 1a Subárvore 2a Subárvore 3a Subárvore
1 A 2 3 0
2 B 0 0 0
3 C 4 5 6
4 D 7 8 0
5 E 0 0 0
6 F 9 0 0
7 G 0 0 0
8 H 0 0 0
9 I 0 0 0
4. Árvores Binárias
4.1. Definição
69
Uma árvore binária cheia é aquela em que, se v apresenta uma de suas
subárvores vazia então v se encontra obrigatoriamente no último nível da árvore.
Pode-se afirmar que uma árvore binária cheia também é completa e
estritamente binária. O item c) da figura abaixo apresenta uma árvore binária
cheia.
a) b) c)
a) b) c) d)
A altura h mínima para uma árvore binária de n nós é conseguida sempre que
esta árvore binária for do tipo completa. É possível deduzir a altura mínima para
uma árvore de n nós através da seguinte fórmula:
h min = 1 + log n
onde n = número de nós da árvore.
70
Esta dedução pode ser comprovada ao se analisar a árvore do item c) da figura
acima, que por ser do tipo cheia apresenta a altura mínima (igual a 4) dados os
seus 15 nós.
Uma das mais importantes operações que pode ser aplicada em uma árvore
binária é o caminhamento, isto é, a habilidade de se mover através de todos os
seus nós.
b) Caminhamento Central:
1) Visite a Subárvore Esquerda;
2) Visite a Raiz;
3) Visite a Subárvore Direita;
Ex:
71
A figura abaixo mostra a seqüência em que o caminhamento central (Esquerdo,
Raiz, Direito) é executado em uma árvore binária.
ERD
A
ERD ERD
B C
ERD
4
D
3 2
1
O ponto de partida é a raiz da árvore 1 (nó A). Na árvore 1 devem ser visitadas a
sua subárvore da esquerda, a sua raiz e depois a sua subárvore da direita. A
subárvore esquerda da árvore 1 é a árvore 2, cuja raiz é o nó B.
72
Na subárvore 4 devem ser visitadas a sua subárvore da esquerda, a sua raiz e
depois a sua subárvore da direita. Uma vez que não existe subárvore à
esquerda de C, o próximo nó a se visitar é a raiz (nó C) da árvore 4. Uma vez
que também não existe subárvore à direita de C, o caminhamento central na
árvore 4 está terminado.
a * b + c / (d + e)
73
O processo de colocação um elemento do conjunto de dados na árvore obedece
a seguinte regra:
42-51-19-37-42-86-71-10-75-22-31-42
No caso das listas ligadas, ponteiros serviam para armazenar os endereços dos
elementos vizinhos a um nó qualquer da lista.
No caso das árvores binárias, os ponteiros terão a função de armazenar os
endereços dos filhos de cada nó raiz de uma subárvore. Estes filhos, enquanto
raízes de outras subárvores, também armazenarão o endereço de seus filhos, e
assim por diante. Nas folhas da árvore, os nós não possuem mais filhos, por isso
seus ponteiros vão apontar para NIL, indicando o fim daquela subárvore.
Esquematicamente:
74
ESQ ITEM DIR RAIZ
A
ESQ ITEM DIR ESQ ITEM DIR
B C
7.1. CAMINHAMENTO
75
O procedimento recursivo que implementa o caminhamento pré-fixado pode ser
assim implementado:
procedure PreFix(raiz);
Início
Se raiz <> NIL então
Escrever(raiz^.item);
PreFix(raiz^.esq);
PreFix(raiz^.dir);
Fim-se
Fim; { procedure PreFix }
procedure Central(raiz);
Início
Se raiz <> NIL Então
Central(raiz^.esq);
Write(raiz^.item);
Central(raiz^.dir);
Fim-se;
Fim; { procedure Central }
procedure PosFix(raiz);
Início
Se raiz <> NIL então
PosFix(raiz^.esq);
PosFix(raiz^.dir);
Write(raiz^.item);
Fim-se;
Fim; { procedure PosFix }
7.2. INSERÇÃO
76
No caso da inserção de elementos em uma árvore binária de ordenação e
pesquisa, o esquema seria:
77
7.3. PESQUISA
.....
.....
78
PesqArv(raiz, 20, ponteiro);
Se ponteiro = NIL então
Escrever("O elemento não existe na árvore")
Senão
Escrever("O elemento 20 da árvore: ", ponteiro^.item);
.....
.....
a)
b) 1-2-3-4-5-6-7-8-9
c) 5-4-3-2-1-6-7-8-9
d) 9-8-7-6-5-4-3-2-1
e) 5-2-7-1-6-4-3-9-8
1 5 9 5
2 4 6 8 2 7
3 3 7 7 1 4 6 9
4 2 b) 8 6 3 8
1 5 d)
5 9
4 c)
a) 6
3
7
2
8
1
9
Na média a pesquisa será mais eficiente na árvore de item d) pois sua altura é
mínima em relação aos seus 9 nós. A pesquisa será mais ineficiente nas árvores
a) e c), pois tratam-se de árvores degeneradas. A organização física das árvores
da figura acima foi ditada pela ordem em que os dados foram inseridos. As
árvores a) e c), mais ineficientes, originaram-se de um conjunto de dados
previamente ordenado. Já a árvore d), mais eficiente, originou-se de um
conjunto de dados não ordenado.
7.4. REMOÇÃO
79
a) quando o nó a ser removido possui uma ou ambas as subárvores vazias;
b) quando o nó a ser removido possui ambas as subárvores ocupadas.
80
temp posição;
posição Link(posição^.esq);
dispose(temp);
Senão
Se posição^.esq = NIL então
temp posição;
posição posição^.dir;
dispose(temp);
temp posição^.dir;
Enquanto temp^.esq <> NIL Faça { procura a posição mais a esquerda }
temp Link(temp^.esq); { possível na subárvore da direita }
Fim-enquanto
Link(temp^.esq) posição^.esq; { liga esta posição na subárvore da esquerda }
tempposição;
posição Link(posição^.dir); { liga o avô com o neto da direita }
dispose(temp);
Fim-se;
Fim-se;
Fim-se;
Fim-se;
Fim; {proc DeletaElementoDaArvore }
B. Grafos
1. Definição
81
As principais características desta estrutura são:
Ex.:
Ex.:
São elas:
82
Inserção de uma curva ou vértice;
Rotação ou redesignação dos vértices pela troca entre eles num dos sentidos;
83
2.2. Operações Binárias
Sejam os grafos G1(V1, A1), G2(V2, A2) e G3(V3, A3), onde V1, V2 e V3 são os
conjuntos de vértices e A1, A2 e A3 são os conjuntos de arestas dos grafos.
A. União
G'(V', A') U G"(V", A") = G(V, A), V = V' U V" e A = A' U A".
Ex.:
B. Intersecção
C. Diferença
84
G'(V', A') - G"(V", A") = G(V, A), V = V' - V" e A = A' - A".
Ex.:
3.1. Cardinalidade
C(V) = 4
A cardinalidade de um conjunto de curvas é igual ao número e elementos que o
compõe. Ex.: Para G(V, A) do exemplo anterior: C(A) = 4.
Dois vértices são adjacentes se existir uma curva entre eles. Dado dois nós x e
y, a representação de uma linha é dada por xy e de um arco por xy.
85
Dois vértices são conexos se são adjacentes ou possuem uma relação de
adjacência, ou seja, dados dois vértices x e y, existe um nodo x1 que é
adjacente a x, outro x2 que é adjacente a x1, e assim sucessivamente.
3.5. Ciclo
3.6. Caminho
86
Ex.:
4. Tipos de Grafos
Ex.:
4.2. Subgrafo
Ex.:
87
4.3. Subgrafo parcial
Ex.:
4.5. Multigrafo
É o grafo que possui ao menos um par de vértices ligados por duas ou mais
curvas.
88
Ex.:
Contra-exemplo:
4.9. Grafo-árvore
89
4. sem ciclos e, inserindo uma nova linha, é possível formar um ciclo.
Ex.:
Exs.:
É um grafo dirigido valorado que possui uma entrada, uma saída e não tem
laços nem ciclos. A entrada é o vértice que serve como extremidade final para
arcos e saída é o vértice que serve apenas como extremidade inicial para arcos.
Este tipo de grafo é utilizado na análise de eventos temporais, que necessitam
do acompanhamento do fluxo de dados em relação ao tempo.
90
Ex.:
Uma fábrica possui cinco módulos de processamento robotizado. Cada módulo
possui uma identificação e uma função específica. A rede de PERT ao lado
representa o tempo de uma peça sendo processada entre os módulos.
91
ma[i,j] =
ma[i,j] =
92
Sua constituição é realizada por um vetor dinâmico ou lista encadeada formando
um índice de vértices. De cada elemento de índice parte uma lista encadeada
descrevendo os vértices adjacentes conectados.
A lista encadeada é formada por nodos que contém o dado do vértice (letra) e o
ponteiro para o vértice adjacente ao indicado no índice (vetor descritor dos
vértices).
Este tipo de matriz representa um grafo a partir de suas arestas. Como exige
muitas vezes a alocação de uma matriz maior do que no método da matriz de
adjacências, não é tão utilizada quanto aquela. A matriz alocada deverá ter
dimensões C(V) x C(A).
93
i[i,j] =
6. Caminhamento em Grafos
94
a ordem de visitação, se for escolhido inicialmente o vértice
C, o adjacente será D (primeiro da fila). Como não há outro
vértice adjacente, escolhe-se o primeiro da fila (D) para
recomeçar o processo. O adjacente a D é B, o qual vai para
a fila. E novamente não há outro adjacente, então escolhe-
se o primeiro da fila, que é B. Repete-se o processo,
visitando-se A, que vai para a fila. Mais uma vez, recomeça
as visitas a partir de A, que era o primeiro da fila. Visita-se
B, mas verifica-se que ele já está marcado como visitado.
Então, visita-se C, mas ele também já foi marcado.
Finalmente, visita-se D, que também já foi visitado,
encerrando, então o caminhamento. A ordem de
caminhamento pode ser vista ao lado.
repete-se o processo até que o vértice procurado seja encontrado ou não haja mais
vértices adjacentes. Se verdadeiro, desempilha-se o topo e procura-se o próximo
adjacente, repetindo o algoritmo;
95
a seqüência de caminhamento inicia com a escolha de um vértice, digamos, A. Sendo A o
vértice inicial, visita-se primeiro um de seus vértices adjacentes, por exemplo, B.
Empilha-se A e B torna-se o novo vértice inicial. Mais uma vez, escolhe-se um adjacente,
no caso há só um, E. Empilha-se B e E torna-se o novo vértice inicial. Como não há mais
vértices adjacentes, desempilha-se o topo, ou seja, B. Mas B também não tem mais
adjacentes, então desempilha-se A, o qual possui C como novo adjacente. Então C torna-
se o novo vértice inicial, empilhando-se novamente A. O vértice adjacente F é escolhido
e C é empilhado. Como F não tem adjacentes, desempilha-se C e visita-se D. O vértice C
é empilhado novamente e D é o novo inicial. Como seu adjacente já foi visitado,
desempilha-se C. Como C não tem mais adjacentes, desempilha-se A. Como A não tem
mais adjacentes, termina a busca. O grafo abaixo indica a ordem de visitação.
7. ORDENAÇÃO
96
Saída: sequência dos n números de entrada reordenada, gerando <a1’, a2’, …, an’>
de forma que: a1’≤ a2’, …, ≤ an’ (para o caso de ordenação crescente) ou
a1’≥ a2’, …, ≥ an’ (para o caso de ordenação decrescente).
Exemplos:
97
1 3 xxx xxx xxx 1 4
2 7 yyy yyy yyy 2 6
3 5 zzz zzz zzz 3 1
4 1 kkk kkk kkk 4 5
5 4 ttt ttt ttt 5 3
6 2 uuu uuu uuu 6 2
98
- método da troca e partição (quicksort)
3. Ordenação por Selecção
- selecção directa
- selecção em árvore (heapsort)
99
Const
TAM = 10
Tipos
V = vector [1..TAM] de inteiros
Var
Vet:v
I , j ,k , temp:inteiro
Achei:logico
Inicio
Para i = 1 até TAM faça
Leia(vet[i])
Fim-para
Para i = 2 até TAM faça
J=1
Achei = Falso
Enquanto (j<i) e (Nao achei) faça /* Compara o */
Se vet[i] < vet[j] Então /* vet[i] com */
Achei = Verdadeiro /* os que estão */
Senão /* à sua */
J = j +1 /* esquerda */
Fim-se
Fim-Enquanto
Se achei Então
Temp = vet[i]
K=i–1
Enquanto k >= j Faça /* Desloca o */
Vet[k+1] = vet[k] /* vector para */
K = k- 1 /* a direita */
Fim-Enquanto
Vet[j] = temp
Fim-Se
Fim-Para
Fim .
Outra versão:
Const
TAM = 10
Tipos
V = vector[1..TAM] de inteiros
Var
Vet:v
I, j, k, temp: inteiro
Achei:Logico
Inicio
Para i = i até TAM Faça
Leia (vet[i])
Fim-Para
Para j = 2 até TAM Faça
Chave = vet[j]
I=j–1
Enquanto (i > 0) e (vet[i] > chave) Faça
Vet[i + 1] = vet[i]
100
I=i–1
Fim-Enquanto
Vet[i + 1] = chave
Fim-Para
Fim.
Segmento 2
Segmento 1 Segmento 4
2 6 1
1 5 9 0 4 8 1
15 1 5 27 directa
2 4 em cada 2
Aplicando inserção
9 0 0 1 13 3 1
segmento:
5 0
Segmento 3 10 1 3
3 5
3 7 1
25 4 4 1
15 1 5
9 0 40 0 4 5 2
5 5
101
27 2 4
0 1
Obtém-se o vector:
1 2 3 4 5 6 7 8 9 10 11 12
15 20 25 10 19 27 40 13 50 41 45 35
Segmento 1 Segmento 2
1 3 5 7 9 11
15 20 25 10 19 27 2 4 6 8 10 12
20 10 27 13 41 35
10 13 20 27 35 41
15 19 25 40 45 50
Obtém-se o vector:
1 2 3 4 5 6 7 8 9 10 11 12
15 10 19 13 25 20 40 27 45 35 50 41
Nesse último passo os elementos estão próximos das suas posições finais, o que leva a
um menor número de trocas.
1 2 3 4 5 6 7 8 9 10 11 12
10 13 15 19 20 25 27 35 40 41 45 50
Algoritmo:
const TAM = 12
Tipos v = vector [1..TAM] de inteiros
var vet: v
np, i, j, inc : inteiro
Início Para i = 1 até TAM Faça
Leia (vet [i] )
Fim - Para
102
Leia (np)
Para i = np até 0 passo -1 Faça
Inc = 2 * * i
Para j = 1 até inc Faça
Método_Shell (vet, inc, j, TAM)
Fim_Para
Fim-Para
Fim.
/*************************************************************/
Procedimento Metodo_shell (Ref vet, r, s, n)
Inicio
Var
i, j, k, temp: inteiro
achei: lógico
Senão
j = j + r
Fim_se
Fim enquanto
Se achei Então
temp = vet [i]
k = i + r
Enquanto k > (j - r) Faça
Vet [k + r] = vet[k]
K= k - r
Fim_enquanto
vet[j] = temp
Fim_se
Fim_para
Fim _Método_Shell
103
sucessivas de pares de elementos. A estratégia de escolha de pares de elementos
estabelece a diferença entre os dois métodos de ordenação por troca.
É um método bastante simplis, porém lento. Onome bolha se deve ao fato de que os
flutuam até a sua corecta posição como bolhas. O Algorítmo é o seguinte:
- A cada passo, cada elemento é comparado com o próximo, Se o elemento estiver fora de
ordem, a troca é realizada
- Realizam-se tantos passos quantos necessários até que não ocorram mais trocas.
Passo 1: 85 500
60 515
170
910
890 910
257 910
650 910
430 910
85 500 60 515 170 890 275 650 430 910
Passo 2: 85 60 500 170 515 275 650 430 890 910
Passo 3: 60 85 170 500 275 515 430 650 890 910
Passo 4: 60 85 170 275 500 430 515 650 890 910
Passo 5: 60 85 170 275 430 500 515 650 890 910
Passo 6: nenhuma troca
Algorítmo
Var
Vet: v
i, temp, lim , k: inteiro
Troca: lógica
104
Início
Para i = 1 até TAM Faça
Leia (vet[i])
Fim _Para
Troca = VERDADEIRO
Lim = TAM – 1
105
Percorrer o vector a partir da direita até que se encontre um V[I] ≤ X
(decrementando o valor de J);
Trocar os elementos V[I] e V[J] (estão forado lugar) efazer I =I +1 e J =J – 1;
Continuar esses processos até que I e J se cruzem em algum ponto do vector;
Aós obtidos os dois elementos do vector através do processo de parição, cada um
é ordenado recursivamnte
Algoritmo:
Procedimento Quicksort (esq, dir: inteiro)
Início
Var
X, i, j, aux :inteiro;
i = esq
j = dir
x = v[ (i + j) Div 2]
Repita
Enquanto x > v [i] faça
i = i +1
Fim-Enquanto
Enquanto x < v [j] faça
j=j-1
Fim-enquanto
Se i <= j Então
aux = v[i]
v [i] = v [j]
v [j] = aux
i=i+1
J=J–1
Fim-se
Até que i > j
Se esq < j Então
Quicksort (esq, j)
Fim-se
Se dir > i Então
Quicksort (i, dir)
Fim-se
Fim.
Nessa abordagem, é realizada uma selcção sucessiva do menor (ou maior) valor
contido no vector. A cada passo este menor (maior) valor é colocado na sua posição
correcta. Repete-se o processo para o segmento que contém os elementos não
seleccionados.
106
5. Selecção Directa
Exemplo:
19 25 10 18 35 17 15 13 TAM
=8
10 25 19 18 35 17 15 13 TAM
=7
10 13 19 18 35 17 15 25 TAM
=6
10 13 15 18 35 17 19 25 TAM
=5
10 13 15 17 35 18 19 25 TAM
=4
10 13 15 17 18 35 19 25 TAM
=3
10 13 15 17 18 19 35 25 TAM
=2
10 13 15 17 18 19 25 35 TAM
=1
Algorítimo:
Const:
TAM = 15
Tipos
V = vector [1..TAM] de inteiros
Var
Vet: v
I, j, temp, pos_menor : inteiro
Início
Para i = 1 até TAM faça
Leia ( vet [i] )
Fim-para
Para i = 1 até TAM - 1 faça
Pos_menor = i
Para j = i + 1 até TAM faça
Se vet [j] < vet [pos_menor] Então
Pos_menor = j
107
Fim-se
Fim-para
Temp = vet [i]
Vet [i] = vet [pos_menor]
Vet [pos_menor] = temp
Fim-para
Fim.
1 ª Fase:
- Monta-se uma árvore binária (heap) contendo todos os elemento do
vector de forma que
o valor contido em qualquer modo seja maior do que os valores de seus
sucessores.
A árvore binária é estruturada no próprio vector da seguinte forma:
2ª Fase:
- Após a formação do heap segue-se a fase de classificação propriamente
dita, na qual o valor
que está na raiz da árvore (maior valor contido na árvore) é colocado na
sua posição correcta,
trocando-o com o elemento de maior índice de árvore (a árvore fica com
1 elemento a menos).
Este novo elemento colocado na raiz pode violar a propriedade do heap,
de modo que deve-se
restaurar o heap novamente. Este procedimento é repetido até que a
árvore fique com um unico
elemento.
108
Exercício 6
56 30 1 38 90 11 95
(1,7)
Esq. = 1...... I=1
Dir =7.........J =7 Y= Vet [ (I+j) Div 2] = Vet [ (1+7) Div 2] = Vet [4] = 38
109
E2 <1 False Fim e Vai a direita F3>2 False {Voltamos para traz B o ultimo que
deixamos para depois}
B (5,7)
Esq. = 5...... I=5
Dir =7.........7=3 Y= Vet [6]=11
Troca 6: I=5, J=7
95 90 56 38 30 11 1
I=6 J=6
......etc,...
fim
110