Você está na página 1de 280

Departamento de Ciências e Tecnologias da Informação

ALGORITMOS E ESTRUTURAS DE
DADOS – FOLHAS DE APOIO

FILIPE SANTOS

Departamento de Ciências e Tecnologias da Informação

Licenciaturas:
Informática e Gestão de Empresas
Engenharia de Telecomunicações e Informática
Engenharia informática

Janeiro 2013

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação

ÍNDICE

Apresentação ......................................................................... 1

Breve Introdução à Lógica Formal ............................................ 4

Cálculo de Hoare e Metodologia de Dijkstra ....................... 21

TDA: motivação ................................................................... 39

TDA: especificação e implementação .................................. 57

Eficiência de Algoritmos ..................................................... 81

Pilhas .................................................................................... 94

Filas .................................................................................... 106

Listas .................................................................................... 115

Conjuntos ............................................................................. 126

Simulação Digital Estocástica .............................................. 132

Árvores ................................................................................ 151

Filas Prioritárias .................................................................. 195

Tabelas ................................................................................ 203

Grafos ................................................................................... 226

Ordenação ............................................................................ 246


Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Apresentação - 1

OBJECTIVOS

A disciplina aprofunda técnicas de desenvolvimento de algoritmos


correctos e bem estruturados e introduz a problemática da
complexidade algorítmica.

Proporciona ainda uma introdução às técnicas de estruturação de


dados no contexto da metodologia de programação centrada em
objectos, com ênfase em princípios de abstracção, encapsulação e
modularização.

Concluída a disciplina o aluno deverá ser capaz de:

1. Descrever, explicar e aplicar conceitos e técnicas de programação


para obtenção de programas correctos;
2. Descrever, explicar e aplicar conceitos e técnicas de programação
para obtenção de programas bem estruturados;
3. Analisar a complexidade de algoritmos;
4. Identificar, reescrever e examinar formas comuns de organização
de dados e algoritmos associados (com e sem gestão dinâmica de
memória, com algoritmos iterativos ou recursivos);
5. Decidir, especificar e produzir novas formas de organização de
dados e algoritmos associados adequadas aos problemas
computacionais a resolver.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Apresentação - 2

PROGRAMA

I Análise da correcção de programas

Introdução às lógicas proposicional e de predicados;


Cálculo de Hoare, especificação formal de comandos e prova da
correcção de comandos;
Metodologia de Dijkstra (metodologia de desenvolvimento informal
de comandos a partir da sua especificação).

II Especificação e implementação de tipos de dados abstractos


(TDA)

Especificação de tipos de dados abstractos (naturais, racionais,


pilhas, filas, listas, conjuntos, etc.);
Implementações estáticas e dinâmicas;
Desenvolvimento de comandos abstractos;
Tipos de dados abstractos e sua utilização no âmbito da
programação em larga escala.

III Estudo de algumas formas de organização dos dados


particularmente úteis e respectivos algoritmos associados

Árvores, Heaps, Grafos, Organização dos dados por dispersão;


Algoritmos de ordenação (elementares e avançados): inserção
directa, selecção directa, Bubblesort, Quicksort, Heapsort, etc.;
Técnicas de avaliação de algoritmos e introdução à problemática da
complexidade.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Apresentação - 3

AVALIAÇÃO

Avaliação periódica: três mini-testes (3x33,(3)%) ou três mini-testes


e trabalho individual de pesquisa (4x25%).
Exame final: só para alunos que não obtiverem aprovação na
avaliação periódica.

BIBLIOGRAFIA

- F. Santos, Algoritmos e Estruturas de Dados – Folhas de Apoio,


ISCTE-IUL, 2013.
- F. Santos, Algoritmos e Estruturas de Dados – Exercícios,
ISCTE-IUL, 2010.
- M. Weiss, Data Structures and Algorithm Analysis in Java (3ª
edição), Addison-Wesley, 2011.
- R. Sedgewick, Algorithms in Java - Parts 1-4: Fundamentals,
Data Structures, Sorting, Searching (3ª edição), Addison-Wesley,
2003.
- R. Sedgewick, Algorithms in Java - Parts 5: Graph Algorithms
(3ª edição), Addison-Wesley, 2004.
- D. Harel, Algorithmics: the Spirit of Computing (2ª edição),
Addison-Wesley, 1992.
- P. Helman, R. Veroff e F. Carrano, Intermediate Problem
Solving and Data Structures - Walls and Mirrors (2ª edição),
Benjamin/Cummings, 1991.
S. Base e A. Gelder, Computer Algorithms: Introduction to
Design and Analysis (3ª edição), Addison-Wesley, 2000, Pearson
2002.
- N. Wirth, Algorithms & data Structures, Prentice-Hall, 1986.
- A. Hamilton, Logic for Mathematicians, Cambridge University
Press, 1988.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 4

BREVE INTRODUÇÃO À LÓGICA FORMAL

O que é a Lógica ? Ramo do conhecimento que aborda a análise de


argumentos e de métodos para distinguir argumentos válidos de
argumentos inválidos

O que é um argumento? Conjunto de asserções em que se pretende


justificar uma delas – a conclusão – com base nas outras – as
permissas.
(, )

permissas conclusão

exemplos:

A1: Se Sócrates é um homem então Sócrates é mortal


Sócrates é um homem }
Portanto, Sócrates é mortal 
A2: Todos os homens são mortais
Sócrates é um homem
Portanto, Sócrates é mortal

A3: Sócrates é um homem A4: A lua é amarela


Portanto, Sócrates é mortal Portanto, a lua é feita de queijo

A5: Se a lua é amarela então é feita de queijo


A lua é amarela
Portanto, a lua é feita de queijo

 Num bom argumento parte-se de permissas, supostas


verdadeiras, para determinar uma conclusão também
verdadeira.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 5

ARGUMENTOS VÁLIDOS E INVÁLIDOS

(, ) válido: quando for “logicamente impossível” ter todas as


premissas  verdadeiras e a conclusão  falsa.

(, ) inválido: caso contrário.

 Se utilizarmos argumentos válidos não corremos o risco de


obter uma conclusão falsa a partir de permissas verdadeiras.

 O modo como a lógica formal analisa argumentos válidos e


inválidos é independente do assunto dos argumentos, mas sim
pela sua forma (descrita através de uma linguagem simbólica).

Vias para a caracterização de argumentos (, ) válidos

Via semântica ╞ 

 Baseia-se na atribuição de significado aos símbolos da


linguagem - noção de “interpretação/modelo”.

 (, ) válido sse todo o modelo que torna as fórmulas de 


verdadeiras também torna  verdadeiro.

Via dedutiva ├ 

 Baseia-se na definição de uma noção de dedução em que a


manipulação de fórmulas é meramente sintática.

 (, ) válido sse ... (ver à frente)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 6

Lógica proposicional

A linguagem utiliza proposições A, B, ... e conectivos


proposicionais ¬ “não”, “e”, “ou”,“se ... então ...”, “...
sse ...” para compor proposicões a partir de proposições mais
simples:

¬A, AB, AB, AB, AB.

Pela via semântica cada proposição pode ser interpretada como


sendo verdadeira (V) ou falsa (F) e as proposições compostas
interpretadas de acordo com as seguintes tabelas de verdade:

A B AB AB AB AB A ¬A


V V V V V V V F
V F F V F F F V
F V F V V F
F F F F V V

Os argumentos anteriores têm nesta linguagem a seguinte forma


(para A, B e C proposições):

A1, A5: ({(AB), A}, B)

A2: ({A, B}, C)

A3, A4: ({A}, B)

Apenas os argumentos A1 e A5 são válidos!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 7

Lógica de predicados

A linguagem utiliza predicados atómicos A(s), B(t), ... (para termos


s e t envolvendo variáveis x, y, ..., constantes a, b, ... e funções f, g,
...), os conectivos proposicionais ¬, , ,, e os
quantificadores  “qualquer que seja ...” e  “existe um ...” para
compor predicados a partir de predicados mais simples:

¬A(s), A(s)B(t), A(s)B(t), A(s)B(t), A(s)B(t),


xA(x), xA(x).

 Aqui apenas se consideram relevantes predicados fechados, i.e.,


predicados em que todas as variáveis estão no âmbito de um
quantificador !

Pela via semântica, para ser interpretado cada predicado necessita


de um domínio de interpretação DI onde se dá significado aos
termos e predicados atómicos da linguagem. Informalmente, os
predicados com quantificadores são interpretados da seguinte
maneira:

xA(x) é verdadeiro
sse
“A(d) é verdadeiro” para algum valor dDI

xA(x) é verdadeiro
sse
“A(d) é verdadeiro” para todo valor dDI

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 8

Exemplo: x (x2<4)

x (x2<4) é verdadeiro para DI=nat


(pois para x=0, 02<4 é verdadeiro)

x (x2<4) é verdadeiro para DI={0, 1}


(pois para x=0, 02<4 é verdadeiro
e para x=1, 12<4 é verdadeiro)

x (x2<4) é falso para DI={2, 4, 6, ...}

x (x2<4) é falso para DI=nat

Os argumentos anteriores têm nesta linguagem a seguinte forma


(sendo A e B predicados, x variável e a constante):

A1, A5: ({(A(a)B(a)), A(a)}, B(a))

A2: ({(x)(A(x)B(x)), A(a)}, B(a))

A3, A4: ({A(a)}, B(a))

Agora o argumento A2 também é válido!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 9

Prova: Uma prova de uma sentença  é a construção/derivação de


um argumento (,) é válido.

Construção de argumentos (,) válidos: Uma derivação de  a


partir de  é uma sequência de fórmulas A1, ..., An em que
An= e tal que para todo o i (1≤ i≤ n):

(i) Ai pertence a  (i.e. Ai é uma hipótese da derivação);

(ii) Ai é uma definição/axioma ou uma sentença já provada


(i.e. (,Ai) é válido);

ou

(iii)Ai deduz-se dos membros anteriores da sequência Aj1, ...,


Ajk (j1<i, ..., jk<i) por aplicação de um argumento válido da
forma

({Aj1, ..., Ajk }, Ai)

Quais os argumentos válidos iniciais ?

 Sistema Axiomático

Axiomas: (,A) ou A

Regras de Inferência: ({Aj1, ..., Ajk }, Ai) ou Aj1, ..., Ajk


Ai

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 10

Sistema axiomático para a lógica proposicional - L

Axiomas (para A, B e C fórmulas quaisquer)

(L1) (A (B A))

(L2) ((A (B C))  ((A B)  (A C)))

(L3) ((¬A) (¬B)) (B A))

Regras de inferência (para A e B fórmulas quaisquer)

(MP) A, (A B)
B

Nesta axiomatização podem ser consideradas as seguintes regras de


abreviatura:

(A B) =abreviatura (¬ (A(¬B)))

(A B) =abreviatura ((¬A)B)

(A B) =abreviatura (¬ ((AB)(¬(BA))))

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 11

Exemplos de derivações (utilizando o sistema axiomático L)

├ ((P Q)  (P P)) (i.e. ├ ((PQ)(P P)))

1 (P (Q P)) L1

2 ((P (Q P))  ((P Q)  (P P))) L2

3 ((P Q)  (P P)) 1,2,MP

{ P, (Q (P1 R))} ├ (Q R)

1 P hipótese

2 (Q (P R)) hipótese

3 (P (Q P)) L1

4 (Q P) 1,3,MP

5 ((Q (P R))  ((Q P)  (Q R))) L2

6 ((Q P)  (Q R)) 2,5,MP

7 (Q R) 4,6,MP

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 12

Difícil fazer derivações ?

Na axiomatização L verificam-se os seguintes resultados:

Resultado1: Metateorema da dedução


Sejam A e B fórmulas e  um conjunto de fórmulas.
Se {A}├ B então ├ (A B)

Resultado2:
Sejam A e B fórmulas e  um conjunto de fórmulas.
Se ├ (A B)então {A}├ B

Resultado3:
Sejam A e B fórmulas e  e  conjuntos de fórmulas.
Se {A}├ B e ├ A então ├ B

Resultado4:
Sejam A e B fórmulas e  e  conjuntos de fórmulas.
Se ├ A e (├ B para todo o B) então  ├ A

 Estes resultados permitem-nos provar a existência de


derivações a partir da existência de outras derivações!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 13

Exemplos (utilizando o sistema axiomático L)

? { P, (Q (P R))} ├ (Q R)

VEJAMOS PRIMEIRO QUE { P, (Q (P R)), Q }├ R

1 P hipótese

2 (Q (P R)) hipótese

3 Q hipótese

4 (P R) 2,3,MP

5 R 1,4,MP

Tem-se portanto { P, (Q (P R)), Q }├ R

Logo, pelo Metateorema da dedução conclui-se:

{ P, (Q (P R))} ├ (Q R)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 14

Exemplos (utilizando o sistema axiomático L)

? { (A B), (B C) }├ (A  C) Silogismo Hipotético (SH)

Vejamos primeiro que { (A B), (B C), A }├ C

1 (A B) hipótese

2 (B C) hipótese

3 A hipótese

4 B 1,3,MP

5 C 2,4,MP

Tem-se portanto { (A B), (B C), A }├ C

Logo, pelo Metateorema da dedução conclui-se:

{ (A B), (B C) }├ (A C)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 15

Exemplo (utilizando o sistema axiomático L)

? ├ ((¬B) (B A))


para A e B fórmulas quaisquer

1 ( (¬B) ((¬A) (¬B)) ) L1

2 ( ((¬A)(¬B)) (BA) ) L3

3 ((¬B) (B A)) 1,3, SH

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 16

Alguns conceitos necessários para apresentar o sistema


axiomático para a lógica de predicados

Domínio de um quantificador
Em (x)A (resp. (x)A) a fórmula A diz-se o domínio do
quantificador  (resp. ).

Variáveis livres e ligadas


Uma ocorrência de um variável x numa fórmula diz-se ligada sse
aparece em (x) ou em (x) ou no domínio de um quantificador.
Caso contrário diz-se livre.

exemplo: ((x)A(x)B(x,y))

ligada livre

Termo (t) livre para uma variável (x) numa fórmula (A)
t é livre para x em A sse nenhuma ocorrência livre de x em A ocorre
no domínio de um quantificador de uma variável em t

exemplos: f(a,y) é livre para z em ((x)A(x,z)(y)B(x,y))


f(a,y) não é livre para x em ((x)A(x,z)(y)B(x,y))
g(x) é livre para x em (x)A(x,z)
g(x) não é livre para z em (x)A(x,z)

A[x ← t]
representa a fórmula que se obtém de A por substituição de todas as
ocorrências livres de x em A

exemplos:
((x)A(x,z)(y)B(x,y))[z ← f(a,y)] “=” ((x)A(x,f(a,y))(y)B(x,y))
((x)A(x,z)(y)B(x,y))[x ← f(a,y)] “=” ((x)A(x,z)(y)B(f(a,y),y))

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 17

Sistema axiomático para a lógica de predicados - K

Axiomas (para A, B e C fórmulas quaisquer, x variável qualquer e t


termo qualquer)

(K1) (A (B A))

(K2) ((A (B C))  ((A B)  (A C)))

(K3) ((¬A) (¬B)) (B A))

(K4) ((x)A A) , se x não ocorre livre em A (caso particular de (K5))

(K5) ((x)A A[x ← t]) , se t é livre para x em A

(K6) ((x) (A B) (A(x)B)) , se x não ocorre livre em A

Regras de inferência (para A e B fórmulas quaisquer, x variável


qualquer)

(MP) A, (A B)
B

(GEN) A
(x)A

Nesta axiomatização pode ser considerada a seguinte regra de


abreviatura:

(x)A =abreviatura (¬((x)(¬A)))

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 18

Na axiomatização K verificam-se os seguintes resultados:

Resultado1: Metateorema da dedução


Sejam A e B fórmulas e  um conjunto de fórmulas.
Se {A}├ B e existe uma derivação de B a partir de {A} sem
utilizar a regra (GEN) envolvendo variáveis livres em A
então ├ (A B)

Resultado2:
Sejam A e B fórmulas e  um conjunto de fórmulas.
Se ├ (A B)então {A}├ B

Resultado3:
Sejam A e B fórmulas e  e  conjuntos de fórmulas.
Se {A}├ B e ├ A então  ├ B

Resultado4:
Sejam A e B fórmulas e  e  conjuntos de fórmulas.
Se ├ A e (├ B para todo o B) então  ├ A

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 19

Exemplo (utilizando o sistema axiomático K)

? ├ ( (A(x)B)  (x) (AB) )


para A e B fórmulas quaisquer e x variável qualquer que não
ocorra livre em A

Vejamos primeiro que {(A(x)B)} ├ (x) (AB)

1 (A(x)B) hipótese

2 ((x)BB) K4 (ou K5)

3 (AB) 1,2, SH

4 (x)(AB) 3,GEN

Tem-se portanto {(A(x)B)}├ (x) (AB), em que existe uma


derivação de (x) (AB) a partir de (A(x)B) sem utilizar a
regra (GEN) envolvendo variáveis livres em (A(x)B)

Logo, pelo Metateorema da dedução (resultado 1- para K) conclui-


se:

├ ( (A(x)B)  (x) (AB) ), se a variável x não ocorre livre


em A!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Introdução à Lógica Formal - 20

Exercícios

1. Para A, B fórmulas quaisquer da lógica proposicional, mostre que


a seguintes fórmulas são teoremas do sistema dedutivo L:

(a) ((¬A)(AB))

(b) ((¬(¬A))A)

(c) (A(¬(¬A)))

(d) ((AB) ¬B¬A)

2. Para x, y variáveis quaisquer e A, B fórmulas quaisquer da lógica


de predicados, mostre que as seguintes fórmulas são teoremas do
sistema dedutivo K:

(a) ((x)(y)A (y)(x)B)

(b) ((x)(A B) ((x)A (x)B))

(c) ((y)(B A) (B (y)A)) , para B sem ocorrências livres


da variável y

(d) ((A B) ((x)A (x)B)) , para A e B sem ocorrências


livres da variável x

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 21

CÁLCULO DE HOARE – Lógica para especificação de


comandos e análise da sua correcção.

Linguagem: uma linguagem da lógica de predicados multi-género,


variáveis de programa, com comandos e fórmulas adicionais da
forma

{A} c {B}

pré-condição de c pós-condição de c
(ou condição dada) (ou condição objectivo)

“se a execução do comando c se iniciar num estado em que se


verifica a pré-condição A então a execução de c, se terminar,
conduz a um estado que satizfaz a pós-condição B”

 Análise da Correcção partial! (falta a garantia de terminação!)

comandos: [definição por indução]

(i) ; é um comando (comando atómico vazio).


(ii) se x é uma variável de programa do tipo T e  é um termo do
tipo T então x =; é um comando (comando atómico de
atribuição).
(iii) se c1, c2, ..., cn são comandos então {c1 c2 ... cn} é um
comando (comando sequêncial).
(iv) se c, c1 e c2 são comandos e G uma condição booleana de
programa então if(G) c e if(G) c1 else c2 são comandos
(comando alternativo).
(v) se c é comando e G uma condição booleana de programa então
while(G) c é comando (comando iterativo).

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 22

Sistema axiomático: Regras de Hoare

(para A, B, D, CI, A1, ..., An fórmulas da lógica de predicados; x


variável de programa do tipo T e  é um termo do tipo T; c, c1, c2,
..., cn comandos; e G uma condição booleana de programa)

RV: Regra do Comando Vazio

{A} ; {A}

RATRIB: Regra da Atribuição

{A[x  ]} x = ; {A}

RCS: Regra da Composição Sequencial

{A1} c1 {A2}, ... , {An} cn {An+1} (n>0)


{A1} {c1 ... cn} {An+1}

RCA: Regra da Composição Alternativa


{(AG)} c1 {B}, {(A(¬G))} c2 {B}
{A} if(G) c1 else c2 {B}

RCAS: Regra da Composição Alternativa Simples

{(AG)} c {B}, ((A(¬G)) B)


{A} if(G) c {B}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 23

RCI: Regra da Composição Iterativa

{(CIG)} c {CI}
{CI} while(G) c {(CI(¬G))}

 CI diz-se invariante do comando while(G)c sse {(CIG)} c {CI}

RA: Regra do Antecedente

(A B), {B} c {D}


{A} c {D}

RC: Regra do Consequente

{A} c {B}, (B D)


{A} c {D}

RT: Regra dos Teoremas da Lógica de Predicados

A , A teorema da lógica de predicados.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 24

Exemplo (utilizando o Cálculo de Hoare)

? ├ {True} if(a>b) s=a; else s=b; {s = maximo(a,b)}

1 {a = maximo(a,b)} s = a; {s = maximo(a,b)} RATRIB

2 ((Truea>b) a=maximo(a,b)) RT

3 {(Truea>b)} s = a; {s = maximo(a,b)} 1,2, RA

4 {b = maximo(a,b)} s = b; {s = maximo(a,b)} RATRIB

5 ((True¬(a>b)) b=maximo(a,b)) RT

6 {(True¬(a>b))} s = b; {s = maximo(a,b)} 4,5, RA

7 {True} if(a>b) s=a; else s=b; {s = maximo(a,b)} 3,6, RCA

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 25

METODOLOGIA DE DIJKSTRA

Metodologia “semi-informal” baseada no cálculo de Hoare para


desenvolvimento de comandos correctos face à sua especificação.

Metodologia de Dijkstra (princípios genéricos)

1 Especificação do comando c pretendido, indicando a asserção

{CD} c {CO}

a ser verificada e incluindo a identificação do tipo das variáveis


utilizadas.

2 Identificação da estrutura genérica de c.

3 Especificação das componentes de c identificadas em 2 (de


modo a assegurar que c é correcto face à sua especificação)

4 Repetir 2 e 3 para cada um dos comandos entretanto obtidos e


especificados, até se chegar a comandos atómicos Java

 A especificação {CD} c {CO} pode ser vista como uma


equação em que c é a variável que pretendemos determinar.

 No passo 3 utilizam-se as regras de Hoare no sentido da


conclusão para as permissas!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 26

Exemplo:

Desenvolva um comando que calcule o máximo entre dois inteiros


guardados, respectivamente, nas variáveis a e b.

1. {True} maximo {s = maximo(a,b)}

int a, b, s;

2. maximo ≡ if(a > b) max_a else max_b

3. {a>b} max_a {s = a}

{a ≤ b} max_b{s = b}

PS: {a>b} max_a {s = a}, { a ≤ b } max_b {s = b}

{True} if(a > b) max_a else max_b {m = maximo(a,b)}

4.2 max_a ≡ s = a;

max_b ≡ s = b;

PS: {a>b} max_a {s = a}, { a ≤ b } max_b {s = b}

estrutura final:

maximo ≡ if (a > b) s = a; else s = b;

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 27

Metodologia de Dijkstra (comandos iterativos)

Se dada uma especificação {CD} c {CO} identificarmos c como


um comando iterativo, então:

1 Considerar para c a estrutura (“ciclo inicializado”):

{inic while(G){acção prog}} .

2 Identificar uma condição invariante CI apropriada.

3 Desenvolver as diferentes componentes de c de acordo com os


critérios a seguir:

3.1 inic: ├ {CD} inic {CI}

3.2 G: ├ ((CI (¬G))  CO)

3.3 prog: deve garantir a terminação do ciclo!


 Correcção total!

3.4 acção: ├ {(CIG)} {acção prog} {CI}

PS:

{CD} inic {CI}, {CI  G} {acção prog} {CI}, ((CI (¬G))  CO)

{CD} {inic while(G){acção prog }} {CO}


Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 28

{ /* CD */
inic /* CI */
while(G)
{ /* (CIG) */
acção
prog /* CI */
} /* (CI (¬G)) */
} /* CO */

 O desenvolvido de um comando iterativo passa pela


determinação de uma condição invariante, i.e. uma condição
que deve ser satisfeita no princípio e no fim de cada iteração
{acção prog}.

 O comando inic deve garantir que a condição invariante é


satisfeita antes da primeira iteração do ciclo.

 A condição (¬G) identifica a condição de terminação do ciclo.

 O comando prog deve garantir que a condição de terminação


(¬G) irá ser satisfeita.

 O comando acção deve garantir, sem interferir com o comando


prog, que a condição invariante é satisfeita após cada iteração
{acção prog}.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 29

Exemplo:

Desenvolva um comando que calcule a soma dos cem primeiros


números naturais.

1. {True} adição {s = 0+1+...+99}

int s;

2. adição ≡ { inic while(G){acção prog}}

3. CI: s = 0+1+...+k  0≤ k≤ 99

“s = soma dos primeiros (k+1) naturais”

int k;

3.1 inic: {k = 0; s = 0;} ou  inicialização mais simples!


{k = 1; s = 1;} ou
{k = 2; s = 3;} ou
{k = 3; s = 6;} ou
...

 ├ {True} inic {s = 0+1+...+k  0≤ k≤ 99}

3.2: G: k != 99  (¬G): k==99 “condição de terminação”

 ├ (((s = 0+1+...+k  0≤ k≤ 99)  k = 99)  s = 0+1+...+99)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 30

3.3 prog: k = k + 1;

 k inicialmente a 0; o ciclo termina quando k = 99!

3.4 acção: s = s + (k + 1);

s = 0+1+...+k  0≤ k≤ 99 (e ainda k≠99)

acção

s = 0+1+...+(k+1)  0≤ (k+1)≤ 99

prog

s = 0+1+...+k  0≤ k≤ 99

├ {s=0+1+...+k0≤ k≤ 99k≠99}{acção prog}{s=0+1+...+k0≤ k≤ 99}

estrutura final:

adição ≡ {{k = 0; s = 0;} while(k != 99){s = s + (k + 1); k = k + 1;}}


≡ {k = 0; s = 0; while(k != 99){s = s + (k + 1); k = k + 1;}}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 31

Análise da Metodologia de Dijkstra

{CD} inic {CI}, {CIG} {acção prog} {CI}, ((CI (¬G))  CO)

{CD} {inic while(G){acção prog}} {CO}

1 {CD} inic {CI} hipótese

2 {CI  G} {acção prog} {CI} hipótese

3 ((CI (¬G))  CO) hipótese

4 {CI} while(G){acção prog} {CI(¬G)} 2, RCI

5 {CI} while(G){acção prog} {CO} 3, 4, RC

6 {CD} {inic while(G){acção prog}} {CO} 1,5, RCS

 ├ {CD} {inic while(G){acção prog}} {CO}

sse

├ {CD} inic {CI}


├ {CIG} {acção prog} {CI}
├ ((CI (¬G))  CO)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 32

Exemplo:

Prove que o comando


adição ≡ {k = 0; s = 0; while(k != 99){s = s + (k + 1); k = k + 1;}}
está correcto face à sua especificação {True} adição {s=∑i=0...99 i}

?├ {True} {k = 0; s = 0;} {s =∑i=0...k i  0≤ k≤ 99}

1 {0=∑i=0...k i  0≤ k≤ 99}{s = 0;}{s=∑i=0...k i  0≤ k≤ 99} RATRIB

2 {0=∑i=0...0 i  0≤ 0≤ 99}{k = 0;}{0=∑i=0...k i  0≤ k≤ 99} RATRIB

3 {0=∑i=0...0 i  0≤ 0≤ 99}{k = 0; s = 0;}{s=∑i=0...k i  0≤ k≤ 99}1, 2, RCS

4 True  (0=∑i=0...0 i  0≤ 0≤ 99) RT

5 {True}{k = 0; s = 0;}{s=∑i=0...k i  0≤ k≤ 99} 3, 4, RA

?├ (((s =∑i=0...k i  0≤ k≤ 99)  k = 99)  s = ∑i=0...99 i)

1 (((s =∑i=0...k i  0≤ k≤ 99)  k = 99)  s = ∑i=0...99 i) RT

?├ {s=∑i=0...ki 0≤ k≤ 99k≠99}{s=s+(k+1);k=k+1;}{s=∑i=0...ki 0≤ k≤ 99}

1 {s=∑i=0...k+1i0≤ k+1≤ 99}{k = k+1;}{s=∑i=0...ki0≤ k≤ 99} RATRIB

2 {s+(k+1)=∑i=0...k+1i0≤ k+1≤ 99}{s=s+(k+1);}{s=∑i=0...k+1i0≤ k+1≤ 99} RATRIB

3 (s=∑i=0...ki 0≤ k≤ 99k≠99) (s+(k+1)=∑i=0...k+1i0≤ k+1≤ 99) RT

4 {s=∑i=0...ki 0≤ k≤ 99k≠99}{s=s+(k+1);}{s=∑i=0...k+1i0≤ k+1≤ 99} 2,3,RA

5 {s=∑i=0...ki 0≤ k≤ 99k≠99}{s=s+(k+1);k=k+1;}{s=∑i=0...ki 0≤ k≤ 99} 4,1,RCS

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 33

Metodologia de Dijkstra (comandos iterativos): técnicas para


obtenção da condição invariante

Seja CO uma condição objectivo envolvendo uma variável lógica


inteira ligada, assumindo valores entre um limite inferior LI e um
limite superior LS; seja X uma variável não ocorrendo em CO:

CI1 ≡ CO[LS ←X]  LI≤ X≤ LS

 inic: X = LI;  G: X != LS;


 acção: X+1 é o índice do próximo elemento a considerar
 prog: incrementar X

CI2 ≡ CO[LI ←X]  LI≤ X≤ LS

 inic: X = LS;  G: X != LI;


 acção: X-1 é o índice do próximo elemento a considerar
 prog: decrementar X

CI3 ≡ CO[LS ←X-1]  LI+1≤ X≤ LS+1

 inic: X = LI+1;  G: X != LS+1


 acção: X é o índice do próximo elemento a considerar
 prog: incrementar X

CI4 ≡ CO[LI ←X+1]  LI-1≤ X≤ LS-1

 inic: X = LS-1;  G: X != LI-1


 acção: X é o índice do próximo elemento a considerar
 prog: decrementar X

Diferentes invariantes  diferentes comandos!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 34

Exemplo:

Desenvolva pela metodologia de Dijkstra o comando adição:


{True} adição {s=∑i=0...99 i}

CI1 ≡ CO[99 ←k]  0≤ k≤ 99


≡ s=∑i=0...k i  0≤ k≤ 99

adição ≡ {k = 0; s = 0; while(k != 99){s = s + (k + 1); k = k + 1;}}

CI2 ≡ CO[0 ←k]  0≤ k≤ 99


≡ s=∑i=k...99 i  0≤ k≤ 99

adição ≡ {k = 99; s = 99; while(k != 0){s = s + (k - 1); k = k - 1;}}

CI3 ≡ CO[99 ←k-1]  1≤ k≤ 100


≡ s=∑i=0...k-1 i  1≤ k≤ 100

adição ≡ {k = 1; s = 0; while(k != 100){s = s + k; k = k + 1;}}

CI4 ≡ CO[0 ←k+1]  -1≤ k≤ 98


≡ s=∑i=k+1...99 i  -1≤ k≤ 98

adição ≡ {k = 98; s = 99; while(k != -1){s = s + k; k = k - 1;}}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 35

Metodologia de Dijkstra (comandos iterativos): técnicas


adicionais

Técnica da inicialização: Em alguns casos pode simplificar-se o


comando inic se nos invariantes anteriores se considerar que a
variável introduzida pode tomar um valor para além do limite
estabelecido.

Exemplo:

Desenvolva pela metodologia de Dijkstra o comando positividade:


que dada uma sucessão inteira F: N0 →Z determina se os 31
primeiros termos da sucessão são ou não todos positivos

{True} positividade {b=(j=0...30)(F(j)>0)}

boolean b;

CI1 ≡ CO[30 ←n]  0≤ n≤ 30 int n;


≡ b=(j=0...n)(F(j)>0) 0≤ n≤ 30

inic ≡ {n = 0; b = F(0)>0;}

CI1* ≡ CO[30 ←n]  -1≤ n≤ 30 int n;


≡ b=(j=0...n)(F(j)>0) -1≤ n≤ 30

inic ≡ {n = -1; b = true;}

n = -1;  ainda não se analisou nada!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 36

Metodologia de Dijkstra (comandos iterativos): técnicas


adicionais

Técnica do caso favorável para a escolha da acção: Se durante a


execução da iteração {acção prog} existe uma condição CF em que
não é preciso fazer nada para manter a condição invariante CI, i.e.
├{CIGCF} {prog} {CI}, então acção ≡ if(!CF) c.

Exemplo:

Desenvolva pela metodologia de Dijkstra o comando positividade:


que dada uma sucessão inteira F: N0 →Z determina se os 31
primeiros termos da sucessão são ou não todos positivos

{True} positividade {b=(j=0...30)(F(j)>0)}

CI ≡ b=(j=0...n)(F(j)>0) -1≤ n≤ 30

G ≡ n != 30

prog ≡ n = n + 1;

 CF ≡ F(n+1)>0

 ├ {b=(j=0...n)(F(j)>0)-1≤ n≤ 30n≠30F(n+1)>0}
{n=n+1;}
{ b=(j=0...n)(F(j)>0) -1≤ n≤ 30}

acção ≡ if (F(n+1)<=0) b = false;

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 37

Metodologia de Dijkstra (comandos iterativos): técnicas


adicionais

Técnica do reforco da guarda: Se para além de (¬G) existe uma


condição C (expressável através de uma condição booleana Java) tal
que ├ ((CI C )  CO) então escolha-se para guarda do ciclo G* ≡
G && !C.

 C deve ser expressável por uma condição booleana Java!

Exemplo:

Desenvolva pela metodologia de Dijkstra o comando positividade:


que dada uma sucessão inteira F: N0 →Z determina se os 31
primeiros termos da sucessão são ou não todos positivos

{True} positividade {b=(j=0...30)(F(j)>0)}

CI ≡ b=(j=0...n)(F(j)>0) -1≤ n≤ 30

G ≡ n != 30

 C ≡ ¬b

 ├ (b=(j=0...n)(F(j)>0)-1≤ n≤ 30(¬b))b=(j=0...30)(F(j)>0)

G* ≡ n != 30 && b.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Hoare & Dijkstra - 38

Exercícios

1. Desenvolva pela metodologia de Dijkstra os seguintes comandos:

(a) potência: que dado um real e um inteiro positivo, guardados


respectivamente nas variáveis x e n, calcula a potência xn (i.e. x
multiplicado por x n vezes).
(b) algumpositivo: que dada uma sucessão inteira F: N0 →Z
determina se algum dos 31 primeiros termos da sucessão é
positivo.

2. Prove a correcção dos comandos desenvolvidos no exercício


anterior.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 39

TIPOS DE DADOS ABSTRACTOS: MOTIVAÇÃO

Abstracção e Implementação

Em programação ...

Abstracção  o que é disponibilizado a um utilizador.

Implementação  detalhes necessários à realização daquilo que


é disponibilizado a um utilizador e que
interessa “esconder” para não comprometer
eventuais alterações.

O processo de abstracção, enquanto meio para esconder detalhes


desnecessários para determinados segmentos de um programa
(capsulação), é um dos principais instrumentos para a redução da
complexidade na Programação.

Um dos principais mecanismos de capsulação é a utilização de tipos


de dados abstractos. Um TDA, permite-nos concentrar nas
propriedades de uma determinada colecção de entidades, na sua
estrutura e no conjunto das operações sobre essas entidades, sem
qualquer comprometimento quanto ao tipo de representação e
implementação (estrutura de dados).

Estes mecanismos de abstracção podem ser ou não facilitados pelas


linguagens de programação, mas fazem parte da “arte de bem
programar”.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 40

APLICAÇÃO DE TDA NA
PROGRAMAÇÃO EM GRANDE ESCALA

Vantagens da metodologia de Dijkstra

Suporta o desenvolvimento estruturado (modular) de comandos


em simultâneo com a prova da sua correcção (parte da
especificação para o comando)

Limitações da metodologia de Dijkstra

"Exige" a adopção de notações precisas

"Pesada" para abordar problemas de "grande escala"

 Incide só sobre o desenvolvimento dos comandos, sem


introduzir qualquer mecanismo de estruturação dos dados


Que metodologia usar em problemas de grande escala?

desenvolvimento dos comandos, mas também, e em primeiro


lugar, estruturação e representação dos dados

controlar a complexidade do problema  POR ETAPAS

soluções independentes da implementação  RE-UTILIZAÇÃO

definição de tipos de dados abstractos !

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 41

Exemplo preliminar:

“Inversão de sequências”

Pretendemos desenvolver um programa que permita ao


utilizador, via teclado, fornecer números inteiros positivos, tantos
quantos quiser, e que, concluída a introdução dos números (o que é
indicado por um número não positivo), eles lhe sejam mostrados no
visor, por ordem inversa.

Assim, por exemplo, se a sequência introduzida de números


inteiros positivos for <3, 1, 9, 45> deve aparecer no visor a
sequência <45, 9, 1, 3>.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 42

teimoso

leitura escrita

Inicialização enviar o último *


(ler primeiro ...) ciclo e retirá-lo

guardar e *
ler próximo

Qual a estrutura de informação mais adequada para guardar os


valores lidos ?

Estrutura que permita aceder facilmente aos números armazenados


por ordem inversa, i.e. disciplina de acesso LIFO, i.e. PILHA!

O que é uma PILHA?

É fundamental definir pilhas independentemente da linguagem de


programação adoptada!  especificar PILHA como TDA!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 43

Método: Programação modular descendente por camadas e


centrada nos dados. Só com duas camadas (a de topo e a
terminal)

1. Identificação dos tipos de dados abstractos necessários ao fácil


desenvolvimento do comando abstracto a escrever sobre a
camada de topo.

2. Especificação desses tipos de dados abstractos.  como?

3. Desenvolvimento do programa/comando abstracto pretendido


sobre a camada de topo, onde se supõe dispor da linguagem Java
enriquecida com esses tipos.

4. Implementação dos tipos de dados abstractos (da camada de


topo) sobre a camada terminal (Java), respeitando as respectivas
especificações em (2).  ... + definição de métodos Java

5. Reificação do programa/comando escrito sobre a camada de


topo (3), com base nas implementações escolhidas em (4).

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 44

Programa
(commando) TDA
abstracto Camada de topo = Java e TDA (2)
(3)

(5) (4)
REIFICAÇÃO IMPLEMENTAÇÃO

Programa TDA
(commando) implementado
reificado Camada terminar = Java

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 45

1. Identificação do tipo abstracto de dados:

pilha de inteiros!

2. Especificação do tipo abstracto de dados:

especificação pilhas_de_inteiros =
boolean  int 
géneros
Pilha
operações
construtoras
nova:  Pilha;
sobrepoe: Pilha int  Pilha
acessórias
retira: Pilha  Pilha;
topo: Pilha  int;
vazia: Pilha  boolean
axiomas
( PPilha) ( Nint)
retira(sobrepoe(P,N)) = P ;
topo(sobrepoe(P,N)) = N ;
vazia(nova) = true ;
vazia(sobrepoe(P,N)) = false
pré-condições
( PPilha)
retira(P) requer !vazia(P);
topo(P) requer !vazia(P)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 46

3. Desenvolvimento do programa abstracto

import java.io.*;
import personal.io.*;
public class Teimoso{
public static void main(String[] args) {
p;
int x;

/* inicialização */
p = ;
System.out.print("? "); x=Keyboard.readInt();

/* ciclo de leitura */
while (x>0) {
p = (p,x) ;
System.out.print("? "); x=Keyboard.readInt();
}

/* ciclo de escrita */
while ( ! (p) ) {
System.out.println( (p) ) ;
p = (p);
}
}
}

4. Implementação do tipo “pilha de inteiros”

????

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 47

Método: Implementação de tipos de dados abstractos (versão


simplificada)

1. Escolha dos géneros dos tipos alvo para implementação dos


géneros dos tipos fonte;

2. Definição correcta das representações das operações dos tipos


fonte, com base nas operações primitivas dos tipos alvo, respeitando
os axiomas relativos a essas operações;

4. Implementação do tipo “pilha de inteiros”

4.1. Implementação dos géneros do tipo fonte

public class Pilha {


protected static final int lim=100;
protected int[] ve=new int[lim]; /* matriz de elementos */
protected int it; /* indicador de topo */
...
}

A pilha 2
7
-3
é representada por: 4

ve 4 -3 7 2 ? … ?
0 99

it
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação TDA: motivação - 48

4.2. Representações das operações do tipo fonte

import java.io.*;

public static Pilha nova(){


Pilha p=new Pilha();
p.it=-1;
return p;
}

public static Pilha sobrepoe(Pilha p, int n){


if(p.it!=lim-1){
p.ve[++p.it]=n;
return p;
}
else{
System.out.println(
"Erro - sobrepoe: capacidade da pilha excedida!");
return p;
}
}

public static Pilha retira(Pilha p){


if(p.it!=-1){
p.it--;
return p;
}
else{
System.out.println("Erro - retira: pilha vazia!");
return p;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 49

public static int topo(Pilha p){


if(p.it!=-1) return p.ve[p.it];
else{
System.out.println("Erro - topo: pilha vazia!");
return -1;
}
}

public static boolean vazia(Pilha p){


return p.it==-1;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 50

5. Reificação do programa abstracto

import java.io.*;
import personal.io.*;
public class Teimoso{
public static void main(String[] args) {
Pilha p;
int x;

/* inicialização */
p = Pilha.nova();
System.out.print("? "); x=Keyboard.readInt();

/* ciclo de leitura */
while (x>0) {
p = Pilha.sobrepoe(p,x) ;
System.out.print("? "); x=Keyboard.readInt();
}

/* ciclo de escrita */
while ( ! Pilha.vazia (p) ) {
System.out.println(Pilha.topo(p) ) ;
p = Pilha.retira(p);
}
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 51

diferenças:

p = nova;  p = Pilha.nova();

p = sobrepoe(p,x);  p = Pilha.sobrepoe(p,x);

p = retira(p);  p = Pilha.retira(p);

vazia(p)  Pilha.vazia(p)

topo(p)  Pilha.topo(p)

teimoso pilhas
inteiros
Java e pilhas de inteiros

REIFICAÇÃO IMPLEMENTAÇÃO

Teimoso.java Pilha.java
Java

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 52

TDA – Vantagens

Fixou-se de uma vez por todas o que são pilhas!

O facto de a definição de TDA ser independente de uma qualquer


possível representação na linguagem de programação adoptada
permite a sua RE-UTILIZAÇÃO noutros contextos em que se opte
por outras linguagens de programação.

No âmbito da metodologia proposta para programação em larga


escala, a definição de TDA permite:

- que se desenvolvam programas abstractos em que nos


concentramos basicamente na concepção do problema;

- abstrair completamente da análise de qual a melhor


representação para o tipo de dados.

CONTROLE DA COMPLEXIDADE!

- CAPSULAR (esconder) as implementações de TDA de tal modo


que eventuais alterações nessas implementações não se
repercutirão no comando abstracto.

INDEPENDÊNCIA DA IMPLEMENTAÇÃO!

- guiar o programador na MODULARIZAÇÃO em torno das


operações de TDA

RE-UTILIZAÇÃO DA IMPLEMENTAÇÃO!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 53

Uma implementação tipicamente orientada por objectos:

4*. Implementação do tipo “pilha de inteiros”

import java.io.*;

public class Pilha {


protected static final int lim=100;
protected int[] ve=new int[lim]; /* matriz de elementos */
protected int it; /* indicador de topo */

public void nova(){


it=-1;
}

public void sobrepoe(int n){


if(it!=lim-1) ve[++it]=n;
else System.out.println(
"Erro - sobrepoe: capacidade da pilha excedida!");
}

public void retira(){


if(it!=-1) it--;
else System.out.println("Erro - retira: pilha vazia!");
}

public int topo(){


if(it!=-1) return ve[it];
else{
System.out.println("Erro - topo: pilha vazia!");
return -1;
}
}
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação TDA: motivação - 54

public boolean vazia(){


return it==-1;
}
}

5*. Reificação do programa abstracto

import java.io.*;
import personal.io.*;
public class Teimoso{
public static void main(String[] args) {
Pilha p = new Pilha();
int x;

/* inicialização */
p.nova();
System.out.print("? "); x=Keyboard.readInt();

/* ciclo de leitura */
while (x>0) {
p.sobrepoe(x) ;
System.out.print("? "); x=Keyboard.readInt();
}

/* ciclo de escrita */
while ( !p.vazia() ) {
System.out.println(p.topo() ) ;
p.retira();
}
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 55

diferenças:

Pilha p;  Pilha p = new Pilha();

p = nova;  p.nova();

p = sobrepoe(p,x);  p.sobrepoe(x);

p = retira(p);  p.retira();

vazia(p)  p.vazia()

topo(p)  p.topo()

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação TDA: motivação - 56

Exercício

Desenvolva um programa que permita ao utilizador, via teclado,


fornecer números inteiros positivos, tantos quantos quiser, e que, só
depois de concluída a introdução dos números (o que é indicado por
um número não positivo), eles lhe sejam mostrados no visor pela
mesma ordem.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 57

TIPOS DE DADOS ABSTRACTOS: ESPECIFICAÇÃO

O que é um tipo de dados?

TIPO DE DADOS

CONJUNTO DE VALORES

CONJUNTO DE OPERAÇÕES
(incluindo regras de cálculo, i.e. semântica das operações)

Para se especificar um TDA tem que se introduzir uma linguagem


que permita representar estas componentes!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 58

Exemplo: especificação de pilha de inteiros

especificação pilhas_de_inteiros =
boolean  int  enriquecimento dos tipos
géneros boolean e int (Java)
Pilha
operações nome do conjunto de todas
construtoras as pilhas de inteiros (género)
nova:  Pilha;
sobrepoe: Pilha int  Pilha
acessórias operações que em particular
retira: Pilha  Pilha; representam todas as pilhas
topo: Pilha  int; de inteiros. Como?
vazia: Pilha  boolean
axiomas
( P Pilha) ( Nint)
retira(sobrepoe(P,N)) = P ;
topo(sobrepoe(P,N)) = N ;
vazia(nova) = true ; regras de cálculo: Porquê estas?
vazia(sobrepoe(P,N)) = false

pré-condições
( P Pilha)
retira(P) requer !vazia(P); pré-condições
topo(P) requer !vazia(P) (situações de erro)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 59

Operações construtoras

Permitem, por geração de termos, representar todos os elementos do


género.

5
3 3

nova sobrepoe(nova, 3) sobrepoe(sobrepoe(nova,3),5)

Qualquer pilha de inteiros que se possa idealizar corresponde a um


termo utilizando as operações construtoras nova e sobrepoe!

Neste caso, quaisquer dois termos diferentes utilizando as operações


construtoras nova e sobrepoe representam duas pilhas de inteiros
diferentes!

sobrepoe(sobrepoe(nova,3),5)  sobrepoe(sobrepoe(nova,5),3)

Por utilização de variáveis podem ser definidos termos cujas formas


sintácticas representam vários elementos do género

( PPilha) ( N, Mint)

sobrepoe(nova,N) pilhas com 1 elemento


sobrepoe(sobrepoe(nova,N),M) pilhas com 2 elementos
sobrepoe(P,N) pilhas não vazias (i.e. com
pelo menos 1 elemento)
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 60

Axiomas

Definem como as operações operam sobre elementos do género


(representados por termos construtores!). Incidem portanto nas
operações acessórias.

( PPilha) ( Nint)

vazia(nova) = true

vazia(sobrepoe(P,N)) = false

retira(nova) = ? erro  retira(P) requer !vazia(P)

retira(sobrepoe(P,N)) = P

topo(nova) = ? erro  topo(P) requer !vazia(P)

topo(sobrepoe(P,N)) = N

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 61

Utilização dos axiomas no cálculo

( P Pilha) ( Nint)

(Ax1) vazia(nova) = true


(Ax2) vazia(sobrepoe(P,N)) = false
(Ax3) retira(sobrepoe(P,N)) = P
(Ax4) topo(sobrepoe(P,N)) = N

sobrepoe(retira(sobrepoe(sobrepoe(nova,25),27)),-1)

Ax3: P=sobrepoe(nova,25) , N=27

= sobrepoe(sobrepoe(nova,25),-1)

topo(retira(sobrepoe(sobrepoe(nova,5),2)))

Ax3: P=sobrepoe(nova,5) , N=2

= topo(sobrepoe(nova,5))

Ax4: P=nova , N=5

=5

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 62

Princípio metodológico na especificação de TDAs

1. Identificação dos géneros relevantes.

2. Identificação das operações construtoras, impondo eventuais


axiomas para essas operações (caso em que os elementos
gerados são iguais!)

3. Identificação das operações acessórias e estabelecimento dos


axiomas que as definem de acordo com o critério da
completude : para cada operação acessória devem ser fornecidos
axiomas que estabeleçam o resultado da operação para cada
combinação de termos construtores dos seus argumentos (à
excepção dos erros que devem ser referidos nas pré-condições).

Por exemplo, os seguintes axiomas por si só não obedecem ao


critério da completude ...

( Ppilha) ( Nint)
vazia(nova) = true
vazia(sobrepoe(nova,N)) = false

... pois não estabelecem o resultado da operação vazia para


termos construtores da forma

sobrepoe(sobrepoe(P,N1),N2),  PPilha,  N1, N2int

i.e., falta definir a operação vazia quando aplicada a pilhas com


2 ou mais elementos!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 63

Exemplo: Especificação de booleanos1 (com operações nao, e)

especificação booleanos1 =
géneros
booleano
operações
construtoras
verdadeiro:  booleano ;
falso:  booleano
acessórias
nao: booleano  booleano ;
e: booleano booleano  booleano
axiomas
( B  booleano)
nao(verdadeiro) = falso ;
nao(falso) = verdadeiro ;
e(falso,B) = falso ;
e(verdadeiro,B) = B
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 64

Exemplo: Especificação de booleanos2 (com operações nao,e,ou)

especificação booleanos2 =
booleanos1 
operações
acessórias
ou: booleano booleano  booleano
axiomas
( B  booleano)
ou(falso,B) = B ;
ou(verdadeiro,B) = verdadeiro
fim

booleanos2

booleanos1

Nota: Nem sempre é necessária a definição axiomática de uma


operação acessória à custa das operações construtoras. Basta que as
operações utilizadas na definição já estejam definidas! Por exemplo:

( B1,B2  booleano)
ou(B1,B2) = nao(e(nao(B1),nao(B2)))
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 65

Exemplo: Especificação de naturais1 (com operação adição)

especificação naturais1 =
géneros
nat
operações
construtoras
zero:  nat ;
suc: nat  nat
acessórias
add: nat nat  nat
axiomas
( N, M  nat)
add(zero,N) = N ; (Ax1)
add(suc(N),M) = suc(add(N,M)) (Ax2)
fim

Qual o valor da expressão add(suc(suc(zero)), suc(zero)) ?

add(suc(suc(zero)), suc(zero))

= suc(add(suc(zero), suc(zero))) Ax2: N=suc(zero), M=suc(zero)

= suc(suc(add(zero, suc(zero)))) Ax2: N=zero, M=suc(zero)

= suc(suc(suc(zero))) Ax1: N=suc(zero)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 66

Exemplo: Especificação de naturais2 (com par, e>1, igual )

especificação naturais2 =
naturais1  booleanos1 
operações
acessórias
par: nat  booleano;
e>1: nat  booleano;
igual: nat nat  booleano
axiomas
( N, M  nat)
par(zero) = verdadeiro ;
par(suc(N)) = nao(par(N)) ;
e>1(zero) = falso;
e>1(suc(zero)) = falso;
e>1(suc(suc(N))) = verdadeiro;
igual(zero, zero) = verdadeiro;
igual(zero, suc(N)) = falso;
igual(suc(N), zero) = falso;
igual(suc(N), suc(M)) = igual(N, M)
fim

naturais2

naturais1 booleanos1

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 67

Exemplo: Especificação de naturais3 (com mult, fact)

especificação naturais3 =
naturais1 
operações
acessórias
mult: nat nat  nat;
fact: nat  nat
axiomas
( N, M  nat)
mult(zero, M) = zero;
mult(suc(N), M) = add(mult(N,M), M);
fact(zero) = suc(zero) ;
fact(suc(N)) = mult(suc(N), fact(N))
fim

PS: Porque não fact(suc(N)) = mult(suc(zero), fact(suc(N))) ?

Definições recursivas – princípios gerais

Nos axiomas as definições recursivas devem ser feitas de modo a


assegurar o cálculo. Isso consegue-se respeitando os dois princípios
seguintes:

1. Devem ser sempre definidos casos base, não recursivos.


(associados a termos construtores mais simples)

2. Nos casos recursivos, as definições devem sempre estabelecer


igualdades que conduzam o cálculo em direcção aos casos base.
(reduzindo a complexidade dos termos construtores)
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 68

Exemplo: Especificação de fracções sobre tipos Java

especificação fracção =
int  boolean 
operações
géneros
fra
operações
construtoras
franova: int int  fra
acessórias
num: fra  int;
den: fra  int;
fmult: fra fra  fra;
igualf: fra fra  boolean
axiomas
( X, Y  fra) ( N, D  int)
num(franova(N, D)) = N ;
den(franova(N, D)) = D ;
fmult(X, Y) = franova(num(X)*num(Y),den(X)*den(Y));
igualf(X, Y) = num(X)*den(Y) =int num(Y)*den(X)
pré-condições
( N, D  int)
franova(N, D) requer D  0
fim

ou alternativamente:

( N1, N2, D1, D2  int)


fmult(franova(N1,D1), franova(N2,D2)) = franova(N1*N2, D1*D2)
...
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 69

Exercícios

1. No contexto das especificações anteriores calcule o valor dos


seguintes termos:

a) igual(suc(suc(zero)), suc(zero))

b) par(suc(suc(suc(zero))))

c) e>1(suc(suc(suc(zero))))

d) mult(suc(suc(zero)), suc(suc(suc(zero))))

e) fact(suc(suc(suc(zero))))

2. Especifique inteiros com as operações de adição, subtração e


multiplicação.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 70

TIPOS DE DADOS ABSTRACTOS: IMPLEMENTAÇÃO

Método: Implementação de TDA

1. Escolha dos tipos alvo da implementação.

2. Escolha dos géneros nos tipos alvo para implementação dos


géneros dos tipos fonte.

3. Introdução das representações das operações dos tipos fonte nos


tipos alvo.

4. Definição correcta das representações das operações


construtoras dos tipos fonte, com base nas operações primitivas
dos tipos alvo, respeitando os axiomas, pré-condições e
invariantes relativos a essas operações dos tipos fonte (caso
existam).

5. Definição correcta das representações das operações acessórias


dos tipos fonte, com base nas operações primitivas dos tipos
alvo, respeitando os axiomas, pré-condições e invariantes
relativos a essas operações dos tipos fonte e tomando em
consideração os resultados obtidos em 4.

Na codificação das operações devem ser consideradas as situações


que não verifiquem as pré-condições e invariantes através de
mensagens de erro e/ou lançamento de excepções.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 71

o: g1 … gn  g TIPO
g a FONTE

TIPO ALVO
(ESTENDIDO)

TIPO
R(g) R(a)
ALVO
R(o): R(g1) … R(gn)  R(g)

R:“função” de implementação; g,g1,gn:géneros; o:operação; a:axioma

CORRECÇÃO DA IMPLEMENTAÇÃO
=
MONOTONIA
(os mesmos axiomas no alvo)
+
CONSERVATIVA
(no alvo não há axiomas adicionais)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 72

Implementação de booleanos1 sobre os inteiros Java

1. Escolha dos tipos alvo da implementação

tipo inteiro Java

2. Escolha dos géneros nos tipo alvo para implementação dos


géneros dos tipos fonte

int, i.e. R(booleano)=int

3. Introdução das representações das operações dos tipos fonte


nos tipos alvo

R(verdadeiro):  R(booleano)
R(falso):  R(booleano)
R(nao): R(booleano)  R(booleano)
R(e): R(booleano) R(booleano)  R(booleano)

i.e.

R(verdadeiro):  int
R(falso):  int
R(nao): int  int
R(e): int int  int

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 73

4. Definição correcta das representações das operações


construtoras dos tipos fonte

R(falso) = 0
R(verdadeiro) = 1

Portanto:

public static int falso(){


return 0;
}

public static int verdadeiro(){


return 1;
}

E porque não utilizar R(falso)=1 e R(verdadeiro)=0 ? ou utilizar


R(falso)=35 e R(verdadeiro)=-54 ?

 Neste passo o factor mais importante é assegurar a eficiência da


implementação!!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 74

5. Definição correcta das representações das operações


acessórias dos tipos fonte, com base nas operações primitivas
dos tipos alvo, respeitando os axiomas relativos a essas
operações

R(nao)( R(falso)) = R(verdadeiro)


R(nao)( R(verdadeiro)) = R(falso)

i.e.

R(nao)( 0) = 1
R(nao)( 1) = 0

Portanto:

public static int nao ( int n ){


return 1 - n;
}

Com esta implementação:

( N  int) R(nao)( N) = 1 - N

Implementação Monótona mas não Conservativa!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 75

R(e)( R(falso),B) = R(falso)


R(e)( R(verdadeiro), B) = B

i.e.

R(e)( 0,B) = 0
R(e)( 1, B) = B

Portanto:

public static int e( int n , int m){


return n * m;
}

Com esta implementação:

( N, M  int) R(e)( N, M) = N * M

Implementação Monótona mas não conservativa!

PS: A eficiência e simplicidade da implementação proposta deve-se


à escolha das definições das representações das operações
construtoras.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 76

E porque em Java todas as funções devem ser definidas como


métodos de uma classe:

public class B{

public static int falso(){


return 0;
}

public static int verdadeiro(){


return 1;
}

public static int nao( int n ){


return 1 - n;
}

public static int e( int n , int m ){


return n * m;
}

/* programa de teste */
public static void main( String[] args ){
int b = B.e(B.nao(B.falso()),B.verdadeiro());
if(b == B.verdadeiro()) System.out.println("verdadeiro");
else System.out.println("falso");
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 77

TDA: DESENHO POR CONTRATO

O DESENHO POR CONTRATO estabelece a relação entre um tipo


de dados e os seus clientes como um acordo formal, expressando os
direitos e obrigações de parte a parte.

A cada método associam-se ...

Pré-condições – expressando as condições que se devem verificar


aquando da chamada do método.

Pós-condições – expressando as propriedades do estado resultante


da acção do método.

À classe pode ainda ser associado ...

Invariante – estabelecendo propriedades globais para instâncias da


classe que devem ser preservadas sempre que é invocado um
método público.

Linguagem para expressar pré-condições, pós-condições e


invariantes:
@pre <expressão booleana>
@post <expressão booleana>
@invariant <expressão booleana>
Nas pós-condições pode ainda utilizar-se:
return – valor retornado pelo método.
<expressão>@pre – valor da expressão antes da invocação
do método.
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 78

No âmbito do desenho por contrato ...

 Os axiomas de TDAs traduzem as pós-condições dos contratos


a estabelecer na implementação das operações construtoras!

 As pré-condições de TDAs traduzem as pré-condições dos


contratos a estabelecer na implementação das operações!

O desenho por contrato pode ou não ser suportado pela linguagem


de programação utilizada. A própria linguagem utilizada para
expressar pré-condições, pós-condições e invariantes também
depende da linguagem de programação utilizada.

No Java as pré-condições, pós-condições e invariantes podem ser


descritos em comentários Javadoc e ser monitorizadas:

1. utilizando iContract;
2. através de comandos assert (Java 1.4) ou lançamento de
excepções no código de métodos

Pré-condições  início do código!


Pós-condições  fim do código!
Invariantes  início e fim do código!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 79

Pilha de inteiros: Desenho por contrato

retira(sobrepoe(P,N)) = P  @post em sobrepoe


topo(sobrepoe(P,N)) = N  @post em sobrepoe
vazia(nova) = true  @post em nova
vazia(sobrepoe(P,N)) = false  @post em sobrepoe

retira(P) requer !vazia(P)  @pre em retira


topo(P) requer !vazia(P)  @pre em topo

Implementação 1:

//** @post vazia(return) */


public static Pilha nova(){
Pilha p=new Pilha ();
p.it=-1;
assert vazia(p);
return p;
}

//** @post !vazia(return) && topo(return)==n


post retira(return).equals(p@pre)  não monitorizável! Porquê?
*/
public static Pilha sobrepoe(Pilha p, int n){
if(p.it!=lim-1) p.ve[++p.it]=n;
else System.out.println(
"Erro - sobrepoe: capacidade da pilha excedida!");
assert !vazia(p) && topo(p)==n;
return p;
}
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Eficiência de Algoritmos - 80

//** @pre !vazia(p) */


public static Pilha retira(Pilha p){
assert !vazia(p);
if(p.it!=-1) p.it--;
else System.out.println("Erro - retira: pilha vazia!");
return p;
}

etc ...

Implementação 2:

//** @post vazia() */


public void nova(){
it=-1;
assert vazia();
}

//** @post !vazia() && topo()==n


post “retira().equals(this@pre)”  não monitorizável! Porquê?
*/
public void sobrepoe(int n){
if(it!=lim-1) ve[++it]=n;
else System.out.println(
"Erro - sobrepoe: capacidade da pilha excedida!");
assert !vazia() && topo()==n;
}

etc ...

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 81

EFICIÊNCIA DE ALGORITMOS

Eficiência de algoritmos / Complexidade Algoritmica Medida dos


recursos computacionais necessários para executar um algoritmo.

Complexidade Temporal tempo de execução


Complexidade Espacial espaço de memória

 espaço e tempo dependem da dimensão dos dados. Podemos


portanto representar a eficiência por funções!

t e
e s
m T(n) p S(n)
p a
o ç
o

n - dimensão dos dados n - dimensão dos dados

S(n) memória utilizada em função do tamanho n do input


T(n) tempo de execução em função do tamanho n do input

 Na prática é muito difícil calcular com rigor a memória utilizada


e o tempo de execução de um algoritmo! Porquê?

 Identificar no algoritmo os espaços elementares; Identificar no


algoritmo as operações elementares e determinar o número de vezes
que são executadas. A memória real e o tempo real de execução de
cada operação elementar será uma constante multiplicativa!

 Independente do computador!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 82

Exemplo: Dado um array de nN empregados (input), calcular a


soma dos seus salários (output)

class Empregado{
String nome;
float salário;
Empregado(String nome, float salário){
this.nome = nome;
this.salário = salário;
}
}
float somasalarios(Empregado[] empregados, int n){
float soma = 0;
for(int i = 0; i < n; i = i +1) soma = soma + empregados[i].salário;
return soma;
}

Traço da execução 1:
Xavier Maria
600 300
soma = 0
i=0
0<2 soma = 600 i=1
1<2 soma = 900 i=2
2<2

Traço da execução 2:
Xavier Maria Ana Gustavo
600 300 1500 3500
soma = 0
i=0
0<4 soma = 600 i=1
1<4 soma = 900 i=2
2<4 soma = 2400 i=3
3<4 soma = 5900 i=4
4<4

S(N) = 2N
T(N) = 3N+3

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 83

 Dados vários algoritmos para solucionar um mesmo problema,


devemos escolher aquele que nos permite resolver o problema mais
rapidamente e utilizando o menor espaço possível para representar
os dados do problema! Mas …

t e S1(n)
e s
m T1(n) p
p a S2(n)
o ç
o
T2(n)
k m
n - dimensão dos dados n - dimensão dos dados

 um algoritmo pode ser mais eficiente que outro para inputs de


“pequena” dimensão e deixar de o ser para inputs de “grande”
dimensão.

e.g. insertsort vs treesort

 um algoritmo pode ser mais eficiente que outro em tempo e não o


ser em espaço.

e.g. bublesort vs treesort

 Mais ainda, diferentes amostras com a mesma dimensão podem


ser processadas em tempos diferentes, podendo-se falar de eficiência
temporal mínima (melhor caso), máxima (pior caso) e média.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 84

Eficiência no melhor caso, pior caso e em média

Exemplo: Dado um array de nN empregados (input), ordenar os


empregados por nome (output)

Empregado[] insertsort(Empregado[] empregados, int n){


for(int i = 2; i < n+1; i = i +1){
empregados[0] = empregados[i];
int pos = i;
while( empregados[pos-1].nome “>”x.nome){
empregados[pos] = empregados[pos-1];
pos = pos-1
}
empregados[pos] = empregados[0];
}
return empregados;
}

Tmin(n) = Σi=2..n 3 = 3(n – 1)


(ocorre quando o array a ordenar já está ordenada)

Tmax(n) = Σi=2..n (2i+1) = n2 + 2n – 3


(ocorre quando o array a ordenar está pela ordem inversa)

Tmed(n) = (n2 + 5n – 6)/2, para amostras equiprováveis.

 A análise de algoritmos é simplificada se nos concentrarmos


na sua eficiência assimptótica, i.e. análise da taxa de crescimento
do pior caso.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 85

Ordem de complexidade

Motivação: Suponhamos, por exemplo, que a complexidade


temporal é expressa pela função
f(n) = an2+bn+c, para a,b,c constantes
À medida que n aumenta os termos de grau 1 e 0 tornam-se
insignificantes. O mesmo acontece com a. A função T(n) = n2
salienta o termo dominante, é mais simples e descreve a taxa de
crescimento de f.

 A notação O proporciona uma forma conveniente de


expressar a taxa de crescimento de uma função

Notação O: Sejam f: N → R e g: N → R funções.


f(n) = O(g(n)) sse cR+ n0N : |f(n)| ≤c|g(n)|, n≥n0

c g(n)

f(n)

n0 n
f(n) = O(g(n))

Exemplos:
(n+1)/2 = O(n)
2n3+2n -7 = O(n3)
(n2+5)(n-3) = O(n3)
ln n = O(log2 n)
25 = O(n0) = O(1)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 86

Classes de Complexidade Algorítmica

O(1) Complexidade Constante


O(log n) Complexidade Logarítmica
O(n) Complexidade Linear
O(n log n) Complexidade Log-Linear
O(n2) Complexidade Quadrática
O(n3) Complexidade Cúbica
O(nk) Complexidade Polinomial
O(an) Complexidade Exponencial
O(n!) Complexidade Factorial

2n

n2

log n

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 87

alguns valores …

10 50 100 1000 104 105 106


log2 n 3 5 6 9 13 16 19
n 10 50 100 1000 104 105 106
n log2 n 30 282 664 9965 105 106 107
n2 100 2500 104 106 108 1010 1012
3
n 1000 125000 106 109 1012 1015 1018
n
2 1000 1016 1030 10300 103000 1030000 10300000
n! 106 1065 10160    
nn 1010 1085 10200    

Com 210 ≈ 103


 ≈ número grande inimaginável

1 dia = 24 X 60 X 60 seg = 86400 seg ≈ 9 X 1010 seg


1 ano = 365 X 24 X 60 X 60 seg ≈ 3 X 107 seg ≈ 3 X 1013 seg
1 século ≈ 3 X 109 seg ≈ 3 X 1015 seg
1 milénio ≈ 3 X 1010 seg ≈ 3 X 1016 seg
Big Bang ≈ 15 X 109 anos ≈ 45 X 1016 seg ≈ 45 X 1022 seg

 Algoritmos de complexidade constante e logaritmica são os mais


eficientes
 Algoritmos razoáveis têm complexidade polinomial.
 Algoritmos de complexidade exponencial geram tempos de
execução incomportáveis

À escala exponencial, computadores mais rápidos oferecem


melhoramentos são insignificantes!

 Robustez da notação O!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 88

Melhoramentos de pequena e grande magnitude

 Pequenos melhoramentos não mudam a classe de complexidade!

Exemplo: Dado um array de nN empregados e um nome (input),


saber se existe um empregado com esse nome (output)

boolean pesquisalinear(Empregado[] empregados, int n, String nome){


int i = 0;
boolean sim = false;
while(i < n){
if( empregados[i].nome “==” nome) sim = true;
i = i +1;
}
return sim;
}  O(n)

boolean pesquisalinear(Empregado[] empregados, int n, String nome){


int i = 0;
boolean sim = false;
while(!sim && i < n){
if( empregados[i].nome “==” nome) sim = true;
i = i +1;
}
return sim;
}  O(n)

boolean pesquisalinear(Empregado[] empregados, int n, String nome){


int i = 0;
empregados[n] = new Empregado(nome, 0.0);
while(empregados[i].nome “==” nome) i = i +1;
return i != n;
}  O(n)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 89

 Mudar a classe de complexidade exige uma reformulação mais


profunda do algoritmo!

Exemplo: Dado um array de nN empregados ordenados por nome


e um nome (input), saber se existe um empregado com esse nome
(output)

boolean pesquisabinária(Empregado[] empregados, int n, String nome){


return pesquisa(empregados, 0, n-1, nome);
}

boolean pesquisa (Empregado[] empregados, int a, int b, String nome){


int meio = (a+b)/2;
if( empregados[meio].nome “==” nome) return true;
else{
if( nome “<” empregados[meio].nome){
if(a<meio) return pesquisa(empregados, a, meio-1, nome);
else return false;
}
else{ if (meio<b) return pesquisa(empregados, meio+1, b, nome);
else return false;
}
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 90

Análise de complexidade:

Considere-se um array com n = 24-1 = 15 elementos e a seguinte


árvore.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Cada nó da árvore representa uma operação de comparação.

Cada ramo a partir do nó raiz até outro nó da árvore representa uma


execução possível do algoritmo.

Melhor caso: 1 operação (não passa do 1º nível da árvore)

Pior caso: 4 comparações (tantas comparações quantos os níveis da


árvore)

 Uma árvore binária completa tem 2nºníveis-1 nós, i.e.


nºníveis≈log2n

 O(log n)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 91

Regras práticas de análise

 Para calcular a classe de complexidade basta contabilizar


comparações! O que não é contabilizado é apenas uma constante
multiplicativa.
 iterações simples são O(n); iterações duplas são O(n2), …

Eficiência de algoritmos recursivos

Exemplo: Torres de Hannoi

Édouard Lucas, Nouveaux jeux scientifiques. La Nature, 17:301–303, 1889.

void Hanoi(int n, char posteInicial, char posteFinal, char posteAuxiliar){


if(n == 1) System.out.println(posteInicial + “ → ” + posteFinal);
else{
Hanoi(n-1, posteInicial, posteAuxiliar, posteFinal);
System.out.println(posteInicial + “→” + posteFinal);
Hanoi(n-1, posteAuxiliar, posteFinal, posteInicial);
}
}

1 , n=1
T(n) =
2T(n-1) +1 , n>1

Que explicitando dá T(n) = 2n-1 , n≥1  O(2n)

 n=64 e 106 posicionamentos/seg, 0,5x106 anos

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 92

Problemas e algoritmos

Problema: consiste em: 1. Caracterização de uma colecção de


entradas / inputs potenciais (possivelmente em número infinito); 2.
Especificação das saídas/outputs desejados em função dos inputs.

Algoritmo: uma solução de um problema. É composto por


instruções elementares de um conjunto pré-definido e deve ser capaz
de produzir o outputs especificado para cada um dos inputs
potenciais.

Problema: Dados nN (input), calcular 1+2+3+ …+n (output)

Algoritmo 1: int soma(int n){


int s =0;
for(int i=1; i<=n;i++) s = s + 1;
return s;
}  O(n)
Algoritmo 2: int gauss(int n){ return n*(n+1)/2;}  O(1)

Complexidade de um problema: complexidade do melhor


algoritmo que o resolve.

 O melhor algoritmo que resolve um problema pode não ser


conhecido.

Problema “Pesquisa em listas ordenadas”: Dado um array de


nN empregados ordenados por nome e um nome (input), saber se
existe um empregado com esse nome (output)

pesquisa linear O(n)


pesquisa binária O(log n)
será que podemos fazer melhor?
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Pilhas - 93

Limites superior e inferior de um problema

 A complexidade de um problema pode não ser conhecida, mas


pode ser delimitada superiormente e inferiormente

Limite superior: complexidade do melhor algoritmo, entre os


algoritmos conhecidos. A descoberta de um novo algoritmo mais
eficiente faz descer o limite superior.

Limite inferior: complexidade estabelecida por técnicas de prova


que comprovem que não existem algoritmos mais eficientes.
Qualquer algoritmo, conhecido ou desconhecido, deverá ser mais
complexo do que esse limite.

Problemas fechados e abertos: Um problema diz-se fechado


quando o seu limite superior e inferior coincidem. Caso contrário
diz-se aberto.

Exemplos de problemas fechados


pesquisa em listas ordenadas O(log n)
pesquisa em listas não ordenadas O(n)
ordenação O(n log n)
torres de Hanoi O(2n)

Exemplos de problemas abertos


“Caixeiro Viajante”/“Traveling Salesman”: Dada uma rede de nN
cidades com as distâncias de cada ligação (input), qual o caminho
mais curto que permite visitar todas as cidades antes de chegar à
cidade de origem? (output).
 limite superior O(n!); limite inferior O(n)
 n=25 e 106 cálculos/seg, 490x109 anos, mais do que o tempo que
já passou desde o Big Bang

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 94

PILHAS

Pilhas: Estruturas de armazenamento linear de dados com disciplina


de acesso LIFO (Last In First Out)

ESPECIFICAÇÃO ABSTRACTA

especificação pilhas[T] =
boolean 
géneros
Pilha
operações
construtoras
nova:  Pilha;
sobrepoe: Pilha T  Pilha
acessórias
retira: Pilha  Pilha;
topo: Pilha  T;
vazia: Pilha  boolean
axiomas
( PPilha) ( NT)
retira(sobrepoe(P,N)) = P ;
topo(sobrepoe(P,N)) = N ;
vazia(nova) = true ;
vazia(sobrepoe(P,N)) = false
pré-condições
( PPilha)
retira(P) requer !vazia(P);
topo(P) requer !vazia(P)
fim
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Pilhas - 95

IMPLEMENTAÇÃO

//Pilha.java

public interface Pilha{


public void nova();
public void sobrepoe(Object n);
public void retira();
public Object topo();
public boolean vazia();
}

PS: Excluem-se nesta implementação pilhas cujo tipo base é um tipo


primitivo Java (int, long, char, boolean, float, double, ...)!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 96

IMPLEMENTAÇÃO ESTÁTICA (MATRICIAL)

A pilha
o4
o3
o2
o1

é representada por:

ve o1 o2 o3 o4 ? … ?
0 lim-1

it

Comentários:

a) a pilha está vazia sse: it = = -1

b) a pilha está cheia sse: it = = lim-1

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 97

// PilhaMatricial.java

import java.io.*;

public class PilhaMatricial implements Pilha {

protected static final int lim=100;

protected Object [] ve=new Object[lim]; // matriz de elementos


protected int it; // indicador de topo

protected Object ITEM_NOT_FOUND;

public void nova(){


it=-1;
}

public void sobrepoe(Object n){


if(it!=lim-1) ve[++it]=n;
else System.out.println(
"Erro - sobrepoe: capacidade da pilha excedida!");
}

public void retira(){


if(it!=-1) it--;
else System.out.println("Erro - retira: pilha vazia!");
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 98

public Object topo(){


if(it!=-1) return ve[it];
else{
System.out.println("Erro - topo: pilha vazia!");
return ITEM_NOT_FOUND;
}
}

public boolean vazia(){


return it==-1;
}
}

PS - No desenho por contrato dever-se-á ter em consideração o


invariante de classe:

@invariante it>=-1 && it<lim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 99

IMPLEMENTAÇÃO DINÂMICA (LISTAS ENCADEADAS)

A pilha
o4
o3
o2
o1

é representada por:

o4 o3 o2 o1
top

Comentários:

a pilha está vazia sse: top = = null

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 100

// PilhaLigada.java

import java.io.*;

public class PilhaLigada implements Pilha{

protected class No{


private Object val;
private No seg;
}

protected No top;

protected Object ITEM_NOT_FOUND;

public void nova(){


top = null;
}

public void sobrepoe(Object n){


No aux = new No();
aux.val = n;
aux.seg = top;
top = aux;
}

public void retira(){


if(top!= null) top = top.seg;
else System.out.println("Erro - retira: pilha vazia!");
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 101

public Object topo(){


if(top!= null) return top.val;
else{
System.out.println("Erro - topo: pilha vazia!");
return ITEM_NOT_FOUND;
}
}

public boolean vazia(){


return top==null;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 102

Exercícios

1. Desenvolva um programa que permita avaliar expressões


aritméticas em notação postfixa (envolvendo apenas inteiros e os
operadores de adição e multiplicação). Considere que a expressão
pode ser fornecida ao programa via linha de comando.

(5x( ( ( 9+8)x(4x6))+7))  notação infixa

x( 5 , +( x( +( 9 , 8 ), x( 4 , 6 ) ), 7) )  notação prefixa

( 5 , ( ( ( 9 , 8 )+ , ( 4 , 6 )x )x , 7 )+ )x  notação postfixa

Observação: As notações prefixa e postfixa não necessitam


parênteses para desambiguar as prioridades das operações. Assim
basta escrever

x5+x+98x467  notação prefixa

598+46xx7+x  notação postfixa

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 103

Sugestão: Cada inteiro é introduzido na pilha. Quando aparece o


operador + ou x extraem-se os dois últimos inteiros da pilha, faz-se
o cálculo e coloca-se o resultado na pilha.

5 5

9 5 9

8 5 9 8

+ 5 17

4 5 17 4

6 5 17 4 6

x 5 17 24

x 5 408

7 5 408 7

+ 5 415

x 2075

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 104

2. Desenvolva um programa que permita converter expressões


aritméticas em notação infixa para notação postfixa. Considere que
a expressão pode ser fornecida ao programa via linha de comando.

Sugestão: Cada inteiro é imediatamente impresso no “output”; os


parêntesis esquerdos são ignorados; os operadores + ou x são
colocados na pilha; quando aparece um parêntesis direito extrai-se
o último operador da pilha que é impresso no “output”.

(
5 5
x x
( x
( x
( x
9 9 x
+ x +
8 8 x +
) + x
x x x
( x x
4 4 x x
x x x x
6 6 x x x
) x x x
) x x
+ x +
7 7 x +
) + x
) x

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Pilhas - 105

3. Enriqueça a especificação pilhas[T] com a operação total (que


dada uma pilha indica qual o número de elementos na pilha).

4. Implemente o tipo de dados abstracto referido no exercício


anterior.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 106

FILAS

Filas: Estruturas de armazenamento linear de dados com disciplina


de acesso FIFO (First In First Out)

ESPECIFICAÇÃO ABSTRACTA

especificação filas[T] =
boolean 
géneros
Fila
operações
construtoras
nova:  Fila;
entra: Fila T  Fila
acessórias
sai: Fila  Fila;
primeiro: Fila  T;
vazia: Fila  boolean
axiomas
( FFila) ( N,N1,N2T)
sai(entra(nova,N)) = nova ;
sai(entra(entra(F,N1),N2)) = entra(sai(entra(F,N1)),N2) ;
primeiro(entra(nova,N)) = N ;
primeiro(entra(entra(F,N1),N2)) = primeiro(entra(F,N1)) ;
vazia(nova) = true ;
vazia(entra(F,N)) = false
pré-condições
( FFila)
sai(F) requer !vazia(F);
primeiro(F) requer !vazia(F)
fim
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Filas - 107

IMPLEMENTAÇÃO

// Fila.java

public interface Fila{


public void nova();
public void entra(Object n);
public void sai();
public Object primeiro();
public boolean vazia();
}

PS: Excluem-se nesta implementação filas cujo tipo base é um tipo


primitivo Java (int, long, char, boolean, float, double, ...)!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 108

IMPLEMENTAÇÃO ESTÁTICA (MATRICIAL)

A fila

o1 o2 o3 o4

é representada por:

ve o3 o4 ? ? … o1 o2
0 lim-1

ult prim

Comentários:

a) a posição que se segue no vector a uma dada posição N é:

(N+1)%lim  seg(N)

b) a fila está vazia sse: prim = = ult = = -1

c) a fila está cheia sse: seg(ult) = = prim

d) a fila tem um só elemento sse: 0 <= prim = = ult <= lim-1

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 109

// FilaMatricial.java

import java.io.*;

public class FilaMatricial implements Fila{

protected static final int lim=100;

protected Object [] ve=new Object[lim]; // matriz de elementos


protected int prim, ult; // indicadores do primeiro e do ultimo

protected Object ITEM_NOT_FOUND;

protected static int seg(int i){


return (i+1)%lim;
}

public void nova(){


prim = -1;
ult = -1;
}

public void entra(Object n){


if(seg(ult)!= prim){
ult=seg(ult);
ve[ult]=n;
if(prim==-1) prim=0;
}
else System.out.println(
"Erro - entra: capacidade da fila excedida!");
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 110

public void sai(){


if(prim!=-1){
if(prim!=ult)
prim=seg(prim);
else{
prim=-1;
ult=-1;
}
}
else System.out.println("Erro - sai: fila vazia!");
}

public Object primeiro(){


if(prim!=-1) return ve[prim];
else{
System.out.println("Erro - primeiro: fila vazia!");
return ITEM_NOT_FOUND;
}
}

public boolean vazia(){


return prim == -1;
}
}

PS - No desenho por contrato dever-se-á ter em consideração o


invariante de classe:

@invariante prim>=-1 && prim<lim && ult>=-1 && ult<lim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 111

IMPLEMENTAÇÃO DINÂMICA (LISTAS ENCADEADAS)

A fila

o1 o2 o3 o4

é representada por:

prim o1 o2 o3 o4
ult

Comentários:

a fila está vazia sse: prim = = ult = = null

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 112

// FilaLigada.java

import java.io.*;

public class FilaLigada implements Fila{

protected class No{


Object val;
No seg;
};

protected No prim, ult;

protected Object ITEM_NOT_FOUND;

public void nova(){


prim = null;
ult = null;
}

public void entra(Object n){


No aux = new No();
aux.val = n;
aux.seg = null;
if(prim != null) ult.seg = aux;
else prim=aux;
ult=aux;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 113

public void sai(){


if(prim!= null){
prim = prim.seg;
if(prim == null) ult = null;
}
else System.out.println("Erro - sai: fila vazia!");
}

public Object primeiro(){


if(prim!= null) return prim.val;
else{
System.out.println("Erro - primeiro: fila vazia!");
return ITEM_NOT_FOUND;
}
}

public boolean vazia(){


return prim==null;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas - 114

Exercícios

1. No contexto da especificação filas[int] calcule o valor dos


seguintes termos:

a) sai(entra(entra(entra(nova,7),9),5))

b) primeiro(entra(entra(entra(nova,7),9),5))

2. Enriqueça a especificação filas[T] com as operações total (que


dada uma fila indica qual o número de elementos na fila),
retiratodos (que dada uma fila e um elemento retira todas as
ocorrências desse elemento dessa fila, retornando a mesma fila se
esse elemento não ocorrer nela) e elemento (que dada uma fila e um
inteiro N indica qual o elemento que se encontra na N-ésima
posição da fila a contar do seu início, no caso de N ser um inteiro
positivo inferior ou igual ao número de elementos na fila).

3. Sobre a camada correspondente à especificação filas[T],


desenvolva uma função que recebe uma fila e retorna a mesma fila
mas sem o elemento que se encontra na última posição.

4. Implemente o tipo de dados abstracto referido no exercício 2.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 115

LISTAS
Listas: Estruturas de armazenamento linear de dados

ESPECIFICAÇÃO ABSTRACTA

especificação listas[T] =
boolean  int 
géneros
Lista
operações
construtoras
nova:  Lista;
acr: Lista T  Lista
acessórias
retirapos: Lista int Lista;
elementopos: Lista int T;
existe: Lista T  boolean;
comprimento: Lista  int;
vazia: Lista  boolean
axiomas
( LLista) ( O,O1,O2T) ( Nint)

comprimento(nova) = 0;
comprimento(acr(L,O)) = 1 + comprimento(L);

retirapos(acr(L,O), N) = se N>0 && N<=comprimento(L)+1


então ( se N==comprimento(L)+1
então L
senão acr(retirapos(L,N),O)
);
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Listas - 116

elementopos(acr(L,O),N) =se N>0 && N<=comprimento(L)+1


então ( se N==comprimento(L)+1
então O
senão elementopos(L,N)
);

existe(nova, O) = false ;
existe (acr(L,O1),O2) = se O1 =T O2
então true
senão existe (L,O2);

vazia(nova) = true ;
vazia(acr(L,O)) = false

pré-condições
( LLista) ( Nint)
retirapos(L,N) requer N>0 && N<=comprimento(L);
elementopos(L,N) requer N>0 && N<=comprimento(L)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 117

IMPLEMENTAÇÃO

// Lista.Java

public interface Lista{


public void nova();
public void acr(Object n);
public int comprimento();
public void retirapos(int i);
public Object elementopos(int i);
public boolean existe(Object n);
public boolean vazia();
}

PS: Excluem-se nesta implementação listas cujo tipo base é um tipo


primitivo Java (int, long, char, boolean, float, double, ...)!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 118

IMPLEMENTAÇÃO ESTÁTICA (MATRICIAL)

// ListaMatricial.Java

import java.io.*;

public class ListaMatricial implements Lista{

protected static final int lim=100;

protected Object[] ve=new Object[lim]; // matriz de elementos


protected int prim, ult; // indicadores do primeiro e do ultimo

protected Object ITEM_NOT_FOUND;

protected static int seg(int i){


return (i+1)%lim;
}

protected static int ant(int i){


if (i==0) return lim-1;
else return i-1;
}

public void nova(){


prim = -1;
ult = -1;
}

public boolean vazia(){


return prim == -1;
}
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Listas - 119

public void acr(Object n){


if(seg(ult)!= prim){
ult=seg(ult);
ve[ult]=n;
if(prim==-1) prim=0;
}
else System.out.println(
"Erro - acr: capacidade da lista excedida!");
}

public int comprimento(){


if(prim==-1) return 0;
else if(prim<=ult) return ult-prim+1;
else return lim-prim+ult+1;
}

public boolean existe(Object n){


if(prim != -1){
boolean encontrei=n.equals(ve[prim]);
for(int j=prim;j!=ult && !encontrei;j=seg(j))
if(n.equals(ve[seg(j)])) encontrei=true;
return encontrei;
} else return false;
}

public Object elementopos(int i){


if(i>0 && i<=comprimento()) return ve[(prim+i-1)%lim];
else { System.out.println(
"Erro - elementopos: posicao fora dos limites!");
return ITEM_NOT_FOUND;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 120

public void retirapos(int i){


int comp = comprimento();
if(i>0 && i<= comp){
if(prim!=ult){
int pos=(prim+i-1)%lim;
if(comp-i<i){
// arruma as componentes à direita
for(int j=pos;j!=ult;j=seg(j))
ve[j]=ve[seg(j)];
ult=ant(ult);
}
else {
// arruma as componentes à esquerda
for(int j=pos;j!=prim;j=ant(j))
ve[j]=ve[ant(j)];
prim=seg(prim);
}
}
else{
prim=-1;
ult=-1;
}
}
else System.out.println(
"Erro - retirapos: posicao fora dos limites!");
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 121

IMPLEMENTAÇÃO DINÂMICA (LISTAS ENCADEADAS)

// ListaLigada.Java

import java.io.*;

public class ListaLigada implements Lista{

protected class No{


Object val;
No seg;
};

protected No prim, ult;

protected Object ITEM_NOT_FOUND;

public void nova(){


prim=null;
ult= null;
}

public void acr(Object n){


No aux=new No();
aux.val=n;
aux.seg= null;
if(prim != null) ult.seg = aux;
else prim=aux;
ult=aux;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 122

public int comprimento(){


int comp=0;
No aux=prim;
while(aux != null){
comp++;
aux=aux.seg;
}
return comp;
}

public Object elementopos(int i){


if(i>0 && i<=comprimento()){
int ind=1;
No pos=prim;
while(ind!=i){
pos=pos.seg;
ind++;
}
return pos.val;
}
else {
System.out.println(
"Erro - elementopos: posicao fora dos limites!");
return ITEM_NOT_FOUND;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 123

public void retirapos(int i){


if(i>0 && i<=comprimento()){
int ind=1;
No ant=null, pos=prim;
while(ind!=i){
ant=pos;
pos=pos.seg;
ind++;
}
if(ant!= null){
ant.seg=pos.seg;
if(pos == ult) ult=ant;
}
else{
prim=pos.seg;
if(prim == null) ult = null;
}
}
else System.out.println(
"Erro - retirapos: posicao fora dos limites!");
}

Observação: no final do ciclo o endereço ant permite a actualização


do encadeamento dos nós da lista.
ant pos

prim o1 o2 o3 o4
ult

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 124

public boolean existe(Object n){


boolean encontrei=false;
No aux=prim;
while(aux!=null && !encontrei){
if(n.equals(aux.val)) encontrei=true;
aux=aux.seg;
}
return encontrei;
}

public boolean vazia(){


return prim == null;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Listas - 125

Exercícios

1. Enriqueça a especificação listas[int] com as operações junta


(que dadas duas listas coloca uma à frente da outra), inverte (que
dada uma lista constrói uma lista com os elementos da primeira mas
por ordem inversa), menor (que dada uma lista indica qual o menor
inteiro na lista) e retiramenor(que dada uma lista retira uma
ocorrência do menor inteiro dessa lista).

2. Sobre a camada correspondente à especificação anterior


desenvolva uma função que recebe uma lista e retorna a mesma lista
mas com todos os seus elementos ordenados por ordem crescente.

3. Esboce alterações à implementação dinâmica de listas


apresentada de modo a optimizar a função comprimento.

4. Implemente os TDAs pilhas[T] e filas[T] utilizando a


implementação de listas.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Conjuntos - 126

CONJUNTOS

Conjuntos: Estruturas de armazenamento não ordenadas e sem


duplicações.

ESPECIFICAÇÃO ABSTRACTA

especificação conjuntos[T] =
boolean  int 
géneros
Conj
operações
construtoras
vazio:  Conj;
poe: Conj T  Conj
acessórias
tira: Conj T  Conj;
estavazio: Conj  boolean;
pertence: Conj T  boolean;
uniao: Conj Conj  Conj;
interseccao: Conj Conj  Conj;
diferença: Conj Conj  Conj;
cardinal: Conj  int

axiomas
( C,C1,C2Conj) ( O,O1,O2T)

poe(poe(C,O1),O2) = poe(poe(C,O2),O1);
poe(poe(C,O),O) = poe(C,O);

Porquê axiomas sobre as operações construtoras ?


Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Conjuntos - 127

Se não impusermos quaisquer axiomas sobre as construtoras, então


cada um dos termos a seguir deverá ser visto como denotando um
conjunto distinto em conjuntos[int].

vazio

poe(vazio,3) poe(vazio,7) poe(vazio,5) ...

poe(poe(vazio,3),3) poe(poe(vazio,7),7) ...

poe(poe(vazio,7),5) poe(poe(vazio,5),7) ...

poe(poe(poe(vazio,7),5),7) poe(poe(poe(vazio,5),7),7) ...

Ora pretende-se, por exemplo, que:

poe(vazio,3) e poe(poe(vazio,3),3) denotem o conjunto {3}

poe(poe(vazio,7),5) e poe(poe(vazio,5),7) denotem o conjunto


{5,7}

poe(poe(poe(vazio,7),5),7) e poe(poe(poe(vazio,5),7),7) denotem


o conjunto {5,7}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Conjuntos - 128

tira(vazio,O) = vazio;
tira(poe(C,O1),O2) = se O1 =T O2 então tira(C,O2)
senão poe(tira(C,O2),O1);

estavazio(vazio) = true;
estavazio(poe(C,O)) = false;

pertence(vazio,O) = false;
pertence(poe(C,O1),O2) = se O1 =T O2 então true
senão pertence(C,O2);

uniao(C,vazio) = C;
uniao(C1,poe(C2,O)) = poe(uniao(C1,C2),O);

interseccao(C, vazio) = vazio;


interseccao(C1, poe(C2,O)) = se pertence(C1,O)
então poe(interseccao(C1,C2),O)
senão interseccao(C1,C2);

diferença(C, vazio) = C;
diferença(C1,poe(C2,O)) = se pertence(C1,O)
então tira(diferença(C1,C2),O)
senão diferença(C1,C2)

cardinal(vazio) = 0;
cardinal(poe(C,O)) = 1 + cardinal(tira(C,O))

fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Conjuntos - 129

MULTICONJUNTOS ou SACOS

Multiconjuntos: Estruturas de armazenamento não ordenadas.

ESPECIFICAÇÃO ABSTRACTA

especificação multiconjuntos[T] =
boolean  int 
géneros
Multi
operações
construtoras
vazio:  Multi;
poe: Multi T  Multi
acessórias
tira: Multi T  Multi;
estavazio: Multi  boolean;
pertence: Multi T  boolean;
uniao: Multi Multi  Multi;
interseccao: Multi Multi  Multi;
diferença: Multi Multi  Multi;
cardinal: Multi  int
axiomas
( C,C1,C2Multi) ( O,O1,O2T)

poe(poe(C,O1),O2) = poe(poe(C,O2),O1);

tira(vazio,O) = vazio;
tira(poe(C,O1),O2) = se O1 =T O2 então C 
senão poe(tira(C,O2),O1);

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Conjuntos - 130

estavazio(vazio) = true;
estavazio(poe(C,O)) = false;

pertence(vazio,O) = false;
pertence(poe(C,O1),O2) = se O1 =T O2 então true
senão pertence(C,O2);

uniao(C,vazio) = C;
uniao(C1,poe(C2,O)) = poe(uniao(C1,C2),O);

interseccao(C, vazio) = vazio;


interseccao(C1, poe(C2,O)) = se pertence(C1,O)
então poe(interseccao(tira(C1,O),C2),O) 
senão interseccao(C1,C2);

diferença(C, vazio) = C;
diferença(C1,poe(C2,O)) = se pertence(C1,O)
então tira(diferença(C1,C2),O)
senão diferença(C1,C2)

cardinal(vazio) = 0;
cardinal(poe(C,O)) = 1 + cardinal(C) 

fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Conjuntos - 131

Exercícios

1. No contexto da especificação conjuntos[char] calcule o valor dos


seguintes termos:

a) tira(poe(poe(poe(vazio,’a’),’b’),’a’),’a’)

b) uniao(poe(poe(vazio,’a’),’b’), poe(poe(vazio,’b’),’c’))

2. Enriqueça a especificação conjuntos[float] com as operações


singular (que dado um conjunto indica se este é singular) e menor
(que dado um conjunto não vazio indica qual o menor float que nele
ocorre).

3. Sobre a camada correspondente à especificação da alínea (2), e


sem utilizar a operação diferença atrás especificada, desenvolva
uma função que recebe dois conjuntos e retorna o conjunto
diferença entre os dois.

4. Implemente o TDA conjuntos[T].

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 132

EXEMPLO DE PROGRAMAÇÃO EM GRANDE ESCALA

Simulação digital estocástica por sequenciamento dos


acontecimentos pendentes

Exemplo: Simulação de um supermercado

Pretende-se simular um dia de funcionamento (das 9h às 20h) de um


pequeno supermercado que vai abrir, com vista a determinar da
necessidade de considerar uma segunda caixa para pagamento das
compras dos clientes.

Para esse efeito, pretende-se que a simulação a efectuar indique o


comprimento máximo da fila de espera para pagamento, o tempo
médio de permanência de cada cliente no supermercado e o tempo
que, em média, cada cliente espera para iniciar o pagamento das
suas compras. Mais ainda, as informações pretendidas deverão ser
precedidas pelo "traço" da simulação efectuada.

Para efeitos da simulação a efectuar, deve assumir-se que os tempos


entre chegadas consecutivas, de recolha dos artigos a adquirir e de
pagamento desses artigos, são variáveis aleatórias exponenciais
cujos valores expectáveis serão fornecidos no decurso de cada
simulação. Deve ainda assumir-se que todos esses tempos são
independentes uns dos outros e que não há chegadas em grupo, nem
desistências.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 133

TIPOS DE SIMULAÇÃO DE SISTEMAS REAIS

ANALÓGICA vs DIGITAL

Simulação Analógica
Utilização de modelos físicos reduzidos.

Simulação Digital
Utilização de modelos cujas mudanças de estado podem ser
descritas por colecções numeráveis de acontecimentos (EVENTOS).
Fácilmente realizavel em computador!

DETERMINÍSTICA vs ESTOCÁSTICA

Simulação Determinística
Utilização de dados completamente determinados.

Simulação Estocástica
Utilização de dados não completamente determinados, através da
observação de variáveis aleatórias.

PS: Geração de variáveis aleatórias

discreta com valores {1, ..., n}: round(1+random*n)


exponencial com valor médio n: -n*ln(random)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 134

SIMULAÇÃO POR SEQUENCIAMENTO DOS EVENTOS/


ACONTECIMENTOS PENDENTES

1. Identificação dos eventos básicos que alteram o estado do


sistema.

Evento = Categoria (Chegada, FimdeRecolha, FimdePagamento)


+ Instante +
“uma ou mais entidades do sistema” (Cliente)
PS: As categorias de eventos devem ser independentes!

2. Realização/simulação de eventos

2.1. Definição das alterações de estado do sistema (Fila de espera


para pagamento, estado do caixa) e Preparação do simulador
para continuar o processo de simulação (através da actualização
da Cadeia de Acontecimentos Pendentes - CAP).

Simulação de Chegada: geração da próxima Chegada e colocação


deste evento na CAP; geração do evento FimdeRecolha do cliente
que chegou e sua colocação na CAP.
Simulação de FimdeRecolha: se o caixa estiver desocupado então
passa a ocupado e geração do FimdePagamento e colocação na
CAP; caso contrário colocar o cliente em fila de espera.
Simulação de FimdePagamento: se a fila estiver vazia então o caixa
passa a desocupado; caso contrário retirar primeiro cliente da fila
e para ele gerar FimdePagamento e colocação na CAP.

2.2. Realização das operações de medida/observação necessárias


à obtenção dos resultados pretendidos com a simulação. (e.g.
actualização do tempo total de permanência no supermercado, para
futuro cálculo do respectivo tempo médio)
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 135

Estrutura de comandos do simulador

simulador

inicialização ciclo finalização

*
passo

determina simula
próximo evento próximo evento

o o
simula evento simula evento
categoria 1 categoria n

inicialização: leitura dos dados da simulação; geração da primeira


Chegada e colocação na CAP.

finalização: envio de mensagens com os resultados pretendidos.

determina próximo evento: evento de menor instante de entre os


eventos pendentes para simulação.

PS: o ciclo termina quando não houver mais eventos!( ... cada
Chegada depois da hora de limite não é colocada na CAP!)
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 136

DESENVOLVIMENTO DO SIMULADOR

1. Identificação dos tipos de dados relevantes:

Categoria (do evento)

Evento

CAP - Cadeia de Acontecimentos Pendentes

Cliente

Fila (de clientes à espera)

Estado (do Caixa)

PS: Para suportar o "traço" da simulação é necessário que os


eventos refiram o cliente respectivo. Portanto, para simplificar,
podemos considerar que a fila guarda eventos!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 137

2. Especificação dos tipos abstractos de dados:

especificação categoria =
géneros
Categoria
operações
construtoras
Chegada:  Categoria;
FimdeRecolha:  Categoria;
FimdePagamento:  Categoria
fim

especificação eventos =
int  double categoria 
géneros
Evento
operações
construtoras
consevt: double Categoria int  Evento
acessórias
instante: Evento  double;
cat: Evento  Categoria;
cli: Evento  int
axiomas
( Ddouble) ( C Categoria) ( Nint)
instante(consevt(D,C,N)) = D ;
cat(consevt(D,C,N)) = C ;
cliente(consevt(D,C,N)) = N
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 138

especificação cadeia_de_acontecimentos_pendentes =
boolean eventos 
géneros
CAP
operações
construtoras
inicial:  CAP
coloca: CAP Evento  CAP
acessórias
proximo: CAP  Evento;
retira: CAP  CAP;
capvazia: CAP  boolean
axiomas
...
pré-condições
...
fim

especificação fila_de_eventos = fila[Evento] fim

especificação estado =
géneros
Estado
operações
construtoras
Ocupado:  Estado;
Desocupado:  Estado
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 139

3. Desenvolvimento do simulador abstracto puro (simulador


sem instrumentação)

import java.io.*;
import personal.io.*;

public class Simulador{

static double obsexp(double m){ // geracao v. a. exponencial


return –m*Math.log(Math.random());
}

static final int abertura=540, // hora da abertura em minutos


fecho=1200; // hora do fecho em minutos

public static void main(String[] args){

//inicializacao
System.out.print( “Tempo medio entre chegadas: ”);
double mc=Keyboard.readDouble();
System.out.print( “Tempo medio de auto-servico: ”);
double ma=Keyboard.readDouble();
System.out.print( “Tempo medio de pagamento: ”);
double mp=Keyboard.readDouble();
double t = abertura; // instante corrente da simulação
Estado est = Desocupado; // estado corrente do caixa
Fila[Evento] f = nova;
CAP c = inicial;

// gerar e colocar na cap a primeira chegada

double ova = obsexp(mc); // observação de var aleatoria


if (t+ova < fecho) c = coloca(c, consevt(t+ova, Chegada, 1));
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 140

// ciclo de simulacao

while(!capvazia(c)){

// dermina proximo evento


Evento e = proximo(c);
c = retira(c);
t = instante(e);

// simula evento

if( cat(e) == Chegada){


ova = obsexp(mc);
if (t+ova < fecho)
c = coloca(c,consevt(t+ova, Chegada, cli(e)+1));
ova = obsexp(ma);
c = coloca(c, consevt(t+ova, FimdeRecolha, cli(e)));
}

if( cat(e) == FimdeRecolha){


if(est == Ocupado) f = entra(f,e);
else{
est = Ocupado;
ova = obsexp(mp);
c = coloca(c,consevt(t+ova,FimdePagamento,cli(e)));
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 141

if( cat(e) == FimdePagamento){


if(vazia(f)) est = Desocupado;
else{
ova = obsexp(mp);
c = coloca(c, consevt(t+ova,
FimdePagamento,
cli(primeiro(f))));
f = sai(f);
}
}
}

// finalizacao

System.out.print( “Terminou a simulacao de um dia ”);


System.out.println(“de funcionamento do supermercado”);
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 142

3’. Desenvolvimento do simulador abstracto instrumentado

Instrumentação pretendida:

Comprimento máximo da fila de espera: cmax, cfe

Tempo médio de permanência de cada cliente: ttp/num

Tempo médio de espera para iniciar o pagamento: tte/num

"traço" da simulação efectuada

Actualização das variáveis utilizadas na instrumentação:

Dois comportamentos possíveis dos clientes do supermercado:

1. o cliente chega, acaba de recolher os produtos e vai logo para a


caixa (o caixa está desocupado)

ova ova
Chegada FimdeRecolha FimdePagamento
num=num+1; ttp=ttp+ova;
ttp=ttp+ova;

2. o cliente chega, acaba de recolher os produtos, entra na fila e vai


para a caixa (quando chegar a sua vez)
t’ t
ova (t-t’) ova
Chegada FimdeRecolha FimdePagamento
num=num+1; cfe=cfe+1; ttp=ttp+(t+ova-t’);
ttp=ttp+ova; “actualizar cmax” tte=tte+(t-t’);
cfe=cfe-1;

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 143

import java.io.*;
import personal.io.*;

public class Simulador{

static double obsexp(double m){ // geracao v. a. exponencial


return –m*Math.log(Math.random());
}

static final int abertura=540, // hora da abertura em minutos


fecho=1200; // hora do fecho em minutos

public static void main(String[] args){

//inicializacao
System.out.print( “Tempo medio entre chegadas: ”);
double mc=Keyboard.readDouble();
System.out.print( “Tempo medio de auto-servico: ”);
double ma=Keyboard.readDouble();
System.out.print( “Tempo medio de pagamento: ”);
double mp=Keyboard.readDouble();
double t = abertura; // instante corrente da simulação
Estado est = Desocupado; // estado corrente do caixa
Fila[Evento] f = nova;
CAP c = inicial;

 int cfe=0, // comprimento da fila


 cmax=0, // comprimento maximo da fila
 num=0; // numero de chegadas
 double ttp=0, // tempo total de permanencia
 tte=0; // tempo total de espera pagar

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 144

// gerar e colocar na cap a primeira chegada


double ova = obsexp(mc); // observação de var aleatoria
if (t+ova < fecho) c = coloca(c, consevt(t+ova, Chegada, 1));

 // cabecalho do traco da simulacao


 System.out.println(“Traco da simulacao: ”);

// ciclo de simulacao

while(!capvazia(c)){

// dermina proximo evento


Evento e = proximo(c);
c = retira(c);
t = instante(e);

// simula evento

if( cat(e) == Chegada){


 num = num + 1;
 System.out.print(“instante ”+t);
 System.out.println(“ : chegada do cliente ”+num);
ova = obsexp(mc);
if (t+ova < fecho)
c = coloca(c,consevt(t+ova, Chegada, cli(e)+1));
ova = obsexp(ma);
 ttp = ttp + ova;
c = coloca(c, consevt(t+ova, FimdeRecolha, cli(e)));
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 145

if( cat(e) == FimdeRecolha){


if(est == ocupado) {
 System.out.print(“instante ”+t+“ : o cliente ”);
 System.out.print(cli(e)+“ acabou recolha e”);
 System.out.println(“ entra na fila”);
f = entra(f,e);
 cfe = cfe + 1;
 if(cfe > cmax) cmax =cfe;
}
else{
 System.out.print(“instante ”+t+“ : o cliente ”);
 System.out.print(cli(e)+“ acabou recolha e”);
 System.out.println(“ inicia pagamento”);
est = Ocupado;
ova = obsexp(mp);
 ttp = ttp + ova;
c = coloca(c,consevt(t+ova,FimdePagamento,cli(e)));
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 146

if( cat(e) == FimdePagamento){


if(vazia(f)){
est = Desocupado;
 System.out.print(“instante ”+t+“ : o cliente ”);
 System.out.print(cli(e)+“ sai do super e o caixa”);
 System.out.println(“ fica desocupado”);
}
else{
 System.out.print(“instante ”+t+“ : o cliente ”);
 System.out.print(cli(e)+“ sai do super e o ”);
 System.out.print(“cliente ”+cli(primeiro(f)));
 System.out.println(“ inicia pagamento”);
 tte = tte + (t – instante(primeiro(f)));
ova = obsexp(mp);
 ttp = ttp + (t + ova – instante(primeiro(f)));
c = coloca(c, consevt(t+ova, FimdePagamento,
cli(primeiro(f))));
f = sai(f);
 cfe = cfe – 1;
}
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 147

// finalizacao

 if(num == 0)
System.out.println(“Nao chegou nenhum cliente!”);
 else {
 System.out.print(“Comprimento maximo da fila: ”);
 System.out.println(cmax);
 System.out.print(“Tempo medio permanencia: ”);
 System.out.println(ttp/num);
 System.out.print(“Tempo medio espera pagar: ”);
 System.out.println(tte/num);
}

System.out.print( “Terminou a simulacao de um dia ”);


System.out.println(“de funcionamento do supermercado”);
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 148

Método: Abordagem de programação modular descendente por


camadas e centrada nos dados à simulação digital estocástica
por sequenciamento dos eventos pendentes

1. Identificação dos eventos básicos relevantes e análise da


informação que é necessário associar a cada evento.

2. Definição dos tipos básicos: eventos e categorias.

3. Análise das tarefas de preparação do simulador para continuar


a simulação para realização/simulação de um evento de cada
uma das categorias e identificação dos tipos necessários (para
além da "CAP").

4. Especificação dos tipos de dados abstractos necessários.

5. Desenvolvimento do simulador abstracto puro.

6. Desenvolvimento do simulador abstracto instrumentado


(instrumentação do simulador abstracto puro)

7. Implementação dos tipos de dados abstractos e reificação do


simulador abstracto instrumentado.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 149

Exercícios

1. Implemente os tipos de dados abstractos utilizados no


simulador e reifique o simulador abstracto instrumentado do
supermercado.

2. A Empresa RAIOX,SA tem vários consultórios radiológicos a


operar no país em localidades pequenas. Estes consultórios têm
todos a mesma concepção de funcionamento: são constituidos por
um guiché de atendimento, um laboratório de raios-X e por um
serviço de revelação de chapas; cada paciente chega, preenche um
impresso e dirige-se ao guiché para recepção dos documentos
necessários e marcação de vez. Depois aguarda a chamada numa
sala de espera caso não possa ser imediatamente observado.
Terminado o exame cada paciente terá de aguardar na sala de espera
pela revelação da radiografia. Uma vez terminada a revelação o
paciente pode sair a menos que tenham surgido problemas que
obriguem a uma (e uma só) repetição da radiografia. Neste caso, ele
tem prioridade sobre todos os que aguardam a sua vez para efectuar
a primeira radiografia.
A administração da RAIOX,SA pretende abrir um consultório
radiológico em Lisboa. Obviamente espera ter muito mais pacientes
a procurar os seus serviços e portanto planeou um consultório com
um guiché e um laboratório de raios-X adicionais. Pretende no
entanto prever o desempenho deste novo laboratório pois sabe que
em Lisboa perderá clientes caso estes permaneçam no consultório
mais do que 60 minutos. Por outro lado sabe que 70% dos pacientes
desistem caso verifique que existem mais de 30 pessoas à espera
para serem atendidas nos guichés.
Auxilie a administração RAIOX,SA a prever o funcionamento
do seu futuro consultório radiológico.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Simulação Digital Estocástica - 150

Para tal pretende-se a implementação em Java de um simulador


desse consultório radiológico, segundo a estratégia do
sequenciamento dos eventos pendentes e seguindo a metodologia da
programação por camadas e centrada nos dados.
Para efeitos da simulação a efectuar, assuma que o tempo entre
chegadas dos pacientes, o tempo de preenchimento dos impressos,
os tempos de atendimento nos guichés, os tempos de serviço dos
laboratórios de raios-X e o tempo de revelação das chapas podem ser
descritos por variáveis aleatórias exponenciais cujos valores médios
devem ser fornecidos no decurso de cada simulação. Assuma ainda
que após o preenchimento do impresso, cada cliente escolherá a fila
de espera para recepção dos documentos de menor comprimento (em
caso de filas com o mesmo comprimento a escolha é aleatória) e que
o fim do atendimento nos guichés define a ordenação dos pacientes
para tirar radiografias.
Pretende-se que a simulação incida sobre um dia de serviço
(início às 8h e fecho às 19h no que respeita à entrada de novos
pacientes) de modo a obter a média dos tempos de permanência no
consultório dos pacientes que não desistiram, o número de
desistências, a hora em que efectivamente fechou o consultório e o
traço dos eventos simulados (instante e categoria).

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 151

ÁRVORES

Árvores: Estruturas de armazenamento não linear de dados.


Oferecem melhor desempenho no que respeita ao armazenamento e
pesquisa de dados comparativamente às estruturas de
armazenamento lineares.

Informalmente, uma árvore, de elementos em T (o "tipo base"), é


uma estrutura com a forma de uma "árvore invertida" (isto é, em
que o nó raiz aparece em cima), em que cada nó guarda um
elemento de T e pode possuir um número variável de nós
descendentes directos.

Exemplo de uma árvore de inteiros:

nó raiz 66 nó interno

nó interno 99 5 -1 nó interno

folha

43 22 76
folha folha folha

Definição de árvore n-área (n≥1):

1) a árvore vazia < > é uma árvore n-área ;


2) se x é um elemento de T e A1, ... ,An são árvores n-áreas, então
<x;A1,...,An> é uma árvore n-área.
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Árvores - 152

Nota: Nas árvores binárias é usual considerar a notação (infixa)


<A1,x,A2>, em vez da notação (prefixa) <x,A1,A2>, dizendo-se
que A1 e A2 são as sub-árvores esquerda e direita da árvore
A=<A1,x,A2>.

Isto é, a árvore binária de inteiros

66

99 -1

43 22 76

pode ser representada por

< < <<>,43,<>> ,99, <> > ,66, < <<>,22,<>> ,-1, <<>,76,<>> > >

66

99 -1

43 22 76

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 153

ESPECIFICAÇÃO ABSTRACTA (ÁRVORES BINÁRIAS)

especificação arvores binárias[T] =


boolean 
géneros
Arvb
operações
construtoras
nova:  Arvb;
cons: Arvb T Arvb  Arvb
acessórias
arvesq: Arvb  Arvb;
arvdir: Arvb  Arvb;
raiz: Arvb  T;
vazia: Arvb  boolean
axiomas
( A1,A2Arvb) ( O,O1,O2T)
arvesq(cons(A1,O,A2)) = A1 ;
arvdir(cons(A1,O,A2)) = A2 ;
raiz(cons(A1,O,A2)) = O ;
vazia(nova) = true ;
vazia(cons(A1,O,A2)) = false
pré-condições
( AArvb)
arvesq(A) requer !vazia(A);
arvdir(A) requer !vazia(A);
raiz(A) requer !vazia(A)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 154

Enriquecimento de árvores binárias com a operação de pesquisa de


um elemento:

especificação pesquisa em arvores binárias[T] =


arvores binárias[T] 
operações
acessórias
existe: Arvb T  boolean
axiomas
( A1,A2Arvb) ( O,O1,O2T)
existe(nova,O) = false;
existe(cons(A1,O1,A2),O2) = se O1 =T O2
então true
senão ( se existe(A1,O2) então true
senão existe(A2,O2)
)

ou

existe(cons(A1,O1,A2),O2) = se O1 =T O2
então true
senão existe(A1,O2) || existe(A2,O2)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 155

IMPLEMENTAÇÃO DINÂMICA

A árvore a
o1

o2 o3

o4 o5 o6

é representada por:

o1

o2 o3

o4 o5 o6

Comentário: a árvore a está vazia sse: a = = null

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 156

IMPLEMENTAÇÃO 1 – implementação respeitando as


assinaturas das operações

// Arvb.java

import java.io.*;

public class Arvb{

protected Comparable raiz; 


protected Arvb arvesq;
protected Arvb arvdir;

protected static Comparable ITEM_NOT_FOUND;

public static Arvb nova(){


return null;
}

public static Arvb cons(Arvb a1, Comparable n, Arvb a2){


Arvb aux = new Arvb();
aux.raiz= n;
aux.arvesq = a1;
aux.arvdir = a2;
return aux;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 157

public static Comparable raiz(Arvb a){


if(a!=null) return a.raiz;
else{System.out.println("Erro - raiz: arvore vazia!");
return ITEM_NOT_FOUND;
}
}

public static Arvb arvesq(Arvb a){


if(a!=null) return a.arvesq;
else{System.out.println("Erro - arvesq: arvore vazia!");
return null;
}
}

public static Arvb arvdir(Arvb a){


if(a!=null) return a.arvdir;
else{System.out.println("Erro - arvdir: arvore vazia!");
return null;
}
}

public static boolean vazia(Arvb a){


return a==null;
}

Como implementar a operação existe?

}
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Árvores - 158

Os axiomas propostos para a operação existe:

( A1,A2Arvb) ( O,O1,O2T)
existe(nova,O) = false;
existe(cons(A1,O1,A2),O2) = se O1 == T O2
então true
senão existe(A1,O2) || existe(A2,O2)

podem ser resumidos no seguinte axioma:

( AArvb) ( OT)
existe(A,O) =
se vazia(A) então false
senão ( se raiz(A) ==T O
então true
senão existe(arvesq(A),O) || existe(arvdir(A),O)
)

portanto:

public static boolean existe(Arvb a, Comparable n){


if(vazia(a)) return false;
else if(n.equals(raiz(a))) return true;
else return existe(arvesq(a), n) || existe(arvdir(a), n);
}

ou seja:

public static boolean existe(Arvb a, Comparable n){


if(a==null) return false;
else if(n.equals(a.raiz)) return true;
else return existe(a.arvesq, n) || existe(a.arvdir, n);
}
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Árvores - 159

ALGUNS CONCEITOS IMPORTANTES

número de nós

nos: Arvb  int

nos(A) = se vazia(A) então 0


senão 1 + nos(arvesq(A)) + nos(arvdir(A))

profundidade (altura)

prof: Arvb  int

prof(A) = se vazia(A) então 0


senão 1 + max(prof(arvesq(A)),prof(arvdir(A)))

nível de um nó

- a raiz tem nível 1;


- o nível de qualquer outro nó é igual ao sucessor do nível do pai.

A profundidade de uma árvore não vazia é igual ao nível máximo


dos seus nós!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 160

completa

Uma árvore binária de profundidade h está completa sse todos os


níveis até h-1 estão repletos e o nível h está preenchido da esquerda
para a direita.

equilibrada

Uma árvore está equilibrada (em altura ou árvore AVL) quando é


vazia ou quando as suas árvores esquerda e direita estão também
equilibradas em altura e as suas alturas não diferem em mais de uma
unidade.

cheia

Uma árvore binária de profundidade h está está cheia sse o seu nível
h está totalmente preenchido (i.e. tem todas as suas folhas.

cheia: Arvb  boolean

cheia(A) = vazia(A) ||
( cheia(arvesq(A)) && cheia(arvdir(A)) &&
prof(arvesq(A)) == prof(arvdir(A))
)

h
número de nós numa árvore binária cheia de profundidade h : 2 -1.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 161

travessia

processo de visitar todos os nós de uma árvore binária uma e uma só


vez.

travessia prefixa: 1) "visitar" nó raiz (caso se trate de uma


árvore vazia terminou a travessia); 2) travessia da subárvore
esquerda; 3) travessia da subárvore direita;

travessia infixa: 1) travessia da subárvore esquerda; 2) "visitar"


nó raiz; 3) travessia da subárvore direita;

travessia sufixa: 1) travessia da subárvore esquerda; 2) travessia


da subárvore direita; 3) "visitar" nó raiz.

Exemplo:
o1

o2 o3

o4 o5 o6

travessia prefixa: o1, o2, o4, o3, o5, o6


travessia infixa: o4, o2, o1, o5, o3, o6
travessia sufixa: o4, o2, o5, o6, o3, o1

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 162

ÁRVORES BINÁRIAS DE PESQUISA

Árvores binárias de pesquisa (de elementos em T munido de uma


ordem parcial): árvores binárias em que o valor guardado em cada
nó é ≥ (respectivamente <) do que o valor guardado em qualquer
dos nós da sua subárvore esquerda (respectivamente direita). Nota:
uma árvore vazia é uma árvore de pesquisa.

pesquisa, inserção e anulação em árvores binárias de pesquisa

pesquisa: “saber se O ocorre na árvore A”


Algoritmo:
1) se a árvore A estiver vazia, o resultado é false
2) senão, comparar O com o valor guardado no nó raiz e:
a) se for igual, o resultado é true
b) se for <, ver se O ocorre na subárvore esquerda
c) se for >, ver se O ocorre na subárvore direita

inserção: “inserir o valor O na árvore A”


Algoritmo:
1) pesquisar posição onde o novo nó deve ser inserido
(de modo a que a árvore permaneça de pesquisa)
2) criar o novo nó e inseri-lo na árvore
(nota: o novo nó ficará sempre a ser uma folha !)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 163

anulação: “tirar nó contendo o valor N da árvore A”


Algoritmo:
1) pesquisar qual o nó a anular
2) anular nó de modo a que a árvore permaneça de pesquisa

Solução: Substituir valor a anular pelo valor guardado no nó mais


à direita da sua subárvore esquerda (ou no nó mais à esquerda da
subárvore direita)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 164

Exemplo: Anular nó raiz da seguinte árvore de inteiros

35

8 70

20

14

20

8 70

14

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 165

ESPECIFICAÇÃO ABSTRACTA

Utilizando as operações auxiliares para a operação retira:


 nódir : dada uma árvore indica, caso esta não esteja vazia, qual
o elemento que está guardado no seu nó mais à direita;
 retiradir : dada uma árvore A retira desta, caso esta não esteja
vazia, o seu nó mais à direita, obtendo-se uma árvore de pesquisa no
caso de A também o ser.

Utilizando as operações auxiliares para a operação epesquisa:


 maior, menor : dado um elemento e uma árvore indica se esse
elemento é ≥ (respectivamente <) que todos os elementos dessa
árvore.

especificação árvores binárias pesquisa[T] =


arvores binárias[T] 
géneros
Arvbp  Arvb
operações
acessórias
epesquisa: Arvb  boolean;
existe: Arvbp T  boolean;
insere: Arvbp T  Arvbp;
retira: Arvbp T  Arvbp
auxiliares
maior: T Arvb  boolean;
menor: T Arvb  boolean;
nodir: Arvb  T;
retiradir: Arvb  Arvb

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 166

axiomas
( A,A1,A2Arvb) ( O,O1,O2T)

epesquisa(A) = vazia(A) ||
(epesquisa(arvesq(A)) && epesquisa(arvdir(A)) &&
maior(raiz(A),arvesq(A)) && menor(raiz(A),arvdir(A))
);

maior(O,A) = vazia(A) ||
(O >=T raiz(A) && maior(O,arvesq (A)) &&
maior(O,arvdir(A))
);

menor(O,A) = vazia(A) ||
(O <T raiz(A) && menor(O,arvesq (A)) &&
menor(O,arvdir(A))
);

existe(nova,O) = false;
existe(cons(A1,O1,A2),O2) = se O1==T O2
então true
senão ( se O2 <T O1 então existe(A1,O2)
senão existe(A2,O2)
);

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 167

insere(nova,O) = cons(nova,O,nova);
insere(cons(A1,O1,A2),O2) = se O2 <=T O1
então cons(insere(A1,O2),O1,A2)
senão cons(A1, O1, insere(A2,O2));

nodir(cons(A,O,nova)) = O;
nodir(cons(A1,O1,cons(A2,O2,A3)))=nodir(cons(A2,O2,A3));

retiradir(cons(A,O,nova)) = A;
retiradir(cons(A1,O1,cons(A2,O2,A3))) =
cons(A1,O1,retiradir(cons(A2,O2,A3));

retira(nova,O)=nova;
retira(cons(A1,O1,A2),O2) = se O2 !=T O1
então ( se O2<T O1 então cons(retira(A1,O2),O1,A2)
senão cons(A1,O1,retira(A2,O2))
)
senão ( se vazia(A1) então A2
senão se vazia(A2) então A1
senão cons(retiradir(A1),nodir(A1),A2)
)

pré-condições
( AArvb)
nodir(A) requer !vazia(A);
retiradir(A) requer !vazia(A)
invariantes
( AArvbp)
epesquisa(A)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 168

IMPLEMENTAÇÃO 1 (continuação) – implementação


respeitando as assinaturas das operações

A mesma implementação discutida anteriormente para o género e


para as operações nova, cons, arvesq, arvdir, raiz e vazia!

protected static boolean maior(Comparable n, Arvb a){


if(a==null) return true;
else return n.compareTo(a.raiz)>=0 &&
maior(n, a.arvesq) && maior(n, a.arvdir);
}

protected static boolean menor(Comparable n, Arvb a){


if(a==null) return true;
else return n.compareTo(a.raiz)<0 &&
menor(n, a.arvesq) && menor(n, a.arvdir);
}

public static boolean epesquisa(Arvb a){


if(a==null) return true;
else return epesquisa(a.arvesq) &&
epesquisa(a.arvdir) &&
maior(a.raiz, a.arvesq) &&
menor(a.raiz, a.arvdir);
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 169

Os axiomas propostos para a operação existe:

( A1,A2Arvb) ( O,O1,O2T)
existe(nova,O) = false;
existe(cons(A1,O1,A2),O2) = se O1==T O2 então true
senão ( se O2 <T O1 então existe(A1,O2)
senão existe(A2,O2) )

podem ser resumidos no seguinte axioma:

( AArvb) ( OT)
existe(A,O) = se vazia(A) então false
senão ( se raiz(A) ==T O então true
senão ( se O <T raiz(A) então existe(arvesq(A),O)
senão existe(arvdir(A),O) ) )

portanto:

public static boolean existe(Arvb a, Comparable n){


if(vazia(a)) return false;
else if(n.equals(raiz(a))) return true;
else if(n.compareTo(raiz(a))<0) return existe(arvesq(a), n);
else return existe(arvdir(a), n);
}

ou seja:

public static boolean existe(Arvb a, Comparable n){


if(a==null) return false;
else if(n.equals(a.raiz)) return true;
else if(n.compareTo(a.raiz)<0) return existe(a.arvesq, n);
else return existe(a.arvdir, n);
}
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Árvores - 170

Analogamente, os axiomas propostos para a operação insere


sugerem a seguinte codificação:

public static Arvb insere(Arvb a, Comparable n){


if(vazia(a)) return cons(nova(),n,nova());
else{
if(n.compareTo(raiz(a))<=0)
return cons(insere(arvesq(a),n),raiz(a),arvdir(a));
else return cons(arvesq(a),raiz(a),insere(arvdir(a),n));
}
}

Quais as desvantagens da implementação anterior?

public static Arvb insere(Arvb a, Comparable n){


if(a==null) return cons(null,n,null);
else{
if(n.compareTo(a.raiz)<=0)
a.arvesq = insere(a.arvesq, n);
else a.arvdir = insere(a.arvdir, n);
return a;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 171

protected static Comparable nodir(Arvb a){


if(a==null){
System.out.println("Erro - nodir: arvore vazia!");
return ITEM_NOT_FOUND;
}
else if(a.arvdir!=null) return nodir(a.arvdir);
else return a.raiz;
}

protected static Arvb retiradir(Arvb a){


if(a==null){
System.out.println("Erro - retiradir: arvore vazia!");
return null;
}
else if(a.arvdir==null) return a.arvesq;
else{
a.arvdir = retiradir(a.arvdir);
return a;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 172

public static Arvb retira(Arvb a, Comparable n){


if(a!=null){
if(!n.equals(a.raiz)){
if(n.compareTo(a.raiz)<0)
a.arvesq = retira(a.arvesq, n);
else a.arvdir = retira(a.arvdir, n);
}
else{
if(a.arvesq == null) a = a.arvdir;
else if(a.arvdir == null) a = a.arvesq;
else{
a.raiz= nodir(a.arvesq);
a.arvesq = retiradir(a.arvesq);
}
}
}
return a;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 173

IMPLEMENTAÇÃO 2 – implementação orientada por objectos

// Arvbp.java

public interface Arvbp{


public void nova();
public boolean vazia();
public boolean existe(Comparable n);
public void insere(Comparable n);
public void retira(Comparable n);
public boolean epesquisa();
}

PS: Excluem-se nesta implementação árvores cujo tipo base é um


tipo primitivo Java (int, long, char, boolean, float, double, ...)!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 174

// ArvbpLigada.java

import java.io.*;

public class ArvbpLigada implements Arvbp{

protected class NO{


Comparable raiz;
NO arvesq;
NO arvdir;
}

protected NO root;

protected Comparable ITEM_NOT_FOUND;

public void nova(){


root =nova_aux();
}

public boolean vazia(){


return vazia(root);
}

public boolean existe(Comparable n){


return existe(root, n);
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 175

public void insere(Comparable n){


root = insere(root, n);
}

public void retira(Comparable n){


root = retira(root, n);
}

public boolean epesquisa(){


return epesquisa(root);
}

protected NO nova_aux(){
return null;
}

protected NO cons(NO a1, Comparable n, NO a2){


NO aux = new NO();
aux.raiz= n;
aux.arvesq = a1;
aux.arvdir = a2;
return aux;
}

protected Comparable raiz(NO a){


if(a!=null) return a.raiz;
else{
System.out.println("Erro - raiz: arvore vazia!");
return ITEM_NOT_FOUND;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 176

protected NO arvesq(NO a){


if(a!=null) return a.arvesq;
else{
System.out.println("Erro - arvesq: arvore vazia!");
return null;
}
}

protected NO arvdir(NO a){


if(a!=null) return a.arvdir;
else{
System.out.println("Erro - arvdir: arvore vazia!");
return null;
}
}

protected boolean vazia(NO a){


return a==null;
}

protected boolean existe(NO a, Comparable n){


if(a==null) return false;
else if(n.equals(a.raiz)) return true;
else if(n.compareTo(a.raiz)<0)
return existe(a.arvesq, n);
else return existe(a.arvdir, n);
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 177

protected NO insere(NO a, Comparable n){


if(a==null) return cons(null,n,null);
else{ if(n.compareTo(a.raiz)<=0)
a.arvesq = insere(a.arvesq, n);
else a.arvdir = insere(a.arvdir, n);
return a;
}
}

protected Comparable nodir(NO a){


if(a==null){
System.out.println("Erro - nodir: arvore vazia!");
return ITEM_NOT_FOUND;
}
else if(a.arvdir!=null) return nodir(a.arvdir);
else return a.raiz;
}

protected NO retiradir(NO a){


if(a==null){
System.out.println("Erro - retiradir: arvore vazia!");
return null;
}
else if(a.arvdir==null) return a.arvesq;
else{
a.arvdir = retiradir(a.arvdir);
return a;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 178

protected NO retira(NO a, Comparable n){


if(a!=null){
if(!n.equals(a.raiz)){
if(n.compareTo(a.raiz)<0)
a.arvesq = retira(a.arvesq, n);
else a.arvdir = retira(a.arvdir, n);
}
else{
if(a.arvesq == null) a = a.arvdir;
else if(a.arvdir == null) a = a.arvesq;
else{
a.raiz= nodir(a.arvesq);
a.arvesq = retiradir(a.arvesq);
}
}
}
return a;
}

protected boolean maior(Comparable n, NO a){


if(a==null) return true;
else return n.compareTo(a.raiz)>=0 &&
maior(n, a.arvesq) && maior(n, a.arvdir);
}

protected boolean menor(Comparable n, NO a){


if(a==null) return true;
else return n.compareTo(a.raiz)<0 &&
menor(n, a.arvesq) && menor(n, a.arvdir);
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 179

protected boolean epesquisa(NO a){


if(a==null) return true;
else return epesquisa(a.arvesq) &&
epesquisa(a.arvdir) &&
maior(a.raiz, a.arvesq) &&
menor(a.raiz, a.arvdir);
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 180

ÁRVORES BINÁRIAS DE PESQUISA EQUILIBRADAS


OU
ÁRVORES AVL (Adelson-Velskii and Landis)

Árvores binárias de pesquisa equilibradas ou árvores AVL (de


elementos em T munido de uma ordem parcial): árvores binárias de
pesquisa vazias ou em que as suas árvores esquerda e direita são
também árvores AVL e em que as suas alturas não diferem em mais
de uma unidade.

inserção em árvores AVL: “inserir o valor O na árvore A”


Algoritmo:

1) pesquisar posição onde o novo nó deve ser inserido


2) criar o novo nó e inseri-lo na árvore
(nota: o novo nó ficará sempre a ser uma folha !)
3) caso a árvore fique desequilibrada então torná-la equilibrada

Solução: Após uma inserção, apenas as árvores que estão na


trajectória efectuada desde a raiz até ao ponto de inserção podem ter
o seu equilíbrio afectado. Portanto, à medida que se segue o
caminho inverso (i.e. do ponto de inserção até à raiz) é necessário
testar a condição de equilíbrio de cada árvore nesse caminho e caso
esta não esteja equilibrada (caso em que a profundidade das duas
sub-árvores diferem de 2 unidades) deve proceder-se ao seu
reequilíbrio. Como?

Observação: A anulação é mais complicada pelo que não é aqui


abordada!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 181

O desiquilíbrio pode ocorrer em 4 casos após uma inserção na


árvore equilibrada A:

1. inserção na sub-árvore esquerda da sub-árvore esquerda da


árvore A;

O2

O1
A3 nível n-2

A2 nível n-1
A1
nível n

2. inserção na sub-árvore direita da sub-árvore esquerda da árvore


A;

O2

O1
A3 nível n-2

A1 nível n-1
A2
nível n

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 182

3. inserção na sub-árvore direita da sub-árvore direita da árvore


A;

O1

O2
A1 nível n-2

A2 nível n-1
A3
nível n

4. inserção na sub-árvore esquerda da sub-árvore direita da árvore


A;

O1

O2
A1 nível n-2

A3 nível n-1
A2
nível n

Como resolver o desiquilíbrio em cada um destes casos ?

Para simplificar considera-se nas soluções seguintes que as árvores


não têm elementos duplicados!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 183

Caso 1: rotação esquerda

O2 O1

O1 O2
A3

A1
A2 A2 A3

A1

rotacaoesq(cons(cons(A1,O1,A2),O2,A3)) =
cons(A1,O1, cons(A2,O2,A3))

Caso 3: rotação direita (simétrico do caso 1)

O1 O2
O1
A1 O2

A3
A2 A1 A2

A3

rotacaodir(cons(A1,O1,cons(A2,O2,A3))) =
cons(cons(A1,O1,A2),O2,A3)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 184

Caso 2: dupla rotação esquerda

Apenas uma rotação esquerda não resolve este caso ...

O2 O1

O1 O2
A3 A1

A1 A3

A2 A2

Solução:
1) rotação direita de cons(A1,O1,A2);
2) rotação esquerda de cons(rotacaodir(cons(A1,O1,A2)),O2,A3).

O3 O3 O2

O1 O2 O1 O3
A4 A4

O2 O1
A1 A3 A1 A2 A3 A4

A2 A3 A1 A2

duplarotacaoesq(cons(cons(A1,O1,cons(A2,O2,A3)),O3,A4)) =
rotacaoesq(cons(rotacaodir(cons(A1,O1,cons(A2,O2,A3))),O3,A4))

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 185

Caso 4: dupla rotação direita (simétrico do caso 2)

Apenas uma rotação direita não resolve este caso ...

O1 O2

A1 O2 O1
A3

A3 A1

A2 A2

Solução:
3) rotação esquerda de cons(A2,O2,A3);
4) rotação direita de cons(A1,O1,rotacaoesq(cons(A2,O2,A3))).

O1 O1 O2

O3 O2 O1 O3
A1 A1

O2 O3
A4 A2 A1 A2 A3 A4

A2 A3 A3 A4

duplarotacaodir(cons(A1,O1,cons(cons(A2,O2,A3),O3,A4))) =
rotacaodir(cons(A1,O1,rotacaoesq(cons(cons(A2,O2,A3),O3,A4))))

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 186

insere2(nova,O) = cons(nova,O,nova);
insere2(cons(A1,O1,A2),O2) =
se O2 <=T O1
então (
se prof(insere2(A1,O2))-prof(A2)==2
então (
se O2 <=T raiz(insere2(A1,O2))
então rotacaoesq(cons(insere2(A1,O2),O1,A2))
senão duplarotacaoesq(cons(insere2(A1,O2),O1,A2))
)
senão cons(insere2(A1,O2),O1,A2)
)
senão (
se prof(insere2(A2,O2))-prof(A1)==2
então (
se O2 >T raiz(insere2(A2,O2))
então rotacaodir(cons(A1,O1,insere2(A2,O2)))
senão duplarotacaodir(cons(A1,O1,insere2(A2,O2)))
)
senão cons(A1, O1, insere2(A2,O2))
);

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 187

ESPECIFICAÇÃO ABSTRACTA

especificação arvores AVL[T] =


arvores binárias pesquisa[T] escondendo insere 
géneros
Arvavl  Arvbp
operações
acessórias
equilibrada: Arvb  boolean;
insere2: Arvavl T  Arvavl
auxiliares
prof: Arvb  int;
rotacaoesq: Arvb  Arvb;
rotacaodir: Arvb  Arvb;
duplarotacaoesq: Arvb  Arvb;
duplarotacaodir: Arvb  Arvb
axiomas
( A,A1,A2,A3,A4Arvb) ( O,O1,O2,O3T)

equilibrada(A) = vazia(A) ||
(equilibrada(arvesq(A)) && equilibrada(arvdir(A)) &&
abs(prof(arvesq(A)) - prof(arvdir(A))) <=1
);

prof(A) = se vazia(A) então 0


senão 1 + max(prof(arvesq(A)),prof(arvdir(A)));

...

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 188

pré-condições
( AArvb)
rotacaoesq(A) requer !vazia(A) && !vazia(arvesq(A));
rotacaodir(A) requer !vazia(A) && !vazia(arvdir(A));
duplarotacaoesq(A) requer !vazia(A) && !vazia(arvesq(A))
&& !vazia(arvdir(arvesq(A)));
duplarotacaodir(A) requer !vazia(A) && !vazia(arvdir(A))
&& !vazia(arvesq(arvdir(A)))
invariantes
( AArvavl)
equilibrada(A)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 189

IMPLEMENTAÇÃO

// Arvbavl.java

public interface Arvbavl extends Arvbp{


public boolean equilibrada();
}

// ArvbavlLigada.java

import java.io.*;

public class ArvbavlLigada extends ArvbpLigada


implements Arvbavl{

public void insere(Comparable n){


root = insere(root, n);
}

public boolean equilibrada(){


return equilibrada(root);
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 190

protected NO rotacaoesq(NO a){


if(a==null) {
System.out.println("Erro - rotacaoesq: arvore vazia!");
return null;
}
else if(a.arvesq==null){
System.out.println("Erro - rotacaoesq:
sub-arvore esquerda vazia!");
return null;
}
else{
NO aux = a.arvesq;
a.arvesq = aux.arvdir;
aux.arvdir = a;
return aux;
}
}

protected NO rotacaodir(NO a){

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 191

protected NO duplarotacaoesq(NO a){


if(a==null) {
System.out.println("Erro - duplarotacaoesq: arvore vazia!");
return null;
}
else if(a.arvesq==null){
System.out.println("Erro - duplarotacaoesq:
sub-arvore esquerda vazia!");
return null;
}
else if(a.arvesq.arvdir==null){
System.out.println("Erro - duplarotacaoesq:
sub-arvore direita da esquerda vazia!");
return null;
}
else{
a.arvesq = rotacaodir(a.arvesq);
return rotacaoesq(a);
}
}

protected NO duplarotacaodir(NO a){

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 192

protected NO insere(NO a, Comparable n){


if(a==null) return cons(null,n,null);
else{ if(n.compareTo(a.raiz)<=0){
a.arvesq = insere(a.arvesq, n);
if(prof(a.arvesq)-prof(a.arvdir) == 2){
if(n.compareTo(a.arvesq.raiz)<=0)
a = rotacaoesq(a);
else a = duplarotacaoesq(a);
}
}
else {
a.arvdir = insere(a.arvdir, n);
if(prof(a.arvdir)-prof(a.arvesq) == 2){
if(n.compareTo(a.arvdir.raiz)>0)
a = rotacaodir(a);
else a = duplarotacaodir(a);
}
}
return a;
}
}

protected boolean equilibrada(NO a){


if(a==null) return true;
else return equilibrada(a.arvesq) && equilibrada(a.arvdir) &&
abs(prof(a.arvesq) - prof(a.arvdir))<=1;
}

protected int prof(NO a){


if(a == null) return 0;
else return 1 + max(prof(a.arvesq), prof(a.arvdir));
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 193

Exercícios
1. No contexto da especificação árvores binárias pesquisa[int]
calcule o valor dos seguintes termos:

a) nodir(cons(cons(nova,7,nova),9,cons(nova,16,nova)))

b) retiradir(cons(cons(nova,7,nova),9,cons(nova,16,nova)))

c) insere(cons(cons(nova,7,nova),9,cons(nova,16,nova)),8)

2. Implemente os seguintes métodos, que dada uma árvore:

a) print_infix – escreve no visor os seus elementos


executando uma travessia infixa;

b) print_sufix – escreve no visor os seus elementos


executando uma travessia sufixa;

c) print_prefix – escreve no visor os seus elementos


executando uma travessia prefixa;

d) print_term – escreve no visor o termo construtor que lhe


corresponde.

3. Enriqueça a especificação árvores binárias pesquisa[T] com a


operação item (que dada uma árvore binária de pesquisa e um
elemento indica qual o elemento nessa árvore igual ao elemento
fornecido).

4. Implemente o tipo de dados abstracto referido no exercício


anterior.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Árvores - 194

5. Implemente a classe Pessoa e o seguinte interface correspondente


a uma árvore binária de pessoas identificadas pelo seu número de
bilhete de identidade.

public interface ArvorePessoas{


void nova(); // inicializa a arvore

void insere(Pessoa p); // insere uma pessoa na arvore

boolean existe(long bi); // confirma se existe uma pessoa com


// o no. bi fornecido
Pessoa item(long bi); // devolve a pessoa com o no. bi
// fornecido
void retira(long bi); // elimina a pessoa com o no. bi
// fornecido
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 195

FILAS PRIORITÁRIAS

Filas Prioritárias: Estruturas de armazenamento de elementos em T


munido de uma ordem parcial, com disciplina de acesso aos
menores elementos.

ESPECIFICAÇÃO ABSTRACTA

especificação filas prioritárias[T] =


boolean 
géneros
FilaPri
operações
construtoras
inicial:  FilaPri;
coloca: FilaPri T  FilaPri
acessórias
menor: FilaPri  T;
retiramenor: FilaPri  FilaPri;
vazia: FilaPri  boolean
axiomas
( FFilaPri) ( N,N1,N2T)

menor(coloca(inicial,N)) = N ;
menor(coloca(coloca(F,N1),N2)) =
se N1 <= T N2 então menor(coloca(F,N1))
senão menor(coloca(F,N2));

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 196

retiramenor(coloca(inicial,N)) = inicial;
retiramenor(coloca (coloca (F,N1),N2)) =
se N1 <= T N2
então coloca(retiramenor(coloca(F,N1)),N2)
senão coloca(retiramenor(coloca(F,N2)),N1);

vazia(inicial) = true ;
vazia(coloca(F,N)) = false
pré-condições
( FFilaPri)
menor(F) requer !vazia(F);
retiramenor(F) requer !vazia(F)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 197

IMPLEMENTAÇÃO

A implementação mais eficiente de filas prioritárias é a obtida por


HEAPS BINÁRIOS!

Heap Binário: árvore binária completa (de elementos em T munido


de uma ordem parcial) em que o valor guardado em cada nó é < do
que o valor guardado em qualquer dos seus nós descendentes.

Exemplo:
13

21 16

24 31 19 68

65 26 32

 árvores binárias completas são facilmente implementadas em


matrizes!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 198

IMPLEMENTAÇÃO ESTÁTICA (MATRICIAL)

A árvore binária completa

13

21 16

24 31 19 68

65 26 32

é representada por:

13 21 16 24 31 19 68 65 26 32 …
0 1 2 3 4 5 6 7 8 9 10 11

Comentários:

c) Cada elemento na posição i tem como descendentes os elementos


nas posições 2i e 2i+1 (desde que <= nº elementos) e como
ascendente i/2 (desde que > 0).

d) Número máximo de nós numa árvore binária completa de


h
profundidade h : 2 -1.
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 199

inserção: “inserir o valor O no heap binário”


Algoritmo:

1) criar um novo nó (sem elemento) de modo a que se obtenha


uma árvore binária completa.

2) coar para cima “percolate up”: caso o nó ascendente do nó


desocupado > O, faz-se deslizar o seu elemento para o nó
desocupado e repete-se o processo em direcção à raiz; o
processo pára quando a raiz estiver desocupada ou o nó
ascendente <= O. Nesse caso coloca-se O no nó desocupado.

anulação: “retirar o menor elemento do heap binário”


Algoritmo:
1) o nó raiz fica sem elemento, elimina-se o último nó de modo a
que se obtenha uma árvore binária completa e reserva-se o seu
elemento em X.

2) coar para baixo “percolate down”: caso X > que algum dos
elementos dos nós descendentes do nó desocupado, faz-se
deslizar para o nó desocupado o menor elemento dos seus nós
descendentes e repete-se o processo em direcção às folhas; o
processo pára quando uma das folhas ficar desocupada ou X
<= os elementos dos nós descendentes do nó desocupado.
Coloca-se X na folha desocupada.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 200

Exemplo: inserção de 14
13

21 16

24 31 19 68

65 26 32

13

14 16

24 21 19 68

65 26 32 31

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 201

Exemplo: anulação do menor elemento

14 16

20 21 19 68

65 26 32 31

14

20 16

26 21 19 68

65 31 32

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Filas Prioritárias - 202

Exercício

Implemente o tipo de dados abstracto Filas Prioritárias utilizando


Heaps Binários.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 203

TABELAS

Tabelas: Estruturas de armazenamento de dados caracterizados por


um determinado campo chave.

ESPECIFICAÇÃO ABSTRACTA

especificação tabelas[Itemkey, Key] =


Itemkey inclui key: Itemkey  Key
boolean 
géneros
Tabela
operações
construtoras
nova:  Tabela;
insere: Tabela Itemkey  Tabela
acessórias
item: Tabela Key  Itemkey;
retira: Tabela Key  Tabela
auxiliares
existe: Tabela Key  boolean
axiomas
( TTabela) ( I,I1,I2Itemkey) ( KKey)

insere(insere(T,I1),I2) = se !existe(insere(T,I1), key(I2))


então insere(insere(T,I2),I1);

item(insere(T,I),K) = se existe(insere(T,I),K)
então ( se K ==Key key(I)
então I
senão item(T,K)
);
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Tabelas - 204

retira(nova,K) = nova;
retira(insere(T,I),K) = se K ==Key key(I)
então T
senão insere(retira(T,K),I);

existe(nova,K) = false;
existe(insere(T,I),K) = se K ==Key key(I)
então true
senão existe(T,K)

pré-condições
( TTabela) ( IItemkey) ( KKey)
insere(T,I) requer !existe(T, key(I));
item(T,K) requer existe(T,K)
fim

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 205

Problema: Dada uma colecção de dados caracterizados por um


determinado campo chave, como os organizar/armazenar de modo a
tornar esses dados eficientemente pesquisáveis (via chave) ?

PS: Nos métodos de organização dos dados anteriores a posição de


um elemento depende de elementos anteriormente armazenados.

Solução: Para assegurar uma pesquisa eficiente é necessário adoptar


uma organização de dados que permita a colocação dos elementos
em posições directamente dependentes das suas chaves, de modo
“independente” (em princípio) uns dos outros !

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 206

Exemplo motivador: Pretende-se armazenar e pesquisar (via


número mecanográfico) informação anual sobre os alunos que
frequentam uma dada disciplina. Sabe-se que anualmente o número
de alunos da disciplina não excede os 200 e que estão registados
30000 alunos. Como estruturar essa informação de modo a tornar a
pesquisa o mais rápida possível ?

Representando os alunos por:

class Aluno {
long chave; ...
}

A estrutura que torna a pesquisa mais rápida é: Inconvenientes ?

class Componente {
Aluno item;
boolean vazio;
}
class TabelaDisciplina{
Componente[] ve = new Componente[30001];
}

Uma escolha mais natural seria:

class TabelaDisciplina{
Componente[] ve = new Componente[200];
}

Mas, então, como guardar os alunos na tabela ?

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 207

TABELAS ESPARSAS E FUNÇÕES DE DISPERSÃO

Solução: Encontrar uma função apropriada H que aplique o


conjunto de todas as possíveis chaves (K) no conjunto dos índices
da matriz (E):
H: K  E

H transforma as chaves em índices do vector ("Key


transformation") ou H dispersa as chaves pelas componentes da
matriz ("Função de dispersão" - "Hashing").

(No exemplo motivador: H: [1, 30000]  [0, 199])

1º problema: Que função H: K  E escolher ?

2º problema: Como resolver as colisões ?

Em geral #K > #E, portanto H não pode ser injectiva !

Há uma colisão sempre que se pretende guardar dois ou mais


elementos cujas chaves são aplicadas por H na mesma posição da
matriz.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 208

Que função de dispersão escolher ?

Características desejáveis:
- dispersão (o mais possível) uniforme das chaves;
- fácil de calcular (composta de poucas operações aritméticas
básicas).

A utilização de uma função H que disperse as chaves


uniformemente pela matriz é vantajosa, mas não evita colisões !

Há vários métodos para construir a função de dispersão ! Por


exemplo (sendo N a dimensão da matriz):

- Método da divisão (para chaves inteiras):


H(k)=k%N
- Método de Horner (para chaves strings):
H(k)=(a0*c0+a1*c1+a2*c2+ ... )%N
(a - constante inteira; ci - código ASCII do i-ésimo caracter)

Nestes exemplos, a capacidade de dispersão uniforme das chaves


depende das propriedades do conjunto K e da dimensão N escolhida
para a matriz ! ...

Métodos para a resolução de colisões:

1. Método do encadeamento directo em área de “overflow”

2. Método de endereçamento aberto (pesquisa prefixada)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 209

Método do encadeamento directo em área de “overflow”

Guardar, por esta ordem, os elementos e1, e3, e5, e2, e6, e4, com
chaves k1, k3, k5, k2, k6, k4, utilizando a função de dispersão
H(k1)=0, H(k2)=3, H(k3)=3, H(k4)=4, H(k5)=4, H(k6)=7.

0 1 2 3 4 5 6 7 8 9

e1 e3 e5 e6

e2 e4

Vantagens: algoritmos de pesquisa, inserção e anulação simples;


pesquisa rápida se houver poucas colisões; não existem restrições
quanto ao número de elementos a armazenar.

Desvantagens: Exige espaço adicional de memória.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 210

IMPLEMENTAÇÃO

// Itemkey.java

public interface Itemkey{


public Object key();
}

// Tabela.java

public interface Tabela{


void nova();
boolean existe(Object key);
void insere(Itemkey x);
void retira(Object key);
Itemkey item(Object key);
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 211

IMPLEMENTAÇÃO – MÉTODO DE ENCADEAMENTO


DIRECTO EM ÁREA DE “OVERFLOW”

// TabelaEncadeamentoDirecto.java

public class TabelaEncadeamentoDirecto implements Tabela{

protected static int tablesize = 10007; 

protected Lista[] ve = new Lista[tablesize];

protected Itemkey ITEM_NOT_FOUND;

public void nova(){


for(int j=0; j<tablesize; j++){
ve[j]=new ListaLigada();
ve[j].nova();
}
}

public boolean existe(Object key){


boolean encontrei = false;
int indlista = hash(key, tablesize),
comp = ve[indlista].comprimento(),
pos=0;
while(pos<comp && !encontrei)
encontrei = key.equals(
((Itemkey)ve[indlista].elementopos(++pos) ).key());
return encontrei;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 212

public void insere(Itemkey x){


if(!existe(x.key())){
int indlista = hash(x.key(),tablesize);
ve[indlista].acr(x);
}
else System.out.println("Aviso - insere:
item com a mesma chave ja existente na tabela.");
}

public void retira(Object key){


if(existe(key)){
boolean encontrei = false;
int indlista = hash(key,tablesize), pos=0;
while(!encontrei)
encontrei = key.equals(
((Itemkey)ve[indlista].elementopos(++pos)).key());
ve[indlista].retirapos(pos);
}
else System.out.println("Aviso - retira:
nao existe item na tabela com a chave fornecida.");
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 213

public Itemkey item(Object key){


if(existe(key)){
boolean encontrei = false;
int indlista = hash(key,tablesize), pos=0;
while(!encontrei)
encontrei = key.equals(
((Itemkey)ve[indlista].elementopos(++pos)).key());
return (Itemkey)ve[indlista].elementopos(pos);
}
else{
System.out.println("Erro - item:
nao existe item na tabela com a chave fornecida.");
return ITEM_NOT_FOUND;
}
}

protected static int hash(Object key, int tablesize){


if(key instanceof Long)
return ((Long)key).intValue()%tablesize;
if(key instanceof String){
int hashval=0;
for(int i=0;i<((String)key).length();i++)
hashval=37*hashval+((String)key).charAt(i);
hashval%=tablesize;
if(hashval<0) hashval+=tablesize;
return hashval;
}
System.out.println("Aviso - hash:
funcao nao definida para o tipo de chaves utilizado.");
return 0;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 214

Método de endereçamento aberto (pesquisa prefixada)

Solução alternativa dispensando a área de “overflow”

Inserção:

- o elemento (de chave K) é colocado na posição H(k);


- se esta posição já estiver ocupada então o elemento é colocado na
primeira posição vazia, de acordo com uma sequência de
pesquisa prefixada.

Sequência de pesquisa prefixada:

h0 = H(k), h1, h2, ... , com h0,h1,h2[0,N-1]

hi – posição a pesquisar após a i-ésima colisão.

Observações:

- Se a matriz já estiver cheia o elemento não pode ser inserido na


matriz. Nesse caso, ou não se guarda o elemento (solução aqui
considerada), ou se considera soluções mistas em que esses
elementos são encadeados numa área de “overflow”.
- Mesmo sem a matriz estar cheia, pode ser impossível acrescentar
o elemento à tabela em virtude de a sequência de pesquisa
prefixada não cobrir todas as posições da matriz (neste caso é
necessário definir um critério que permita terminar a pesquisa em
tempo finito: por exemplo, após N comparações).

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 215

Conforme a particular sequência de pesquisa prefixada escolhida,


assim se nomeia o particular método de dispersão por
endereçamento aberto. Por exemplo:

Endereçamento linear

Utiliza a seguinte sequência de pesquisa linear:

h0 = H(k)
hi = (h0+i)%N (i>0)

Na pesquisa linear, a próxima posição a pesquisar pode ser obtida


directamente a partir da última posição pesquisada, como se segue:

h0 = H(k)
hi+1 = (hi+1)%N (i≥0)

i.e.

h0 = H(k)
hi+1 = seg(hi) (i≥0)

onde

seg(h)=(h+1)%N (seg:[0,N-1]  [0,N-1])

Neste método, se o elemento a inserir não estiver na tabela, ou a


matriz está cheia ou é encontrada uma posição vazia após a análise
de, no máximo, N posições.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 216

Método de endereçamento linear

Guardar, por esta ordem, os elementos e1, e3, e5, e2, e6, e4, com
chaves k1, k3, k5, k2, k6, k4, utilizando a função de dispersão
H(k1)=0, H(k2)=3, H(k3)=3, H(k4)=4, H(k5)=4, H(k6)=7.

0 1 2 3 4 5 6 7 8 9
e1 e3 e5 e2 e4 e6 item
ve
false true true false false false false false true true vazio

Eliminar elemento cuja chave é k3:

- e1,e5 e e6 não devem sair das posições em que se encontram !


- e2 deve ir para a posição 3 ! porquê ?
- e4 deve ir para a posição 5 ! porquê ?

0 1 2 3 4 5 6 7 8 9
e1 e2 e5 e4 e6 item
ve
false true true false false false false false true true vazio

Um elemento x, na posição j, só deve ir para a posição h, do


elemento y que foi anulado, se o facto do elemento x estar na
posição j se devia ao elemento y estar na posição h!

 x não muda de posição se H(key(x)) = j !


 x não muda de posição se H(key(x)) está “entre” h e j !

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 217

IMPLEMENTAÇÃO – MÉTODO DE ENDEREÇAMENTO


ABERTO (COM SEQUÊNCIA DE PESQUISA PREFIXADA)

// TabelaEnderecamentoAberto.java

public class TabelaEnderecamentoAberto implements Tabela{

protected static int tablesize = 10007;

protected class Componente{


Itemkey item;
boolean vazio;
}

protected Componente[] ve = new Componente[tablesize];

protected Itemkey ITEM_NOT_FOUND;

public void nova(){


for(int j=0; j<tablesize; j++){
ve[j]=new Componente();
ve[j].vazio=true;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 218

public boolean existe(Object key){


boolean encontrei = false,
stop = false;
int h = hash(key, tablesize),
ntentativas = 0;
while(!stop && ntentativas!=tablesize){
if(ve[h].vazio) stop=true; // encontrada posicao livre
else if(key.equals(ve[h].item.key())){
// encontrado elemento
encontrei=true;
stop=true;
}
else{ // colisao
ntentativas++;
h=seg(h,tablesize);
}
}
return encontrei;
}

Observações:
- o elemento de chave k é procurado na posição H(k);
- se ve[H(k)].item.key()≠ k, o elemento é procurado na próxima
posição de acordo com a sequência de pesquisa prefixada;
- a pesquisa termina: ou i) porque é encontrado o elemento; ou ii)
porque se chega a uma posição vazia (caso em que o elemento
não se encontra na tabela); ou iii) porque se esgotaram as
posições a procurar;
- se encontrei=false e stop=false, então a tabela está cheia!
- se encontrei=false e stop=true, então h indica a primeira posição
vazia !

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 219

public void insere(Itemkey x){

// pesquisa
boolean encontrei = false,
stop = false;
int h = hash(x.key(), tablesize),
ntentativas = 0;
while(!stop && ntentativas!=tablesize){
if(ve[h].vazio) stop=true; // encontrada posicao livre
else if(x.key().equals(ve[h].item.key())){
// encontrado elemento
encontrei=true;
stop=true;
}
else{ // colisao
ntentativas++;
h=seg(h,tablesize);
}
}

// insercao
if(encontrei)
System.out.println("Aviso - insere: item com a mesma
chave ja existente na tabela.");
else if(!stop)
System.out.println("Erro - insere: a tabela esta cheia ou a
s.p.p. nao permite encontrar posicao vazia.");
else{
ve[h].item=x;
ve[h].vazio=false;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 220

public Itemkey item(Object key){

// pesquisa
boolean encontrei = false,
stop = false;
int h = hash(key, tablesize),
ntentativas = 0;
while(!stop && ntentativas!=tablesize){
if(ve[h].vazio) stop=true; // encontrada posicao livre
else if(key.equals(ve[h].item.key())){
// encontrado elemento
encontrei=true;
stop=true;
}
else{ // colisao
ntentativas++;
h=seg(h,tablesize);
}
}

// elemento pesquisado
if(encontrei) return ve[h].item;
else{
System.out.println( "Erro - item: nao existe item na tabela
com a chave fornecida.");
return ITEM_NOT_FOUND;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 221

public void retira(Object key){


// pesquisa
boolean encontrei = false, stop = false;
int h = hash(key, tablesize), ntentativas = 0;
while(!stop && ntentativas!=tablesize){
if(ve[h].vazio) stop=true; // encontrada posicao livre
else if(key.equals(ve[h].item.key())){
// encontrado elemento
encontrei=true;
stop=true;
}
else{ntentativas++; // colisao
h=seg(h,tablesize);
}
}
// anulacao
if(encontrei){
int j=seg(h,tablesize);
ve[h].vazio=true;
while(!ve[j].vazio){
int aux=hash(ve[j].item.key(),tablesize);
if(entre_seq(aux,h,j,tablesize)) // nao muda de posicao
j = seg(j,tablesize);
else{ // muda de posicao
ve[h].item = ve[j].item;
ve[h].vazio = false; ve[j].vazio = true;
h = j; j=seg(j,tablesize);
}
}
}
else System.out.println( "Aviso - retira: nao existe item na
tabela com a chave fornecida.");
}
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Tabelas - 222

protected static int seg(int ind, int tablesize){ // s.p.p. linear


return (ind+1)%tablesize;
}

protected static boolean entre_seq(int i, int li, int ls, int tablesize){
// s.p.p. linear
return (ls>li&&i>li&&i<=ls)||(li>ls&&(i>li||i<=ls));
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 223

Outras sequências de pesquisa prefixada

No endereçamento linear as colisões são agrupadas todas juntas


formando agrupamentos (“clustering”) que comprometem a
eficiência das operações de inserção, pesquisa e anulação.

Vários outros métodos foram propostos com o objectivo de


dispersar as colisões uniformemente pela matriz, tentando manter a
simplicidade do cálculo da sequência.

Sequência de pesquisa quadrática

h0 = H(k)
hi = (h0+i2)%N (i>0)

A pesquisa quadrática tem a vantagem de dispersar as colisões.


Garante que pelo menos metade da matriz é percorrida pela
sequência, se N for um número primo.

O cálculo pode ser simplificado (evitando o quadrado), utilizando a


seguintes relações de recorrência:

h0 = H(k); d0 = 1
hi+1 = (hi + di)%N; di+1 = di + 2 (i≥0)

Desvantagens: Em certas situações metade das componentes da


matriz não são aproveitadas (podem ocorrer casos em que a
sequência de pesquisa percorre sucessivamente posições ocupadas,
sendo incapaz de detectar uma das posições livres existentes para
inserir um elemento).

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 224

Breve análise do método da dispersão

Se o vector for carregado aleatoriamente e a função H dispersar as


diferentes chaves uniformemente pela matriz, são de esperar poucas
colisões, tornando a pesquisa extremamente rápida!

As condições anteriores podem não ser fáceis de preencher!

Se para além da pesquisa, inserção e anulação, se pretender


frequentemente outras operações sobre dados guardados, esta forma
de organização dos dados pode tornar-se desajustada ! (e.g. como
obter a lista dos elementos guardados ordenada pelas chaves ?)

É necessário efectuar uma boa estimativa do número esperado de


elementos a armazenar para se poder dimensionar convenientemente
uma tabela com poucas colisões e com boa taxa de ocupação (para
evitar desperdiçar memória) !

No caso de métodos “puros” por endereçamento aberto, a tabela


deve ser dimensionada um “pouco” acima do número de elementos
esperados ! (para evitar o custo computacional de redimensionar o
vector mais tarde – “rehashing”: operação que consiste na
transferência de todos os elementos da matriz antiga para a nova
matriz, por utilização de uma nova função de dispersão e sequência
de pesquisa prefixada adaptadas à nova dimensão da matriz)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Tabelas - 225

Exercícios

1. Implemente a classe Pessoa e o seguinte interface correspondente


a uma tabela de pessoas identificadas pelo seu número de bilhete de
identidade.

public interface TabelaPessoas{


void nova(); // inicializa a tabela

void insere(Pessoa p); // insere uma pessoa na tabela

boolean existe(long bi); // confirma se existe uma pessoa com


// o no. bi fornecido
Pessoa item(long bi); // devolve a pessoa com o no. bi
// fornecido
void retira(long bi); // elimina a pessoa com o no. bi
// fornecido
}

2. Volte a implementar o interface anterior utilizando uma tabela


esparsa implementada de acordo com o método de encadeamento
directo em área de “overflow” organizada com árvores binárias de
pesquisa.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 226

GRAFOS

Definição: Um grafo G é formado por dois conjuntos: V e A. V é o


conjunto dos vértices (ou nós): um conjunto finito, não vazio. A é o
conjunto das arestas (ou arcos): um conjunto de pares de vértices.

1) Num grafo não orientado cada aresta é representada por um par


não ordenado (representação da aresta que liga os vértices x e y:
(x,y); (x,y) = (y,x) );
2) Num grafo orientado cada aresta é representada por um par
ordenado (representação da aresta que liga os vértices x e y:
<x,y>; <x,y> ≠ <y,x>.

Notação:

- G = (V,A);
- V(G): conjunto dos vértices do grafo G;
- A(G): conjunto dos arestas do grafo G;
- lacete: aresta ligando um vértice a si próprio;
- seta: aresta de um grafo orientado;
- “a seta <x,y> dirige-se de x (origem, ou vértice inicial) para y
(destino, ou vértice terminal)”

Aplicações multiplas: Análise de circuitos eléctricos, descoberta do


caminho mais curto, análise de planeamento de projectos, genética,
linguística, ciências sociais, etc.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 227

Exemplos de grafos:

1 a

2 3 5 b

4 6 c

G1 G2

V(G1)={1, 2, 3, 4, 5, 6}
A(G1)={(1,2), (1,3), (1,4), (2,3), (2,4), (3,4), (3,6), (5,5)}

V(G2)={a, b, c}
A(G1)={<a,b>, <b,a>, <b,c>}

Generalização da noção de grafo:

- um multigrafo não orientado (resp. orientado) é um “grafo”


em que pode existir mais do que uma aresta (resp. seta com o
mesmo “sentido”) ligando o mesmo par de vértices.
- Um s-grafo (s≥1) é um multigrafo em que não podem existir
mais do que s arestas (resp. setas com o mesmo sentido)
ligando o mesmo par de vértices. (um grafo é um s-grafo com
s=1)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 228

Notas, resultados, definições e conceitos:

1) Seja <b,c> uma aresta de um grafo orientado. Diz-se que <b,c> é


uma aresta divergente de b e aresta convergente em c.

2) Num grafo não orientado G:


- dois vértices b e c dizem-se adjacentes sse (b,c)A(G);
- grau de um vértice = nº. de vértices adjacentes que possui.

3) Num grafo orientado G:


- um vértice c é sucessor de um vértice b, e b é um antecessor
de c, sse <b,c>A(G) (diz-se também que b e c são
adjacentes);
- grau externo de um vértice = nº. de sucessores que possui;
- grau interno de um vértice = nº. de antecessores que possui.

4) Nº. máximo de arestas num grafo não orientado com n vértices:

C(n,2) = n (n-1) / 2

PS: C(n,p) = combinações de n tomados p a p = n! / (n-p)! p!

Um grafo não orientado diz-se completo sse existe uma aresta


que une quaisquer dois vértices distintos.

5) Nº. máximo de setas num grafo orientado com n vértices:

A(n,2) = n (n-1)

PS: A(n,p) = arranjos de n tomados p a p = n! / (n-p)!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 229

6) G'=(V',A') diz-se um subgrafo de G = (V,A) sse V'V e A' A.

7) Um caminho num grafo G não orientado (resp.: orientado) é uma


sequência de n+1 vértices (n>0) - a1, a2, ..., an, an+1 -
satisfazendo a condição:
(i=1,...,n) (ai, ai+1)A(G)
(resp.: (i=1,...,n) <ai, ai+1>A(G) )

comprimento do caminho a1, a2, ..., an, an+1 = n (= nº de


arestas/setas que é necessário "percorrer" para ir de a1 a an+1);

Diz-se que dois vértices estão ligados sse existe um caminho


entre eles;

Num grafo orientado, se existe um caminho entre b e c diz-se que


c é um descendente de b, e que b é um ascendente de c;

caminho simples: caminho que não utiliza a mesma aresta mais


do que uma vez;

caminho elementar: caminho que não passa pelo mesmo vértice


mais do que uma vez (isto é, caminho em que todos os vértices
são distintos com a possível excepção do primeiro e do último);

circuito: caminho em que o vértice inicial coincide com o vértice


final;

circuito hamiltoneano: circuito elementar que passa por todos os


vértices do grafo;

circuito euleriano: circuito simples que passa por todas as


arestas do grafo.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 230

8) Um grafo (orientado ou não orientado) diz-se conexo sse


quaisquer dois vértices distintos estão ligados;

Um grafo orientado diz-se fortemente conexo sse para quaisquer


dois vértices distintos b e c, existe (pelo menos) um caminho de b
para c e existe (pelo menos) um caminho de c para b.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 231

ESPECIFICAÇÃO ABSTRACTA

especificação grafos não orientados[Vertice] =


boolean 
géneros
Grafo
operações
construtoras
inicial:  Grafo;
acrv: Grafo Vertice  Grafo;
acra: Grafo Vertice Vertice  Grafo
acessórias
adjacentes: Grafo Vertice Vertice  boolean;
ligados: Grafo Vertice Vertice  boolean
auxiliares
existev: Grafo Vertice  boolean
axiomas
( GGrafo) ( V,V1,V2,V3,V4Vertice)

acrv(acrv(G,V1),V2) = se existev(acrv(G,V1),V2)
então acrv(G,V1)
senão acrv(acrv(G,V2),V1);
acra(G,V1,V2) = se existev(G,V1) && existev(G,V2)
então (
se adjacentes(G,V1,V2)
então G
senão acra(G,V2,V1) //graf. não orientados
);

existev(inicial,V) = false;
existev(acrv(G,V1),V2) = se V1 == V2 então true
senão existev(G,V2);
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Grafos - 232

existev(acra(G,V1,V2),V3) = existev(G,V3);

adjacentes(inicial,V1,V2) = false;
adjacentes(acrv(G,V1),V2,V3) = adjacentes(G,V2,V3);
adjacentes(acra(G,V1,V2),V3,V4) =
se (V3==V1 && V4==V2) || (V3==V2 && V4==V1)
então true
senão adjacentes(G,V3,V4);

ligados(inicial,V1,V2) = false;
ligados(acrv(G, V1),V2,V3) = ligados(G,V2,V3);
ligados(acra(G,V1,V2),V3,V4) =
(a) se ligados(G,V3,V4) então true
senão(
(b) se (V3==V1 && V4==V2) || (V3==V2 && V4==V1)
então true
senão(
(c) se V3==V1 então ligados(G,V4,V2)
senão(
se V3==V2 então ligados(G,V4,V1)
senão(
se V4==V1 então ligados(G,V3,V2)
senão(
se V4==V2 então ligados(G,V3,V1)
(d) senão (ligados(G,V3,V1)&&ligados(G,V4,V2))
||
(ligados(G,V3,V2)&&ligados(G,V4,V1))
)
)
)
)
)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 233

pré-condições
( GGrafo) ( V1,V2Vertice)
acra(G,V1,V2) requer existev(G,V1) && existev(G,V2);
fim

(a)

V3 V4 V2

V1

(b) (V3==V1 && V4==V2) || (V3==V2 && V4==V1)

V2

V1

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 234

(c) (V3==V1 && V4!=V2)

V2 V4

V1

(d) (V3!=V1 && V4!=V2 && V3!=V2 && V4!=V1)

V3

V2 V4

V1

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 235

Representações (computacionais) para os grafos

Existem várias representações possíveis para os grafos !

1º Problema: Quais ?

A análise de diferentes "representações matemáticas" do conceito de


grafo (i.e. de diferentes definições alternativas de grafos) sugere
naturalmente diferentes representações computacionais

2º Problema: Qual escolher ?

A escolha de uma particular representação computacional depende


muito da aplicação em questão e, nomeadamente, das operações que
se espera ter de executar sobre o grafo, havendo que entrar em linha
de conta com

 o espaço de memória ocupado

 o tempo de execução dessas operações

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 236

Algumas "representações matemáticas" dos grafos

Definições:

1) Dado um grafo G=(V,A), orientado (resp.: não orientado), as


funções sucessor e antecessor definem-se como se segue:

Função sucessor (t+):


(*) t+: V  2V
t+(b) = {c : <b,c>  A} (resp.: {c : (b,c)  A} )

Função antecessor (t-):


t-: V  2V
t-(c) = {b : c  t+(b)}

Definição alternativa de grafo:


G = (V,t+) com V um conjunto não vazio e t+ como em (*)
(G é não orientado se t+ = t-)

2) Dado um grafo orientado (resp.: não orientado), G = (V,A), com


n vértices x1,...,xn, a matriz da adjacência de G é uma matriz
M = [mij]ij=1,...,n , de zeros e uns, assim definida:
mij = 1 sse <xi , xj>  A (resp.: (xi , xj)  A )

Definição alternativa de grafo:


G = (V,M) com V um conjunto não vazio e M uma matriz n por
n (com n = #V) de zeros e uns
(G é não orientado se M é uma matriz simétrica)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 237

3) Dado um grafo orientado, sem lacetes, G = (V,A), com n vértices


x1,...,xn, e k arestas (setas) a1,...,ak (k ≤ n2: porquê?), a matriz
da incidência de G é uma matriz
M = [mij] i=1,...,n ; j=1,...,k
de elementos em {0, 1, -1}, assim definida:
mij = 1 sse xi é o vértice origem de aj
mij = -1 sse xi é o vértice terminal de aj
mij = 0 nos outros casos

Definição alternativa de grafo orientado sem lacetes:


G = (V,M) com V um conjunto não vazio e M uma matriz n por
k (com n = #V e k ≤ n2) de de elementos em {0, 1, -1}

Notas:

a) No caso dos grafos orientados, com lacetes, podemos definir a


matriz de incidência de modo análogo, supondo, por exemplo,
que mij pode também tomar o valor 2 se a seta aj se dirigir de xi
para xi;

b) No caso dos grafos não orientados, sem lacetes podemos


definir a matriz de incidência de modo análogo, supondo que
mij só toma o valor 1 desde que xi seja uma extremidade de aj
(isto é, deixa de se considerar o valor -1) ;

c) Analogamente se poderá definir a matriz de incidência de


um grafo não orientado, com lacetes

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 238

Exemplos:

Grafo:
v1
a3
a1
a2
v2 v3

a4 a5
v4

Função successor:

v1  {v2, v3}
v2  {v1, v4}
v3  { }
v4  {v3}

Matriz de adjacência: Matriz de incidência:

1 2 3 4 1 2 3 4 5
1 0 1 1 0 1 -1 1 1 0 0
2 1 0 0 1 21 1 -1 0 1 0
3 0 0 0 0 3 0 0 -1 0 -1
4 0 0 1 0 4 0 0 0 -1 1

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 239

Algumas representações computacionais de um grafo

Representação da matriz da adjacência:

Espaço ocupado: O(n2), i.e. proporcional a n2 (n = nº de vértices)

"Tempo" de resposta a algumas questões para grafos orientados


(para um grafo g) :

i é adjacente a j ?  O(1) (g[i,j]=1||g[j,i]=1)

grau externo de i ?  O(n) (Σj=1..n g[i,j])

grau interno de i ?  O(n) (Σj=1..n g[j,i])

nº de setas ?  O(n2)

Quando os grafos são esparsos (i.e. a sua matriz da adjacência é


esparsa) é natural que se possa encontrar representações destes que
permitam responder à última questão muito mais rapidamente, e que
ocuparão muito menos espaço:

 Representação em matrizes esparsas!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 240

Representação da função sucessor por listas encadeadas:

Grafo: Função successor:


0
0  {1, 2}
1  {0, 3}
1 2 2{}
3  {2}

Representação da função sucessor:

0 1 2
1
0 3
2
3 2

Espaço ocupado: O(n+m)


(n = nº de vértices; m= nº de arestas << n2, para grafos esparsos)

"Tempos" (para um grafo orientado g):

i é adjacente a j ?  no pior dos casos: percorrer g[i] e g[j]


grau externo de i ?  percorrer g[i]
grau interno de i ?  percorrer todas as listas: O(n+m)
nº de setas ?  percorrer todas as listas: O(n+m)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 241

Representação de grafos com pesos:

Certos grafos (que designaremos de grafos com pesos) incluem um


número associado a cada seta/aresta: o seu peso ou custo (por
exemplo, a distância entre duas cidades).

Tal informação poderá ser guardada acrescentando um campo


conveniente aos nós que são encadeados, ou às componentes da
matriz da adjacência (e.g. se os pesos forem inteiros positivos,
poder-se-á substituir os 1's por esse peso).

Travessia de um grafo

Para saber e.g. se dois vértices estão ligados temos de fazer uma
travessia (eventualmente parcial) do grafo.

Travessia de um grafo: dado um grafo e um seu vértice V, "visitar"


todos os vértices que são acessíveis a partir de V.

Enquanto que se considera em geral três modos de efectuar a


travessia duma árvore binária (travessia prefixa, infixa e sufixa), nos
grafos considera-se em geral dois modos:

1) Travessia em profundidade

2) Travessia em largura

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 242

Travessia (a partir de V) em profundidade:

1. Visitar o vértice inicial V;


2. Escolher um vértice W, adjacente/sucessor (caso grafo não
orientado/orientado) a V, que ainda não tenha sido visitado,
e iniciar uma travessia em profundidade do grafo a partir de
W;
3. Quando é alcançado um vértice U cujos vértices
adjacentes/sucessores já foram todos visitados, voltamos ao
último vértice visitado que possui um vértice
adjacente/sucessor W, ainda não visitado, e iniciamos uma
travessia em profundidade a partir de W;
4. A pesquisa termina quando não existir qualquer vértice
ainda não visitado que possa ser alcançado a partir de
algum dos vértices visitados.

Travessia (a partir de V) em largura:

1. Visitar o vértice inicial V;


2. Ao contrário da travessia em profundidade, são em seguida
visitados todos os vértices adjacentes/sucessores de V que
ainda não tenham sido visitados;
3. Em seguida, são visitados todos os vértices
adjacentes/sucessores a esses vértices que ainda não
tenham sido visitados, e assim sucessivamente.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 243

Exemplo:

Grafo:
v3 v4

v1 v2 v5

Travessia em profundidade a partir de v1:

v1 v2 v5 v3 v4

Travessia em largura a partir de v1:

v1 v2 v3 v5 v4

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 244

Pseudo-código:

Assumindo: o tipo Vertice tomando valores em {0,..., N-1}; e uma


variável boolean[] visitado=new boolean[N], cujas componentes
deverão ter sido inicializadas a false antes de cada travessia.

void travessia_profundidade(Grafo G, Vertice V){


pilha[Vertice] P=sobrepoe(nova,V);
Vertice Z,W;
while(!vazia(P)){
Z=topo(P); P=retira(P); visitado[Z]=true;
for(“cada vertice W adjacente/successor a Z no grafo G”){
if(visitado[W]==false)
P=sobrepoe(P,W);
}
}
}

void travessia_largura(Grafo G, Vertice V){


fila[Vertice] F=entra(nova,V);
Vertice Z,W;
while(!vazia(F)){
Z=primeiro(F); F=sai(F); visitado[Z]=true;
for(“cada vertice W adjacente/successor a Z no grafo G”){
if(visitado[W]==false)
F=entra(F,W);
}
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Grafos - 245

Exercício

1. Implemente Grafos através de uma das representações


computacionais apresentadas. Inclua nessa implementação os
métodos, que dado um grafo g e um vértice v (de g):

a) print_prof – escreve no visor os vértices do grafo g executando


uma travessia em profundidade a partir do vértice v;
b) print_larg – escreve no visor os vértices do grafo g executando
uma travessia em largura a partir do vértice v.

2. Adapte uma das representações computacionais apresentadas de


modo a incluir distâncias nas arestas e implemente o algoritmo de
Dijkstra para obter o caminho mais curto entre dois vértices do
grafo.

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 246

ORDENAÇÃO

Considerações genéricas

Objectivo: definir "algoritmos" que ordenem rapidamente uma


lista/sequência de elementos, de acordo com um seu “campo
chave”.

Interesse: Existem operações que exigem a ordenação prévia (e.g.


pesquisa binária).

Vamos supor que se pretende ordenar a lista por ordem não


decrescente (i.e. crescente em sentido lato: ≤)

Os algoritmos de ordenação dependem fortemente da estrutura de


dados onde a lista está memorizada:

Algoritmos de ordenação interna: matrizes 

Algoritmos de ordenação externa: ficheiros

A escolha de um particular algoritmo depende de múltiplos factores


relacionados com a natureza da lista a ordenar (e.g. nº de elementos,
etc.)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 247

Eficiência de um algoritmo de ordenação: que critérios ?

1. Cálculo do nº de comparações entre elementos da lista


(em média , no pior caso e, às vezes, no melhor caso)

2. Cálculo do nº de movimentos (ou do nº de trocas )


(em média , no pior caso e, às vezes, no melhor caso)

3. Memória utilizada

Nos algoritmos que iremos apresentar, para ordenar n elementos de


uma lista:

- Cálculo do nº de comparações:

algoritmos de ordenação elementares ( métodos directos ):

O(n2) (média)

algoritmos de ordenação "avançados" ("bons"):

O(n log n)

- Memória utilizada:

O(n) ordenação da matriz "no próprio lugar" 

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 248

Notas e notações:
1) log = log2 ; ln = loge .

2) Cmax(n), Cmed(n), Cmin(n) = nº máximo, médio e mínimo de


comparações que o algoritmo exige para ordenar uma lista de n
elementos;

C(n) = nº de comparações para os algoritmos em que este


número é independente da permutação inicial considerada;

Mmax(n), Mmed(n), Mmin(n), M(n) = idem para os


movimentos;

Nem sempre é fácil calcular estes valores (exacta ou


aproximadamente) !

3) Os algoritmos elementares podem ser preferíveis aos


avançados para n pequeno (até por serem mais simples).

4) Um movimento de um elemento da lista é em geral uma


operação mais custosa que uma simples comparação;

5) A importância dos movimentos para registos longos pode ser


diminuída (recorrendo a memória adicional):

a) encadear os elementos na matriz através de mais um campo


que refere o índice do próximo elemento da lista (e apenas o
valor desse campo é alterado pelo algoritmo);

b) considerar uma matriz auxiliar onde se refere (apenas) as


posições na matriz "input" (que não é alterado pelo algoritmo)
dos elementos que deviam estar na posição em questão.
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Ordenação - 249

Algoritmos para a ordenação de matrizes

Assume-se as seguintes declarações, a utilizar na função de


ordenação:

final int n = ... ; // superior a 1

public interface ItemComparablekey{


public Comparable key();
}

Assume-se ainda que cada matriz v do tipo ItemComparablekey[]


contem n elementos para ordenar !

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 250

Algoritmo da inserção directa

Ideia básica: Adicionar (no lugar apropriado) repetidamente um


novo elemento a uma lista já ordenada, de modo a que a lista
resultante fique também ordenada.

0 k n-1

sub-matriz ordenada sub-matriz não ordenada

v[0..k[ (i.e. v[0..k-1]) está ordenado!


notação: v[0..k] abreviatura de <v[0],...,v[k]> ;
v[0..k[abreviatura de <v[0],...,v[k-1]>

ordenação: for(int k=1; k!=n; k++) acção

acção deve "inserir" v[k] numa posição em [0..k] que garanta que as
componentes <v[0],...,v[k]> ficam por ordem crescente (da sua
chave), sabendo-se que no seu início as componentes v[0],...,v[k-1]
se encontravam já por ordem crescente.

Ideia: (i) guardar o valor de v[k] numa variável (e.g. x );


(ii) deslocar as componentes, cuja chave é maior que a chave
de x, uma casa para a direita;
(iii) colocar x na posição pos, assim obtida;
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Ordenação - 251

inserção directa – versão 1

void ordena(ItemComparablekey[] v){


ItemComparablekey x;
int pos;
for(int k = 1; k <= n-1; k++){
x = v[k];
pos = k;
while( (pos != 0) &&
(v[pos-1].key().compareTo(x.key())>0) ){
v[pos] = v[pos-1];
pos--;
}
v[pos] = x;
}
}

Nalgumas linguagens de programação pode não funcionar! Porquê ?

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 252

inserção directa – versão 2

void ordena(ItemComparablekey[] v){


ItemComparablekey x;
int pos;
boolean stop;
for(int k = 1; k <= n-1; k++){
x = v[k];
pos = k;
stop = v[pos-1].key().compareTo(x.key())<=0;
while( !stop ){
v[pos] = v[pos-1];
pos--;
if(pos > 0)
stop = v[pos-1].key().compareTo(x.key())<=0;
else stop = true;
}
v[pos] = x;
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 253

inserção directa – versão 3: "técnica da sentinela”

Considera-se que a matriz tem mais uma componente, e que a de


índice 0 é reservada como componente “artificial” para obter uma
condição de terminação simples e eficiente para o ciclo.

0 1 k n

sub-matriz ordenada sub-matriz não ordenada

Adaptando a versão 1 onde se substitui v[0] por x:

void ordena(ItemComparablekey[] v){


int pos;
for(int k = 2; k <= n; k++){
v[0] = v[k];
pos = k;
while( v[pos-1].key().compareTo(v[0].key())>0 ){
v[pos] = v[pos-1];
pos--;
}
v[pos] = v[0];
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 254

eficiência do algoritmo da inserção directa

void ordena(ItemComparablekey[] v){


int pos;
for(int k = 2; k <= n; k++){
v[0] = v[k];
pos = k;
while( v[pos-1].key().compareTo(v[0].key())>0 ){
v[pos] = v[pos-1];
pos--;
}
v[pos] = v[0];
}
}

Número de comparações:

- passo do ciclo for executado para: k=2,..., n


- em cada passo do ciclo for:
uma comparação por cada avaliação da guarda do ciclo while
(tantas comparações quantos os elementos em [1..k-1] que têm
chave maior que a de v[0], mais uma)

Cmin(n) = Σk=2..n 1 = n – 1
(ocorre quando a lista a ordenar já está ordenada)

Cmax(n) = Σk=2..n k = (n2 + n – 2)/2


(ocorre quando a lista a ordenar está pela ordem inversa)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 255

Cmed(n) ?

Assumindo que todas as chaves são diferentes e que todas as


permutações das n chaves são equiprováveis!

Qual é o número médio (esperado) de comparações que são


necessárias para colocar v[k] na posição correcta = E(k) ?

E(k) = Σi=1..k nc(ki) x prob(ki)

onde:
nc(ki) = nº de comparações se v[k] deve ir para a posição i
=k–i+1

Prob(ki) = probabilidade de v[k] dever ir para a posição i

= 1/k

(não esquecer que nas passagens anteriores v[k] nunca foi


comparado com v[1],...,v[k-1])

E(k) = Σi=1..k (k – i + 1) x 1/k = (k + 1)/2

portanto:

Cmed(n) = Σk=2..n (k + 1)/2 = (n2 + 3n – 4)/4

Conclusão: algoritmo particularmente aconselhável quando é de


prever que a lista inicial tenha poucos elementos fora de ordem!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 256

Número de movimentos:

Cálculo análogo: em cada execução do passo do for, o nº de


movimentos executados é igual ao nº de comparações mais um!

Mmin(n) = Σk=2..n 2 = 2(n-1)

Mmax(n) = Σk=2..n (k + 1) = (n2 + 3n – 4)/2

Mmed(n) = Σk=2..n (k + 3)/2 = (n2 + 7n – 8)/4

Nota:

Para n pequeno é importante conhecer o valor exacto dos números


anteriores!

Para n grande o importante é a ordem da sua grandeza!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 257

Algoritmo da selecção directa

Ideia básica: Seleccionar o elemento de menor chave de entre os


elementos que ainda não estão na posição correcta, e colocar esse
elemento na posição definitiva. A lista resultante é tal que, quer esse
elemento, quer os anteriores, ficam ordenados e na posição
definitiva.

0 k n-1

já estão nas posições correctas ainda não estão nas posições


correctas

ordenação: for(int k=0; k<=n-1; k++) acção

Mas: “v[0..n-2] nas pos. correctas  v[0..n-1] nas pos. correctas”

ordenação: for(int k=0; k<=n-2; k++) acção

acção deve colocar em v[k] o mínimo de v[k..n-1];

Ideia: (i) Encontrar a posição pos do mínimo (das chaves) de


v[k..n-1];
(ii) trocar v[k] com v[pos].

i.e.: acção = {descobre_pos; troca;}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 258

descobre_pos:

0 k i n-1

não interessam ainda não já analisados


analisados

{
pos = n-1;
for(int i=n-2; i >= k; i--)
if( v[i].key() “<” v[pos].key() ) pos = i;
}

troca:

{
x = v[k];
v[k] = v[pos];
v[pos] = x;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 259

selecção directa:

void ordena(ItemComparablekey[] v){


ItemComparablekey x;
int pos;
for(int k = 0; k <= n-2; k++){

// descobre_pos
pos = n-1;
for(int i=n-2; i >= k; i--)
if(v[i].key().compareTo(v[pos].key() )<0) pos = i;

// troca
x = v[k]; v[k] = v[pos]; v[pos] = x;
}
}

eficiência do algoritmo de selecção directa

Os números de comparações e de movimentos não dependem da


ordem por que estão os elementos na matriz inicial (a ordenar)!

- passo do ciclo for de fora: k = 0, ..., n-2 !


- em cada passo do ciclo for de fora:
1 comparação em cada passo do ciclo for de dentro i = k,...,n-2
1 troca (= 3 movimentos)

C(n) = Σk=0..n-2(Σi=k..n-2 1) = (n2 – n)/2

M(n) = Σk=0..n-2 3 = 3(n – 1)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 260

Algoritmo das trocas directas ("Bubblesort")

Tal como para a selecção directa:

ordenação: for(int k=0; k<=n-2; k++) acção

acção deve colocar em v[k] o mínimo de v[k..n-1];

Mas agora tal objectivo é atingido por trocas sucessivas através de


um simples ciclo:

acção: for(int i=n-2; i >= k; i--)


if(v[i].key() “>” v[i+1].key() ) “trocar v[i] com v[i+1]”

o mínimo de v]i..n-1] está à esquerda (i.e. em v[i+1])!!

0 k i n-1

não interessam ainda não já analisados:


analisados chave menor à
esquerda

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 261

Bubblesort:

void ordena(ItemComparablekey[] v){


ItemComparablekey x;
for(int k = 0; k <= n-2; k++)
for(int i=n-2; i >= k; i--)
if(v[i].key().compareTo(v[i+1].key() ) > 0){
x = v[i]; v[i] = v[i+1]; v[i+1] = x;
}
}

eficiência do algoritmo bubblesort

O número de comparações não depende da ordem por que estão os


elementos no vector inicial, sendo igual ao número de comparações
da selecção directa:

C(n) = (n2 – n)/2

O número de movimentos já depende da ordem inicial !


- passo do ciclo for de fora: k = 0, ..., n-2 !
- em cada passo do ciclo for de fora:
3 movimentos sempre que v[i].key()>v[i+1].key() em cada passo do ciclo for de
dentro i = k,...,n-2

Mmax(n) = Σk=0..n-2 (Σi=k..n-2 3) = (3n2 – 3n)/2

Mmed(n) = (3n2 – 3n)/4 (S. Baase)

Mmin(n) = Σk=0..n-2 (Σi=k..n-2 0) = 0

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 262

Comparação da eficiência dos três métodos directos

Inserção directa Selecção directa Bubblesort

Cmax(n) (n2 + n – 2)/2 (n2 – n)/2 (n2 – n)/2

Cmed(n) (n2 + 3n – 4)/4 (n2 – n)/2 (n2 – n)/2

Cmin(n) n–1 (n2 – n)/2 (n2 – n)/2

Mmax(n) (n2 + 3n – 4)/2 3(n – 1) (3n2 – 3n)/2

Mmed(n) (n2 + 7n – 8)/4 3(n – 1) (3n2 – 3n)/4

Mmin(n) 2(n-1) 3(n – 1) 0

Conclusões:

Em geral: Selecção directa

MELHOR QUE

Inserção directa

MELHOR QUE

Bubblesort

Contudo, quando a lista inicial está quase ordenada é de escolher


a inserção directa !
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Ordenação - 263

Optimizações do Bubblesort

1ª Optimização

Observação: sempre que numa passagem, para a colocação na


posição k do mínimo de v[k],...,v[n-1], não ocorre qualquer troca a
partir de uma dada posição pos, tal significa que as componentes em
[k..pos] estão já na posição correcta!

optimização (do nº de comparações a efectuar): memorizar a


posição da última troca!

void ordena(ItemComparablekey[] v){


ItemComparablekey x;
int pos, k=0;
while(k <= n-2){
pos=n-1;
for(int i=n-2; i >= k; i--)
if(v[i].key().compareTo(v[i+1].key()) > 0 ){
x = v[i]; v[i] = v[i+1]; v[i+1] = x;
pos = i; // actualizacao de pos
}
k = pos + 1; // actualizacao da var. de progresso do ciclo
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 264

2ª Optimização

Pode ainda optimizar-se o algoritmo, alternando o sentido das


passagens, de modo a evitar-se a seguinte assimetria no
comportamento:

12 18 42 44 55 67 94 6  exige uma passagem

94 6 12 18 42 44 55 67  exige 7 passagens

O algoritmo que se obtém chama-se SHAKERSORT !

Eficiência:

- A análise do número de comparações para estas optimizações do


Bubblesort é complicada !

- De qualquer modo, o nº de movimentos permanece igual !

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 265

Limitações dos métodos directos

limites à eficiência de qualquer algoritmo que remova no máximo


uma inversão após cada comparação !

Teorema: (S. Baase)

Qualquer algoritmo que ordene por comparação de chaves e que


remova no máximo uma "inversão" após cada comparação tem de
executar pelo menos

n (n-1) / 2 comparações, no pior caso


n (n-1) / 4 comparações, em média

Nota: "inversão em v[0..n-1]" é um par (v[i],v[j]) tal que i<j e


v[i].key() > v[j].key().

Consequência:

Os métodos elementares essencialmente comparam um elemento


com o da posição adjacente, mudando um elemento de uma posição
(em cada passo básico): removem no máximo uma inversão por
cada comparação!

Pelo teorema o número de comparações é necessariamente da ordem


de n2 !

Se pretendermos métodos melhores (com um nº de comparações


da ordem de n log n) devemos procurar mover elementos "sobre
grandes distâncias" através de um só "salto"!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 266

Métodos de ordenação avançados

Shellsort

Inserção através da diminuição dos incrementos (algoritmo


avançado baseado num melhoramento do método da inserção
directa - D.L.Shell, 1959).

Método:

1) Escolher sequência de incrementos h1 ,..., ht :


ht = 1 e hi+1 < hi

2) Para i=1,...,t efectuar uma hi-ordenação de v

hi-ordenação: ordena todas as hi-sublistas de v, i.e. todas as


sublistas formadas por elementos separados de hi posições.
sublistas: <v[0],v[0+hi],v[0+2hi],v[0+3hi],v[0+4hi],...> ,
<v[1],v[1+hi],v[1+2hi],v[1+3hi],v[1+4hi],v[1+2hi],...>, etc.

Objectivo de uma hi-ordenação de v:

(I[0..n-1])(J[0..n-1]) (J = I+hi  v[I].key() ≤ v[J].key())

ou

(I[0+hi,1+hi,2+hi,3+hi,4+hi,...,n-1]) (v[I-hi].key() ≤ v[I].key())

Se hi = 1, temos como objectivo a ordenação de toda a matriz, i.e.


(I[1..n-1]) (v[I-1].key() ≤ v[I].key())
Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Ordenação - 267

Exemplo de aplicação do método:

44 55 12 42 94 18 06 67

 4-ordenação

44 18 06 42 94 55 12 67

 2-ordenação

06 18 12 42 44 55 94 67

 1-ordenação

06 12 18 42 44 55 67 94

A 1-ordenação final garante que o método funciona !

Embora cada hi-ordenação envolva todos os elementos da lista, a


ordenação de cada hi-sublista ou envolve poucos elementos, ou
estes já estão + ou - bem ordenados pelas hr-ordenações anteriores.

Logo para ordenar cada hi-sublista deve-se escolher um método que


seja eficiente nessas condições:  INSERÇÃO DIRECTA!

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 268

Método da inserção directa (versão 1)

h-ordenação, com h = 1:

for(int k = 1 /* i.e.: h */ ; k <= n-1; k++){


x = v[k];
pos = k;
while( (pos != 0) && (v[pos-1].key() “>” x.key()) ){
/* guarda equivalente:
(pos-h >= 0) && (v[pos-h]. key() “>” x. key())
*/
v[pos] = v[pos-1]; //i.e.: v[pos] = v[pos-h];
pos--; //i.e.: pos = pos – h;
}
v[pos] = x;
}

h-ordenação, com h ≥ 1 qualquer:

for(int k = h; k <= n-1; k++){


x = v[k];
pos = k;
while( (pos-h>= 0) && (v[pos-h].key() “>” x.key()) ){
v[pos] = v[pos-h];
pos = pos – h;
}
v[pos] = x;
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 269

Que sequência de incrementos escolher ?

A melhor sequência de incrementos não é formada por potências de


2!

De facto estes não devem ser múltiplos uns dos outros, de modo a
garantir o maior número de interações entre as diferentes sublistas
que vão ser ordenadas em conjunto na passagem seguinte.

Pode provar-se que se a lista está hi-ordenada, então a lista


permanece hi-ordenada após as hj-ordenações ulteriores.

Embora não esteja ainda estabelecida qual a melhor sequência de


incrementos, Knuth recomenda:

1, 4, 13, 40, ... : hi-1 = 3hi+1; ht = 1 e t = | log3n | -1


ou
1, 3, 7, 15, 31,... : hi-1 = 2hi+1; ht = 1 e t = | log2n | -1

eficiência do algoritmo Shellsort

A análise da eficiência deste algoritmo levanta problemas


matemáticos complicados. Para a última sequência de incrementos,
pode obter-se um valor proporcional a

n1.2

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 270

shellsort (para uma particular escolha de 4 incrementos):

void ordena(ItemComparablekey[] v){


ItemComparablekey x;
int pos, h;
int t = 4;
int[] inc = {9, 5, 3, 1};
for(int m = 0; m < t; m++){
// valor do incremento
h = inc[m];
// inicio da h-ordenacao
for(int k = h; k <= n-1; k++){
x = v[k];
pos = k;
while( (pos-h>= 0) &&
(v[pos-h].key().compareTo(x.key()) > 0) ){
v[pos] = v[pos-h];
pos = pos – h;
}
v[pos] = x;
} // fim da h-ordenacao
}
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 271

Métodos de ordenação avançados

Quicksort

Baseado em trocas das posições dos elementos. Versão utilizando


matrizes (Hoare)

Ideia:

1) Partição em duas sublistas:

Partir a lista a ordenar em duas sublistas (a esquerda e a direita),


de modo a que todos os elementos da sublista esquerda tenham
chave menor ou igual à de que qualquer elemento da sublista
direita.

2) Ordenação das sublistas de modo independente:

Para cada lista ("sublista") obtida repetir 1, excepto se a lista for


unitária em cujo caso o processo termina (pois ela está ordenada).

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 272

Partição de uma lista de n elementos:

a) Escolher uma chave para partição (k) que satisfaça a propriedade


de ser maior ou igual que a menor chave na lista e menor ou
igual que a maior chave na lista.

Que chave para partição escolher ? Fácil de obter !

k = v[0].key() , a chave do elemento na 1ª posição;


k = v[n-1]. key() , a chave do elemento na ultima posição;
k = v[n/2]. key(), a chave do elemento na posição “central”;

k = mediana das chaves dos elementos da lista: valor que é <


(≤) que metade das chaves na lista e > (≥) que a outra metade.
Mas é preciso obter esse valor !

b) A sublista esquerda (resp.: direita) é formada por todos elementos


da lista inicial cuja chave é < (resp.: ≥) que k.

exemplo:

[44 55 12 42 94 18 06 67]

↓ (partição para k = 26)

[06 18 12] + [42 94 55 44 67]

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 273

Quicksort sobre matrizes: Partição

Como efectuar a partição de modo simples, assumindo que a lista se


encontra numa matriz ?

INICIAL: [44 55 12 42 94 18 06 67] k = 26


↑ ↑
i j

PROGRIDE: [44 55 12 42 94 18 06 67] k = 26


↑ ↑ ↑
i j

TROCA: [06 55 12 42 94 18 44 67] k = 26


↑ ↑ ↑
i j

PROGRIDE: [06 55 12 42 94 18 44 67] k = 26


↑ ↑ ↑ ↑ ↑
i j

TROCA: [06 18 12 42 94 55 44 67] k = 26


↑ ↑ ↑ ↑ ↑
i j

PROGRIDE: [06 18 12 42 94 55 44 67] k = 26


↑ ↑ ↑ ↑
i
↑ ↑ ↑ ↑ ↑ ↑
j

PÁRA: [06 18 12] + [42 94 55 44 67]


Algoritmos e Estruturas de Dados Filipe Santos
Departamento de Ciências e Tecnologias da Informação Ordenação - 274

Partição para a lista/matriz v[esq..dir]:

Duas variáveis de progresso:

i (da esquerda para a direita e inicialmente igual a esq)

j (da direita para a esquerda e inicialmente igual a dir)

1) i deve progredir enquanto v[i].key() “<” k ;

2) j deve progredir enquanto v[j].key() “>” k ;

3) após essa progressão troca-se v[i] com v[j], a não ser que j < i ;

4) caso j > i , i e j continuam a progredir ;

5) no final as listas a partir são:

lista esquerda = v[esq..j]


lista direita = v[i..dir] .

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 275

Partição para a lista/matriz v[esq..dir]:

{
ItemComparablekey x;
int i, j;
Comparable k;
i = esq; j = dir;
k = v[(esq+dir)/2].key(); // escolha de uma chave
do{
while(v[i].key() “<” k) i++;
while(v[j].key() “>” k) j--;
if(i <= j){
// troca v[i] com v[j]
x = v[j]; v[j] = v[i]; v[i]= x;
// actualiza i e j
i++; j--;
}
} while(j > i);
}

Após partição: lista_esq = v[esq..j] ; lista_dir = v[i..dir].

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 276

Quicksort:

void particao(ItemComparablekey[] v, int esq, int dir){


ItemComparablekey x;
int i, j;
Comparable k;
i = esq; j = dir;
k = v[(esq+dir)/2].key(); // escolha de uma chave
do{
while(v[i].key().compareTo(k)<0) i++;
while(v[j].key().compareTo(k)>0) j--;
if(i <= j){
// troca v[i] com v[j]
x = v[j]; v[j] = v[i]; v[i]= x;
// actualiza i e j
i++; j--;
}
} while(j > i);
// particao das sublistas esquerda e direita
if(esq < j) particao(v, esq, j);
if(i < dir) particao(v, i, dir);
}

void ordena(ItemComparablekey[] v){


particao(v, 0, n-1);
}

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 277

Eficiência do Quicksort sobre vectores:

Número de comparações:

no melhor caso: a chave de partição de cada lista é sempre a sua


mediana !

árvore de partições: para simplificar vamos supor que n é uma


potência de 2.
n elementos

n/2 elementos n/2 elementos

… … … …

1 elem. 1 elem. 1 elem. 1 elem. 1 elem. 1 elem. 1 elem. 1 elem.

Notas: Profundidade d; d-1 "níveis de partições"; em cada "nível" n


elementos; em cada nível todos os elementos são comparados com a
chave de partição: n comparações (às vezes n+1)

Cmin(n) = n (d-1) = n log n (nº de nós no nível d = 2d-1= n)

Algoritmos e Estruturas de Dados Filipe Santos


Departamento de Ciências e Tecnologias da Informação Ordenação - 278

no pior caso: a chave de partição de cada lista é sempre a maior (ou


a menor)!

árvore de partições:
n elementos

n-1 elementos 1 elemento

n-2 elementos 1 elemento

… 1 elemento

1 elemento 1 elemento

Notas: Profundidade d=n; d–1 "níveis de partições"; em cada


"nível" i=1,...,d-1 a partição só envolve n–i+1 elementos;

Cmax(n) = Σi=1..n-1 (n – i + 1) = (n2 + n – 2)/2

Algoritmos e Estruturas de Dados Filipe Santos