Algoritmos e Estruturas de Dados
Lição n.º 1
“Algoritmos e Estruturas de Dados”
“Algoritmos e Estruturas de Dados”
•  Logística
•  Tecnologia
•  Aulas
•  Avaliação
•  Programa da cadeira
•  O ambiente d...
Tecnologia
•  Linguagem: Java.
•  Componente gráfica: Processing.
•  Estilo: Checkstyle.
•  Ambiente: Eclipse, linha de co...
Aulas
•  Teóricas: Pedro Guerreiro.
•  Práticas: Hamid Shahbazkia.
20140529 Algoritmos e Estruturas de Dados 4
Bibliografia principal
Algorithms, quarta edição (2011), Robert
Sedgewick e Kevin Wayne.
Book site: http://algs4.cs.prince...
Bibliografia de interesse
•  Introduction to Algorithms, 3.ª
edição,Thomas Cormen, Charles
Leiserson, Ronald Rivest, Cliff...
Coursera
Algorithms, Part I
•  Instructors: Robert Sedgewick, Kevin Wayne.
•  “This course covers the essential informatio...
Mais Coursera
Algorithms, Part II
•  “Part II covers graph-processing algorithms, including
minimum spanning tree and shor...
Programa de AED
•  Conceitos fundamentais
•  Modelo de programação
•  Sacos, pilhas e filas
•  “Union-Find”
•  Análise de ...
Algoritmos de programação
•  Busca linear
•  Busca dicotómica
•  Bubblesort
•  Quicksort
•  Conversão numérica
•  Reversão...
Algoritmos no ensino secundário
•  Fatorização
•  Máximo divisor comum
•  Simplificação de frações
•  Regra de Ruffini
•  ...
Algoritmos da escola primária
•  Adição
•  Subtração
•  Multiplicação
•  Divisão
•  (Sucessor)
20140529 Algoritmos e Estru...
Algoritmos clássicos
•  Algoritmo de Euclides.
•  Método para o cálculo de π, de Arquimedes.
•  Crivo de Eratóstenes.
2014...
Algoritmo de Euclides
•  Calcula o máximo divisor comum de dois números
inteiros positivos.
•  É o mais antigo algoritmo i...
Algoritmo de Euclides, variantes recursivas
•  Formulação recursiva
•  Formulação recursiva, curto-circuito:
20140529 Algo...
Algoritmo de Euclides, versão de combate
•  Normalmente, usamos a seguinte variante iterativa:
20140529 Algoritmos e Estru...
Comparando as versões iterativas
•  Eis uma função de teste para comparar na consolas
duas versões iterativas:
20140529 Al...
Classes StdIn e StdOut
•  Usaremos a classe StdIn para ler e a classe StdOut
para escrever na consola.
20140529 Algoritmos...
A função main
•  Em cada classe, a função main apenas chamará as
funções de teste.
•  Por enquanto, só temos uma função de...
Classe Euclid
20140529 Algoritmos e Estruturas de Dados 20
public final class Euclid
{
public static int euclid(int x, int...
Correndo no Eclipse
20140529 Algoritmos e Estruturas de Dados 21
Correndo no Eclipse, a
interação dá-se na
consola do Ecli...
Correndo na linha de comando
•  Para correr na linha de comando, colocamo-nos da
diretoria bin, dentro do projecto, no wor...
Testando as funções todas
•  Eis uma segunda função de teste, para comparar os
resultados das quatro funções:
20140529 Alg...
Argumentos na linha de comando
•  Selecionamos a função de teste por meio de um
argumento na linha de comando:
20140529 Al...
Algoritmos e Estruturas de Dados
Lição n.º 2
Animação algorítmica
Animação algorítmica
•  Visualização dos algoritmos
•  Utilização do Processing
•  Recapitulação
•  Integração com o Java
...
Animação
•  Os algoritmos são pensamento puro.
•  Devemos compreendê-los abstratamente.
•  Mas é divertido vê-los a mexer....
Processing
•  O Processing é uma linguagem de programação que
vem com o seu próprio ambiente de
desenvolvimento.
•  De cer...
Recapitulando...
•  Em Processing, um programa é constituído por
algumas declarações globais e pelas funções setup e
draw....
setup e draw
20140529 Algoritmos e Estruturas de Dados 30
Java com Processing
20140529 Algoritmos e Estruturas de Dados 31
import processing.core.PApplet;
public class TShirtSketch...
Usando da linha de comando
•  Para corrermos o sketch a partir da linha de
comando, acrescentamos a função main:
20140529 ...
Animando o algoritmo de Euclides
•  Queremos representar cada variável, x e y, por uma
barra vertical, com altura proporci...
Animação diferida
•  Em vez de ir calculando cada novo valor segundo a
segundo, é mais prático calcular tudo logo, guardar...
Parametrização geral
•  Parametrizamos a largura das barras, a margem esquerda (que
é igual à margem direita, o intervalo ...
Gravando os valores
•  Precalculamos os sucessivos
valores de x e y, guardando-
os em dois arrays paralelos,
por meio de u...
Construção
•  O objeto sketch cria um objeto EuclidGraphical, o
qual guarda uma referência para o seu “pai”:
20140529 Algo...
A função setup
•  A função setup lê os dois números da consola, invoca
start no objeto gráfico, dimensiona a janela e marc...
As dimensões da janela
•  As dimensões da janela são calculadas pelo objeto
gráfico:
20140529 Algoritmos e Estruturas de D...
O método start
•  O método start fixa os valores dos números, manda
precalcular e determina as cores das barras:
20140529 ...
A função draw
•  A função draw é chamada automaticamente, com a
frequência determinada pela “frame rate”.
•  De cada vez, ...
O método display
•  O método display é cíclico: quando chega ao fim, recomeça.
•  De cada vez, determina quais os valores ...
Correndo na linha de comandos
•  Para poder correr na linha de comandos, temos a função main:
•  Neste caso, há duas bibli...
Variantes
•  Fazer uma cópia da barra mais baixa deslocar-se
suavemente para sobre a barra mais alta, antes de
esta baixar...
Algoritmos e Estruturas de Dados
Lição n.º 3
Coleções: sacos, pilhas e filas
Coleções: sacos, pilhas e filas
•  Especificação das coleções
•  Classes iteráveis
•  Sacos (em inglês bags)
•  Implementa...
Coleções
•  Coleções são... coleções!
•  Às coleções podemos acrescentar objetos, removê-
los e “iterá-los”.
•  Podemos ai...
Classes iteráveis
•  Queremos que as nossas coleções sejam iteráveis,
isto é, que permitam processar sequencialmente cada
...
APIs
20140529 Algoritmos e Estruturas de Dados 49
public class Bag<T> implements Iterable<T>
{
public Bag()
public void ad...
Capacidade?
•  Como indicamos a capacidade das coleções?
•  Não indicamos! Há apenas um construtor, sem
argumentos.
•  Log...
Sacos, implementação
•  Vamos implementar a classe Bag usando arrays
redimensionáveis.
•  Haverá um campo items, que é o a...
Sacos, construtor
•  Inicialmente, o array terá capacidade 1 e tamanho 0:
20140529 Algoritmos e Estruturas de Dados 52
pub...
Questão técnica: arrays genéricos
•  Nós gostaríamos de ter programado a criação do
array genérico items assim:
•  No enta...
Supressão de avisos
•  Como, forçar conversões é uma operação arriscada,
o Java emite um warning:
•  Evitamos o warning co...
Redimensionamento
•  Na verdade, não redimensionamos o array: criamos
outro array com a capacidade pretendida e fazemos a
...
Método add
•  Se, ao acrescentar um elemento ao saco, notarmos
que já não há espaço no array, redimensionamo-lo
para o dob...
Métodos size e isEmpty
•  Estes são muito simples:
20140529 Algoritmos e Estruturas de Dados 57
public int size()
{
return...
O iterador
•  Optamos por iterar pela ordem de entrada.
•  Usamos uma classe interna:
20140529 Algoritmos e Estruturas de ...
O método iterator
•  O método iterator apenas cria e retorna um objeto
de tipo BagIterator:
•  Mais tarde, para, por exemp...
Exemplo: problema dos estádios
•  A federação tem a lista dos estádios, com nome, comprimento
do relvado e largura do relv...
Classe Stadium
•  Uma classe imutável, para representar estádios:
20140529 Algoritmos e Estruturas de Dados 61
public clas...
Classe Stadium, continuação
20140529 Algoritmos e Estruturas de Dados 62
public class Stadium
{
// ...
public int area()
{...
Classe do problema
•  Usamos um saco de estádios.
•  O saco é criado vazio (como todos os sacos) e depois é
preenchido por...
Estádios selecionados
•  Os estádios selecionados também formam um saco:
20140529 Algoritmos e Estruturas de Dados 64
publ...
Método solve
•  Resolvemos o problema no método solve:
•  A função main cria um problema e invoca o método solve:
20140529...
Diretoria work
•  Tipicamente, guardaremos os nosso ficheiros de dados numa
diretoria work, dentro do projeto, ao lado das...
Custo do redimensionamento?
•  Redimensionar o array que suporta o saco parece ser um grande trabalho
suplementar. Será qu...
Exercício
•  Dispomos de um ficheiro com as cotações na bolsa de Lisboa.
•  Em cada linha vem o nome do “papel”, a cotação...
Algoritmos e Estruturas de Dados
Lição n.º 4
Pilhas
Pilhas
•  Implementação com arrays redimensionáveis.
•  Implementação com listas ligadas.
•  Aplicação: avaliação de expre...
Pilhas
•  As pilhas chamam-se pilhas porque funcionam como
pilhas...; pilhas de livros, de pratos, de papéis, não
pilhas n...
API da pilha
•  Já sabemos:
•  Comparando com Bag<T>, temos push em vez de
add, temos pop e o iterador emitirá os elemento...
Implementação com arrays redimensionáveis
•  É análoga à da classe Bag<T>:
20140529 Algoritmos e Estruturas de Dados 73
pu...
Encolhendo no pop
•  A estratégia será encolher para metade quando o
tamanho estiver a 25% da capacidade:
20140529 Algorit...
Iterador reverso
•  Nas pilhas, queremos iterar os elementos de maneira a ver
primeiro os que entraram na pilha mais recen...
Função de teste
•  Eis uma função de teste, que empilha os números lidos e
desempilha com ‘-’. No fim mostra o tamanho, se...
Implementação com listas ligadas
•  A lista ligada é programada em termos da classe
interna Node, a qual detém um campo pa...
O primeiro nó
•  Na pilha, um dos membros é o primeiro nó da lista; o
outro é o tamanho:
•  Omitimos o construtor por defe...
Empilhar, desempilhar
•  Para empilhar, criamos um novo nó, que passa a ser o
primeiro, ligando-o ao que estava em primeir...
isEmpty, size
•  Os métodos isEmpty e size são muito simples:
20140529 Algoritmos e Estruturas de Dados 80
public boolean ...
Iterador de listas
•  O cursor é uma referência para o nó corrente:
20140529 Algoritmos e Estruturas de Dados 81
private c...
Avaliação de expressões aritméticas
•  Como exemplo de aplicação das listas, estudemos a avaliação
de expressões aritmétic...
Pilha dupla de Dijkstra
•  Usamos duas pilhas: uma pilha de cadeias, para os operadores,
e uma pilha de números, para os o...
Classe DijkstraTwoStack
•  Primeiro as operações de suporte:
20140529 Algoritmos e Estruturas de Dados 84
public final cla...
Avaliação da expressão
•  A seguinte função meramente exprime as regras do algoritmo:
20140529 Algoritmos e Estruturas de ...
Aplicação dos operadores
20140529 Algoritmos e Estruturas de Dados 86
{
String op = operators.pop();
// StdOut.println(op)...
Função de teste
•  Para abreviar, determinamos que os tóquenes na expressão
vêm separados por espaços.
•  Assim, podemos o...
Experiência
•  Na linha de comando:
•  Próximas tarefas:
•  Evitar ter de separar os tóquenes por espaços.
•  Evitar ter d...
Algoritmos e Estruturas de Dados
Lição n.º 5
Expressões regulares
Expressões regulares
•  Toquenização de cadeias de caracteres.
•  Expressões regulares.
20140529 Algoritmos e Estruturas d...
Problema
•  O nosso problema é obter a sequência de tóquenes de
uma expressão aritmética, para depois poder avaliar a
expr...
Classe Tokenization
•  Programemos uma classe para fazer a toquenização
que nos convém.
•  Precisamos de uma função para c...
Encontrar números
•  A função findNumber encontra números inteiros e também
números com parte decimal.
•  Encontrar número...
Encontrar identificadores
•  Um identificador começa por uma letra e depois tem
zero ou mais letras ou algarismos:
2014052...
Toquenizando
•  Se na posição corrente estiver um algarismo, aí começa um número;
se estiver uma letra, aí começa um ident...
Toquenizando (continuação)
20140529 Algoritmos e Estruturas de Dados 96
public static Bag<String> tokens(String s)
{
// .....
Testando a toquenização
•  Primeiro, uma função para transformar um saco de cadeias numa
cadeia única (pronta a ser escrit...
Sacos e arrays
•  Podemos agora melhorar a função de teste da classe
DijkstraTwoStack, pois deixa de ser preciso apresenta...
Avaliação após toquenização
•  Eis a nova função de teste, que toqueniza a linha de entrada
antes da avaliação:
20140529 A...
Expressões regulares
•  Modernamente, a análise de cadeias para efeitos de
toquenização faz-se com expressões regulares.
•...
Expressões regulares, exemplos
•  “a”: representa o conjunto cujo único elemento é a cadeia “a”.
•  “abc”: representa o co...
Expressões regulares, mais exemplos
•  “[0-9]”: representa o conjunto das cadeias formadas por um único algarismo.
•  “[0-...
Aprendendo as expressões regulares
20140529 Algoritmos e Estruturas de Dados 103
public static void learnRegularExpression...
Exemplos
20140529 Algoritmos e Estruturas de Dados 104
pedros-­‐imac-­‐5:bin	
  pedro$	
  java	
  -­‐cp	
  ../stdlib.jar:....
Grupos
•  Podemos obter os grupos que quisermos,
parametrizando convenientemente a expressão regular:
20140529 Algoritmos ...
Tóquenes
•  Para obter os tóquenes de uma expressão aritmética,
“basta” indicar a expressão regular apropriada:
20140529 A...
Análise da expressão regular
•  A expressão regular que usámos tem quatro alternativas:
20140529 Algoritmos e Estruturas d...
Toque final
•  Podemos agora substituir a toquenização da classe
Tokenization pela da classe RegularExpressions:
20140529 ...
Algoritmos e Estruturas de Dados
Lição n.º 6
Filas
Filas
•  Implementação com listas ligadas.
•  Outros assuntos
•  Objetos funcionais
•  Functores
•  Arrays de functores
20...
Filas
•  As filas são a contrapartida informática das filas de espera
que encontramos na vida corrente.
•  A ideia é que o...
API da fila
•  Já a conhecemos:
•  Comparando com Stack<T>, temos enqueue em vez de push,
dequeue em vez de pop, e front e...
Implementação com listas
•  É semelhante a StackLists<T>, com uma diferença essencial: os
elementos são acrescentados no f...
Entrar na fila
•  Para fazer um elemento entrar na fila, primeiro criamos um
novo nó para esse elemento.
•  Depois, das du...
Sair da fila
•  Para fazer um elemento sair da fila, basta avançar a referência
first para o nó seguinte. Se não houver nó...
isEmpty, size, iterator
•  Os métodos isEmpty, size e iterator são iguais aos
das pilhas:
20140529 Algoritmos e Estruturas...
Functores
•  Em Java as funções não são conceitos de “primeira classe”.
•  Por outras palavras, as funções não são objetos...
Exemplo prático
•  Na classe DijkstraTwoStack selecionamos a operação a
usar observando o operador:
•  OK, mas isto é muit...
Calculando a função a usar
•  Funcionalmente, calcularíamos a função a usar, “em
função” do operador, assim, em esquema:
•...
Funções binárias e funções unárias
•  Como temos dois tipos de funções (binárias e unárias)
precisamos de dois tipos de fu...
Classes abstratas para os functores
•  Implementaremos os functores por meio de classes
abstratas:
•  Depois meteremos cad...
Functores binários
•  Eis as classes para os operadores binários:
20140529 Algoritmos e Estruturas de Dados 122
class Plus...
Functores unários
•  No exercício, só usámos um operador unário, mas o
esquema é o mesmo:
20140529 Algoritmos e Estruturas...
Avaliação, melhor
•  Na função de avaliação, fica assim:
20140529 Algoritmos e Estruturas de Dados 124
private static doub...
Arrays de functores
•  Precisamos de dois arrays de functores, um para as
operações binárias, outro para as operações unár...
Seleção de functores
•  Restam as funções de busca que obtêm o functor
correspondente ao operador dado:
20140529 Algoritmo...
Moral da história
•  Com este esquema, a função de avaliação fica mais
simples (mas mais sofisticada, é verdade).
•  Acres...
Algoritmos e Estruturas de Dados
Lição n.º 7
Análise de Algoritmos
Análise de Algoritmos
•  Questões centrais: tempo e memória.
•  Exemplo: soma tripla.
•  Determinação da frequência (absol...
Questões centrais
•  Quanto tempo levará o meu programa?
•  Quanta memória gastará o meu programa?
20140529 Algoritmos e E...
Exemplo: problema da soma tripla
•  Dada uma lista de números, sem repetições, quantos triplos
de números dessa lista soma...
Soma tripla: primeiro algoritmo
•  Só contamos um triplo se for o primeiro. Isto é, se
encontramos um triplo nas posições ...
Soma tripla: segundo algoritmo
•  Podemos evitar a repetição da contagens, ajustando a
variação dos índices diretamente, d...
Comparando o tempo de execução
•  A função countBasic faz nitidamente menos
operações do que a função countBrute.
•  Logo,...
Quantas vezes mais rápido?
•  “Quantas vezes mais rápido” é uma forma de falar, pois o
“número de vezes” pode depender da ...
Tempo de execução
•  O tempo de execução de um programa é
determinado por dois fatores:
•  O tempo de execução de cada ins...
O ciclo interno
•  Na prática, em muitos casos apenas as instruções que
são executadas com maior frequência têm peso
signi...
O ciclo interno, em countBasic
•  Na função countBasic, o ciclo interno contém a instrução.
•  Quantas vezes é executada?
...
O ciclo interno, ao contrário
•  A seguinte variante é equivalente à anterior, mas faz
as variáveis variar descendentement...
O ciclo interno, ao contrário
•  O ciclo interno é o mesmo:
•  Quantas vezes é executada esta instrução?
•  Para cada valo...
Aproximação til
•  A expressão N3/6-N2/2+N/3 é complicada...
•  É mais prático usar uma aproximação, notando que
para valo...
Comparação countBrute e countBasic
•  O número de vezes que a instrução if é executada
em countBrute é ~N3.
•  Em countBas...
Utilização de functores
•  Primeiro a classe abstrata:
•  Um functor para cada algoritmo:
20140529 Algoritmos e Estruturas...
Medição do tempo
•  Medimos o tempo do algoritmo com uma função da
classe abstrata:
20140529 Algoritmos e Estruturas de Da...
Função de teste
•  A função de teste lê os números do ficheiro cujo nome é
indicado no primeiro argumento da linha de coma...
Resultados da observação
•  Corremos com ficheiros com 1000, 2000, 3000, 4000 e 5000
números. Em cada ficheiro há números ...
Algoritmos e Estruturas de Dados
Lição n.º 8
Ordem de crescimento do tempo de execução
Ordem de crescimento do tempo de
execução
•  Classificação por ordem de crescimento do
tempo de execução.
•  Ensaios de ra...
Ordem de crescimento
•  Em análise de algoritmos, as aproximações til que nos
interessam são geralmente da forma g(N) ~ a*...
Ordens de crescimento habituais
20140529 Algoritmos e Estruturas de Dados 150
Descrição Função
Constante 1
Logarítmica log...
Ordem de crescimento do tempo
•  As medições de há pouco parecem indicar que o
tempo de execução cresce 8 vezes quando o
t...
Ensaios de razão dobrada
•  Primeiro, construímos um gerador de inputs
apropriado ao problema.
•  Depois experimentamos o ...
Array de números aleatórios
•  Para o problema da soma tripla precisamos de arrays
de números arbitrários, sem duplicados....
Testando a criação de números aleatórios
•  A função de teste obtém os argumentos da linha de
comando. Se não houver, usa ...
Função stringOf para arrays de inteiros
•  Função utilitária para observar o conteúdo de arrays
de int:
20140529 Algoritmo...
Números aleatórios, sem duplicados
•  Observe:
20140529 Algoritmos e Estruturas de Dados 156
public static int[] randomArr...
Observando os números aleatórios
•  Chamamos as duas funções de teste para os mesmos
argumentos na linha de comando:
20140...
Medição do tempo, com arrays aleatórios
•  Acrescentamos à classe abstrata uma função que faz
n ensaios, com arrays com si...
Testando com o algoritmo escolhido
•  A seguinte função de teste usa o algoritmo
countBasic:
20140529 Algoritmos e Estrutu...
Razão dobrada
•  Podemos automatizar os cálculos da página anterior,
em que a dimensão dos cálculos dobra de cada vez:
201...
Testando a razão dobrada
•  Para maior conveniência, a função de teste seleciona o
algoritmo pelo seu índice no array dos ...
Função printTable
•  Mostra os números no array dos tempos, linha a linha,
precedidos pelo tamanho do array usado no ensai...
Invocando a função de teste
20140529 Algoritmos e Estruturas de Dados 163
public static void main(String[] args)
{
// test...
Um algoritmo mais rápido
•  Vimos que countBasic é 6 vezes mais rápido do que countBrute; no entanto,
ambos são algoritmos...
Custo da ordenação
•  Os algoritmos de ordenação elementares—insertionsort,
selectionsort, bubblesort—são quadráticos.
•  ...
Busca dicotómica
•  A função rank calcula o índice de um elemento de valor x no
array a, se houver, ou -1, se não.
2014052...
Função countFaster
•  Eis o novo algoritmo para o problema da soma tripla:
20140529 Algoritmos e Estruturas de Dados 167
p...
Razão dobrada para o countFaster
•  Criamos uma nova classe derivada de ThreeSumAlgorithm:
•  Acrescentamos o objeto respe...
Memória: tipos primitivos
20140529 Algoritmos e Estruturas de Dados 169
Tipo Bytes
boolean 1
byte 1
char 2
int 4
float 4
l...
Memória: objetos
•  Num objeto, há a memória dos membros (instance variables), mais 16 bytes
de overhead (referência para ...
Memória: cadeias de carateres
•  As cadeias de carateres são objetos.
•  Cada objeto String tem 4 membros: uma referência ...
Algoritmos e Estruturas de Dados
Lição n.º 9
Union-Find
Union-Find
•  Problema da conectividade dinâmica.
•  Union-Find, em geral.
•  Quick-Find.
•  Quick-Union.
•  Quick-Union-W...
Conectividade dinâmica
•  Temos um conjunto de nós: x1, x2, ... xn.
•  Alguns desses nós estão ligados, dois a dois.
•  Es...
Exemplo: reticulado dinâmico
•  Visualizar um reticulado em que cada vez que ligamos dois
nós vizinhos, as conexões aos vi...
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Próximos SlideShares
Carregando em…5
×

Algoritmos e Estruturas de Dados, edição de 2013/2014

1.021 visualizações

Publicada em

Slides da cadeira Algoritmos e Estrutura de Dados, edição de 2013/2014, licenciatura em engenharia informática, Universidade do Algarve. Cobre as estruturas de dados básicas (pilhas, filas), Union-Find, algoritmos de ordenação, filas com prioridade, árvores e tabelas de dispersão.
Baseia-se em ideias e código do livro "Algorithms (4th ed.) de Robert Sedgewick e Kevin Wayne. No entanto, todos os programas foram apresentados nos slides foram reescritos usando um estilo de programação Java ligeiramente diferente.

Publicada em: Educação
0 comentários
4 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

Sem downloads
Visualizações
Visualizações totais
1.021
No SlideShare
0
A partir de incorporações
0
Número de incorporações
6
Ações
Compartilhamentos
0
Downloads
131
Comentários
0
Gostaram
4
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

Algoritmos e Estruturas de Dados, edição de 2013/2014

  1. 1. Algoritmos e Estruturas de Dados Lição n.º 1 “Algoritmos e Estruturas de Dados”
  2. 2. “Algoritmos e Estruturas de Dados” •  Logística •  Tecnologia •  Aulas •  Avaliação •  Programa da cadeira •  O ambiente de programação 20140529 Algoritmos e Estruturas de Dados 2
  3. 3. Tecnologia •  Linguagem: Java. •  Componente gráfica: Processing. •  Estilo: Checkstyle. •  Ambiente: Eclipse, linha de comando. •  Sistema operativo:Windows, MacOS, Linux. •  Avaliador automático: Mooshak. •  Tutoria: Moodle. 20140529 Algoritmos e Estruturas de Dados 3
  4. 4. Aulas •  Teóricas: Pedro Guerreiro. •  Práticas: Hamid Shahbazkia. 20140529 Algoritmos e Estruturas de Dados 4
  5. 5. Bibliografia principal Algorithms, quarta edição (2011), Robert Sedgewick e Kevin Wayne. Book site: http://algs4.cs.princeton.edu/ 20140529 Algoritmos e Estruturas de Dados 5 http://www.amazon.co.uk/Algorithms-Robert-Sedgewick/dp/032157351X/
  6. 6. Bibliografia de interesse •  Introduction to Algorithms, 3.ª edição,Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein. http://www.amazon.co.uk/Introduction-Algorithms-T- Cormen/ •  The Art of Computer Programming, Donald Knuth. http://www.amazon.co.uk/Art-Computer-Programming- Volumes-1-4a/ •  Algorithms + Data Structures = Programs, Niklaus Wirth (1975). http://www.amazon.co.uk/Algorithms-Structures-Prentice-Hall- automatic-computation/ 20140529 Algoritmos e Estruturas de Dados 6
  7. 7. Coursera Algorithms, Part I •  Instructors: Robert Sedgewick, Kevin Wayne. •  “This course covers the essential information that every serious programmer needs to know about algorithms and data structures, with emphasis on applications and scientific performance analysis of Java implementations. Part I covers (…) union-find algorithms; basic iterable data types (stack, queues, and bags); sorting algorithms (quicksort, mergesort, heapsort) and applications; priority queues; binary search trees; red-black trees; hash tables; and symbol-table applications.” 20140529 Algoritmos e Estruturas de Dados 7 https://www.coursera.org/course/algs4partI
  8. 8. Mais Coursera Algorithms, Part II •  “Part II covers graph-processing algorithms, including minimum spanning tree and shortest paths algorithms, and string processing algorithms, including string sorts, tries, substring search, regular expressions, and data compression, and concludes with an overview placing the contents of the course in a larger context.” 20140529 Algoritmos e Estruturas de Dados 8 https://www.coursera.org/course/algs4partII
  9. 9. Programa de AED •  Conceitos fundamentais •  Modelo de programação •  Sacos, pilhas e filas •  “Union-Find” •  Análise de algoritmos •  Ordenação •  Algoritmos elementares •  Mergesort, quicksort •  Filas com prioridade •  Busca •  Árvores binárias de busca •  Árvores equilibradas •  Tabelas de dispersão •  Grafos •  Busca em profundidade •  Busca em largura •  Árvores de cobertura •  Caminho mais curto •  Cadeias de carateres •  Busca de subcadeias •  Compressão de dados •  Estratégias programativas •  Divisão-conquista •  Algoritmos gananciosos •  Programação dinâmica •  Intratabilidade 20140529 Algoritmos e Estruturas de Dados 9
  10. 10. Algoritmos de programação •  Busca linear •  Busca dicotómica •  Bubblesort •  Quicksort •  Conversão numérica •  Reversão numérica •  Máximo, mínimo de um array •  Remoção de duplicados •  Comparação lexicográfica de arrays 20140529 Algoritmos e Estruturas de Dados 10
  11. 11. Algoritmos no ensino secundário •  Fatorização •  Máximo divisor comum •  Simplificação de frações •  Regra de Ruffini •  Método de Gauss 20140529 Algoritmos e Estruturas de Dados 11
  12. 12. Algoritmos da escola primária •  Adição •  Subtração •  Multiplicação •  Divisão •  (Sucessor) 20140529 Algoritmos e Estruturas de Dados 12
  13. 13. Algoritmos clássicos •  Algoritmo de Euclides. •  Método para o cálculo de π, de Arquimedes. •  Crivo de Eratóstenes. 20140529 Algoritmos e Estruturas de Dados 13
  14. 14. Algoritmo de Euclides •  Calcula o máximo divisor comum de dois números inteiros positivos. •  É o mais antigo algoritmo inventado pelo espírito humano. 20140529 Algoritmos e Estruturas de Dados 14 public static int euclidAlgorithm(int x, int y) { while (x != y) if (x < y) y -= x; else x -= y; return x; } Isto será um método estático de alguma classe, em Java.
  15. 15. Algoritmo de Euclides, variantes recursivas •  Formulação recursiva •  Formulação recursiva, curto-circuito: 20140529 Algoritmos e Estruturas de Dados 15 public static int euclid(int x, int y) { return x < y ? euclid(x, y-x) : x > y ? euclid(x-y, y) : x; } public static int greatestCommonDivisor(int x, int y) { int result = x; if (y != 0) result = greatestCommonDivisor(y, x % y); return result; } Esta versão substitui uma sequência de subtrações x -= y até x ser menor que y por x % y.
  16. 16. Algoritmo de Euclides, versão de combate •  Normalmente, usamos a seguinte variante iterativa: 20140529 Algoritmos e Estruturas de Dados 16 public static int gcd(int x, int y) { while (y != 0) { int r = x % y; x = y; y = r; } return x; } Tenha esta sempre à mão!
  17. 17. Comparando as versões iterativas •  Eis uma função de teste para comparar na consolas duas versões iterativas: 20140529 Algoritmos e Estruturas de Dados 17 public static void testIterativeVersions() { while (!StdIn.isEmpty()) { int x = StdIn.readInt(); int y = StdIn.readInt(); int z1 = euclidAlgorithm(x, y); StdOut.println(z1); int z2 = gcd(x, y); StdOut.println(z2); } } Lemos e escrevemos usando a biblioteca stdlib.jar, que teremos juntado ao projeto no Eclipse, importando-a e acrescentando-a ao build path.
  18. 18. Classes StdIn e StdOut •  Usaremos a classe StdIn para ler e a classe StdOut para escrever na consola. 20140529 Algoritmos e Estruturas de Dados 18 StdIn StdOut
  19. 19. A função main •  Em cada classe, a função main apenas chamará as funções de teste. •  Por enquanto, só temos uma função de teste: •  A função main e todas as outras formam a classe Euclid, que teremos desenvolvido no Eclipse. 20140529 Algoritmos e Estruturas de Dados 19 public static void main(String[] args) { testIterativeVersions(); }
  20. 20. Classe Euclid 20140529 Algoritmos e Estruturas de Dados 20 public final class Euclid { public static int euclid(int x, int y) { return x < y ? euclid(x, y - x) : x > y ? euclid(x - y, y) : x; } // ... public static void testIterativeVersions() { while (!StdIn.isEmpty()) { int x = StdIn.readInt(); int y = StdIn.readInt(); int z1 = euclidAlgorithm(x, y); StdOut.println(z1); int z2 = gcd(x, y); StdOut.println(z2); } } public static void main(String[] args) { testIterativeVersions(); } }
  21. 21. Correndo no Eclipse 20140529 Algoritmos e Estruturas de Dados 21 Correndo no Eclipse, a interação dá-se na consola do Eclipse, terminando com ctrl-z (Windows) ou ctrl-d (Mac ou Linux).
  22. 22. Correndo na linha de comando •  Para correr na linha de comando, colocamo-nos da diretoria bin, dentro do projecto, no workspace. •  Aí damos o comando •  Observe: 20140529 Algoritmos e Estruturas de Dados 22 java –cp ../stdlib.jar:. Euclid Note que estamos “dentro” da diretoria bin e que a biblioteca stdlib.jar está “ao lado” da diretoria bin.
  23. 23. Testando as funções todas •  Eis uma segunda função de teste, para comparar os resultados das quatro funções: 20140529 Algoritmos e Estruturas de Dados 23 public static void testAllVersions() { while (!StdIn.isEmpty()) { int x = StdIn.readInt(); int y = StdIn.readInt(); int z1 = euclid(x, y); int z2 = euclidAlgorithm(x, y); int z3 = greatestCommonDivisor(x, y); int z4 = gcd(x, y); StdOut.printf("%d %d %d %dn", z1, z2, z3, z4); } }
  24. 24. Argumentos na linha de comando •  Selecionamos a função de teste por meio de um argumento na linha de comando: 20140529 Algoritmos e Estruturas de Dados 24 public static void main(String[] args) { int choice = 1; if (args.length > 0) choice = Integer.parseInt(args[0]); switch (choice) { case 1: testIterativeVersions(); break; case 2: testAllVersions(); break; default: break; } }
  25. 25. Algoritmos e Estruturas de Dados Lição n.º 2 Animação algorítmica
  26. 26. Animação algorítmica •  Visualização dos algoritmos •  Utilização do Processing •  Recapitulação •  Integração com o Java •  Animação do algoritmo de Euclides 20140529 Algoritmos e Estruturas de Dados 26
  27. 27. Animação •  Os algoritmos são pensamento puro. •  Devemos compreendê-los abstratamente. •  Mas é divertido vê-los a mexer. •  Por exemplo, eis um site com animações muito interessantes de algoritmos de ordenação: http://www.sorting-algorithms.com/. •  De que precisamos para programarmos nós próprios animações destas? •  Precisamos de uma biblioteca gráfica, neste caso uma biblioteca gráfica para o Java. 20140529 Algoritmos e Estruturas de Dados 27
  28. 28. Processing •  O Processing é uma linguagem de programação que vem com o seu próprio ambiente de desenvolvimento. •  De certa forma, o Processing é uma “camada” sobre o Java, que pretende ocultar certas particularidades mais “difíceis”. •  Na verdade, o que nos interessa agora é que o Processing traz uma grande biblioteca gráfica, bem documentada, que podemos usar nos nossos programas Java. 20140529 Algoritmos e Estruturas de Dados 28
  29. 29. Recapitulando... •  Em Processing, um programa é constituído por algumas declarações globais e pelas funções setup e draw. •  Ambas são chamadas automaticamente. •  A função setup é chamada uma vez, no início do programa. •  A função draw é chamada repetidamente, 60 vezes por segundo. 20140529 Algoritmos e Estruturas de Dados 29 O número de vezes que a função draw é chamada por segundo pode ser mudado, com a função frameRate().
  30. 30. setup e draw 20140529 Algoritmos e Estruturas de Dados 30
  31. 31. Java com Processing 20140529 Algoritmos e Estruturas de Dados 31 import processing.core.PApplet; public class TShirtSketch extends PApplet { private static final long serialVersionUID = 1L; private int displayWidth = 400; private int displayHeight = 300; public void setup() { size(displayWidth, displayHeight); colorMode(HSB, 26); noCursor(); } public void draw() { background(color(key & 31, 26, 26)); } } Teremos acrescentado ao projeto a biblioteca core.jar do Processing.
  32. 32. Usando da linha de comando •  Para corrermos o sketch a partir da linha de comando, acrescentamos a função main: 20140529 Algoritmos e Estruturas de Dados 32 public class TShirtSketch extends PApplet { // ... public void setup() { // ... } public void draw() { // ... } public static void main(String[] args) { PApplet.main(new String[] {TShirtSketch.class.getName()}); } }
  33. 33. Animando o algoritmo de Euclides •  Queremos representar cada variável, x e y, por uma barra vertical, com altura proporcional ao valor da variável. •  A cada passo do ciclo while atualizamos o desenho, por exemplo, segundo a segundo (ou de acordo com um período pré-estabelecido). 20140529 Algoritmos e Estruturas de Dados 33 public static int euclidAlgorithm(int x, int y) { while (x != y) if (x < y) y -= x; else x -= y; return x; }
  34. 34. Animação diferida •  Em vez de ir calculando cada novo valor segundo a segundo, é mais prático calcular tudo logo, guardar os valores num array e ir buscá-los sincronizadamente. •  Sendo assim, os valores a mostrar graficamente são função do tempo decorrido desde o início. •  Vejamos como fazer isso, numa classe EuclidGraphical. •  Esta terá um método start, chamado por setup, para inicializar as operações, e um método display, chamado por draw, para desenhar em função do tempo decorrido. 20140529 Algoritmos e Estruturas de Dados 34
  35. 35. Parametrização geral •  Parametrizamos a largura das barras, a margem esquerda (que é igual à margem direita, o intervalo entre as barras, e o espaço livre em cima, no início, e ainda o período. •  Com estes valores e com os valores dos argumento da função, calculamos o tamanho da janela. 20140529 Algoritmos e Estruturas de Dados 35 import processing.core.PApplet; public class EuclidGraphical { private int x; private int y; private static final int BAR_WIDTH = 40; private static final int MARGIN = 40; private static final int GAP = 20; private static final int TOP = 20; private static final int PERIOD = 1000; // ... } Assim evitamos “números mágicos” no nosso programa.
  36. 36. Gravando os valores •  Precalculamos os sucessivos valores de x e y, guardando- os em dois arrays paralelos, por meio de uma função que será chamada pela função start. •  Previamente, os valores de x e y terão sido fixados, também pela função start. 20140529 Algoritmos e Estruturas de Dados 36 public class EuclidGraphical { // ... private int[] xx; private int[] yy; private int size; private void precompute() { int a = x; int b = y; int capacity = Math.max(x, y); xx = new int [capacity]; yy = new int [capacity]; size = 0; while (a != b) { xx[size] = a; yy[size] = b; size++; if (a < b) b -= a; else a -= b; } xx[size] = a; yy[size] = b; size++; }
  37. 37. Construção •  O objeto sketch cria um objeto EuclidGraphical, o qual guarda uma referência para o seu “pai”: 20140529 Algoritmos e Estruturas de Dados 37 public class EuclidSketch extends PApplet { private static final long serialVersionUID = 1L; private EuclidGraphical euclid = new EuclidGraphical(this); // ... } public class EuclidGraphical { // ... private PApplet parent; public EuclidGraphical(PApplet p) { parent = p; } // ... }
  38. 38. A função setup •  A função setup lê os dois números da consola, invoca start no objeto gráfico, dimensiona a janela e marca o início do tempo: 20140529 Algoritmos e Estruturas de Dados 38 public class EuclidSketch extends PApplet { // ... private int millis0; public void setup() { StdOut.print("Two numbers, please: "); int x = StdIn.readInt(); int y = StdIn.readInt(); euclid.start(x, y); size(euclid.width(), euclid.height()); millis0 = millis(); } // ...
  39. 39. As dimensões da janela •  As dimensões da janela são calculadas pelo objeto gráfico: 20140529 Algoritmos e Estruturas de Dados 39 public class EuclidGraphical { // ... public int width() { return 2 * MARGIN + 2 * BAR_WIDTH + GAP; } public int height() { return Math.max(x, y) + TOP; } // ... }
  40. 40. O método start •  O método start fixa os valores dos números, manda precalcular e determina as cores das barras: 20140529 Algoritmos e Estruturas de Dados 40 public class EuclidGraphical { // ... private int colorX; private int colorY; public void start(int x0, int y0) { x = x0; y = y0; precompute(); colorX = parent.color(0, 0, 255); // BLUE colorY = parent.color(255, 0, 0); // RED } // ... } NB: nos programa Java as cores são representadas pelo tipo int, e não pelo tipo color do Processing.
  41. 41. A função draw •  A função draw é chamada automaticamente, com a frequência determinada pela “frame rate”. •  De cada vez, desenha tudo: primeiro o background, diretamente, depois as barras, invocando o método display, que tem como argumento o tempo: 20140529 Algoritmos e Estruturas de Dados 41 public class EuclidSketch extends PApplet { // ... public void draw() { background(100); int t = millis() - millis0; euclid.display(t); } // … }
  42. 42. O método display •  O método display é cíclico: quando chega ao fim, recomeça. •  De cada vez, determina quais os valores que devem ser mostrados, em função do tempo decorrido: 20140529 Algoritmos e Estruturas de Dados 42 public class EuclidGraphical { // ... public void display(int t) { int r = (t / PERIOD) % size; displayX(xx[r]); displayY(yy[r]); } } private void displayX(int h) { parent.stroke(colorX); parent.fill(colorX); parent.rect(MARGIN, parent.height-h, BAR_WIDTH, h); } private void displayY(int h) { parent.stroke(colorY); parent.fill(colorY); parent.rect(MARGIN + BAR_WIDTH + GAP, parent.height-h, BAR_WIDTH, h); }
  43. 43. Correndo na linha de comandos •  Para poder correr na linha de comandos, temos a função main: •  Neste caso, há duas bibliotecas: 20140529 Algoritmos e Estruturas de Dados 43 public class EuclidSketch extends PApplet { // ... public static void main(String[] args) { PApplet.main(new String[] {EuclidSketch.class.getName()}); } }
  44. 44. Variantes •  Fazer uma cópia da barra mais baixa deslocar-se suavemente para sobre a barra mais alta, antes de esta baixar. •  Desafios: •  Animar o algoritmo de Arquimedes. •  Animar o algoritmo de Newton para a raiz quadrada. 20140529 Algoritmos e Estruturas de Dados 44
  45. 45. Algoritmos e Estruturas de Dados Lição n.º 3 Coleções: sacos, pilhas e filas
  46. 46. Coleções: sacos, pilhas e filas •  Especificação das coleções •  Classes iteráveis •  Sacos (em inglês bags) •  Implementação com arrays •  Pilhas (em inglês stacks) •  Implementação com listas •  Filas (em inglês queues) •  Implementação como exercício. 20140529 Algoritmos e Estruturas de Dados 46 Estudaremos hoje os sacos.As pilhas e as filhas ficam para a próxima lição.
  47. 47. Coleções •  Coleções são... coleções! •  Às coleções podemos acrescentar objetos, removê- los e “iterá-los”. •  Podemos ainda “perguntar-lhes” se estão vazias e quantos objetos têm. •  Em Java teremos coleções genéricas, parametrizadas pelo tipo dos objetos que contêm. Por exemplo: 20140529 Algoritmos e Estruturas de Dados 47 public class Bag<T> { // ... } As coleções não inspecionam os objetos que contêm, apenas os guardam, e por isso não há restrições sobre o tipo de objetos que podem conter.
  48. 48. Classes iteráveis •  Queremos que as nossas coleções sejam iteráveis, isto é, que permitam processar sequencialmente cada um dos objetos da coleção. •  Programaremos isso fazendo cada coleção dispor de um método iterator que devolve um iterador. •  Paralelamente, declaramos a classe como implementando a interface Iterable<T>: 20140529 Algoritmos e Estruturas de Dados 48 public class Bag<T> implements Iterable<T> { // ... }
  49. 49. APIs 20140529 Algoritmos e Estruturas de Dados 49 public class Bag<T> implements Iterable<T> { public Bag() public void add(T x) public boolean isEmpty() public int size() public Iterator<T> iterator() } public class Stack<T> implements Iterable<T> { public Stack() public void push(T x) public boolean isEmpty() public int size() public T pop() public Iterator<T> iterator() } public class Queue<T> implements Iterable<T> { public Queue() public void enqueue(T x) public boolean isEmpty() public int size( public T dequeue() public Iterator<T> iterator() }
  50. 50. Capacidade? •  Como indicamos a capacidade das coleções? •  Não indicamos! Há apenas um construtor, sem argumentos. •  Logo, as coleções têm de ser capazes de aumentar a capacidade, se necessário (e, porventura, de diminuir a capacidade, se desejável). •  Para isso, implementamo-las à base de listas ligadas ou à base de arrays redimensionáveis. 20140529 Algoritmos e Estruturas de Dados 50 Um array redimensionável é um array cuja capacidade pode mudar durante a execução do programa. Aparentemente a expressão “array redimensionável” é um oximoro, pois habituámo-nos a que a capacidade de um array é fixada na sua criação e não muda mais.
  51. 51. Sacos, implementação •  Vamos implementar a classe Bag usando arrays redimensionáveis. •  Haverá um campo items, que é o array, e um campo size, que representa o número de elementos presentes no array: 20140529 Algoritmos e Estruturas de Dados 51 public class Bag<T> implements Iterable<T> { private T[] items; private int size; // ... } Recorde que a capacidade do array está registada no membro length do array.
  52. 52. Sacos, construtor •  Inicialmente, o array terá capacidade 1 e tamanho 0: 20140529 Algoritmos e Estruturas de Dados 52 public class Bag<T> implements Iterable<T> { private T[] items; private int size; public Bag() { items = (T[]) new Object[1]; size = 0; } // ... } Note bem: criamos um array de objetos, e forçamos a conversão para array de T.
  53. 53. Questão técnica: arrays genéricos •  Nós gostaríamos de ter programado a criação do array genérico items assim: •  No entanto, por razões técnicas, aceitáveis mas complicadas, o Java não permite a criação de arrays genéricos. •  Remedeia-se criando um array de objetos Object e forçando a conversão para o tipo de arrays pretendido. •  Em geral, forçar conversões de tipo é mau estilo. 20140529 Algoritmos e Estruturas de Dados 53 items = new T[1];
  54. 54. Supressão de avisos •  Como, forçar conversões é uma operação arriscada, o Java emite um warning: •  Evitamos o warning com uma anotação: 20140529 Algoritmos e Estruturas de Dados 54 @SuppressWarnings("unchecked") public Bag() { items = (T[]) new Object[1]; size = 0; } Só usaremos o SuppressWarnings neste caso da construção de arrays genéricos!
  55. 55. Redimensionamento •  Na verdade, não redimensionamos o array: criamos outro array com a capacidade pretendida e fazemos a com que a variável que apontava para o array original passe a apontar para o array novo: 20140529 Algoritmos e Estruturas de Dados 55 private void resize(int capacity) { @SuppressWarnings("unchecked") T[] temp = (T[]) new Object[capacity]; for (int i = 0; i < size; i++) temp[i] = items[i]; items = temp; } A memória do array original fica inacessível e será libertada automaticamente pelo garbage collector dentro em pouco. Atenção: isto presume que size <= capacity.
  56. 56. Método add •  Se, ao acrescentar um elemento ao saco, notarmos que já não há espaço no array, redimensionamo-lo para o dobro: 20140529 Algoritmos e Estruturas de Dados 56 public void add(T x) { if (size == items.length) resize(2 * items.length); items[size++] = x; }
  57. 57. Métodos size e isEmpty •  Estes são muito simples: 20140529 Algoritmos e Estruturas de Dados 57 public int size() { return size; } public boolean isEmpty() { return size == 0; }
  58. 58. O iterador •  Optamos por iterar pela ordem de entrada. •  Usamos uma classe interna: 20140529 Algoritmos e Estruturas de Dados 58 public class Bag<T> implements Iterable<T> { // ... private class BagIterator implements Iterator<T> { private int i = 0; public boolean hasNext() { return i < size; } public T next() { return items[i++]; } public void remove() { throw new UnsupportedOperationException(); } } } O método remove não é suportado.
  59. 59. O método iterator •  O método iterator apenas cria e retorna um objeto de tipo BagIterator: •  Mais tarde, para, por exemplo, mostrar o conteúdo de saco de inteiros, b, faremos: 20140529 Algoritmos e Estruturas de Dados 59 public Iterator<T> iterator() { return new BagIterator(); } for (int x: b) StdOut.println(x);
  60. 60. Exemplo: problema dos estádios •  A federação tem a lista dos estádios, com nome, comprimento do relvado e largura do relvado. Pretende saber quais são os estádios com relvado de área máxima, de entre aqueles cujo relvado tem as dimensões regulamentares. •  Para a FIFA, o comprimento do relvado deve estar entre 90 e 120 metros e a largura entre 45 e 90 metros. •  Cada linha do ficheiro tem o nome (um cadeia de carateres sem espaços) o comprimento e a largura (ambos números inteiros). 20140529 Algoritmos e Estruturas de Dados 60 Variante do problema usado em Laboratório de Programação 13/14.drag 110 85 alg_arv 124 90 light 120 80 saintlouis 118 82 alv_xxi 115 80
  61. 61. Classe Stadium •  Uma classe imutável, para representar estádios: 20140529 Algoritmos e Estruturas de Dados 61 public class Stadium { private final String name; private final int length; private final int width; public static final int MIN_LENGTH = 90; public static final int MAX_LENGTH = 120; public static final int MIN_WIDTH = 45; public static final int MAX_WIDTH = 90; Stadium(String s, int u, int w) { name = s; length = u; width = w; } // ... } Note bem: por opção de desenho, os valores destes membros não podem mudar após a construção.
  62. 62. Classe Stadium, continuação 20140529 Algoritmos e Estruturas de Dados 62 public class Stadium { // ... public int area() { return length * width; } public boolean isLegal() { return MIN_LENGTH <= length && length <= MAX_LENGTH && MIN_WIDTH <= width && width <= MAX_WIDTH; } public static Stadium read() { String s = StdIn.readString(); int u = StdIn.readInt(); int w = StdIn.readInt(); return new Stadium(s, u, w); } public String toString() { return name + " " + length + " " + width; } } Note bem: método estático. Note bem: método redefinido.
  63. 63. Classe do problema •  Usamos um saco de estádios. •  O saco é criado vazio (como todos os sacos) e depois é preenchido por leitura: 20140529 Algoritmos e Estruturas de Dados 63 public class StadiumProblem { private Bag<Stadium> stadiums = new Bag<Stadium>(); public void read() { while (!StdIn.isEmpty()) stadiums.add(Stadium.read()); } // ... }
  64. 64. Estádios selecionados •  Os estádios selecionados também formam um saco: 20140529 Algoritmos e Estruturas de Dados 64 public class StadiumProblem { // ... public Bag<Stadium> selected() { Bag<Stadium> result = new Bag<Stadium>(); int maxArea = -1; for (Stadium x: stadiums) if (x.isLegal()) { if (maxArea < x.area()) { maxArea = x.area(); result = new Bag<Stadium>(); } if (x.area() == maxArea) result.add(x); } return result; } // ... } De cada vez que surge uma nova área máxima, recomeça-se com um novo saco.
  65. 65. Método solve •  Resolvemos o problema no método solve: •  A função main cria um problema e invoca o método solve: 20140529 Algoritmos e Estruturas de Dados 65 public void solve() { read(); Bag<Stadium> solution = selected(); if (solution.isEmpty()) StdOut.println("(empty)"); else for (Stadium x : selected()) StdOut.println(x); } public static void main(String [] args) { new StadiumProblem().solve(); }
  66. 66. Diretoria work •  Tipicamente, guardaremos os nosso ficheiros de dados numa diretoria work, dentro do projeto, ao lado das diretorias bin e src (criadas pelo Eclipse). •  Nesse caso, podemos correr o programa a partir da diretoria work, redirigindo o input, assim, por exemplo: 20140529 Algoritmos e Estruturas de Dados 66 Também podemos redirigir o output, de maneira análoga, claro.
  67. 67. Custo do redimensionamento? •  Redimensionar o array que suporta o saco parece ser um grande trabalho suplementar. Será que é? •  Quando redimensionamos de N para 2N, quantos acessos se fazem aos arrays items e temp? •  O array temp é criado com 2N elementos e cada um deles tem de ser inicializado com null. Logo 2N acessos. •  A afectação temp[i] = items[i] faz-se N vezes. Logo, ao todo, mais 2N acessos. •  Portanto, ao redimensionar de 1 para 2, foram 4 acessos; de 2 para 4 foram 8 acessos; ...; de N/2 para N foram 2N acessos. •  Portanto, se o array tiver N elemento e N for uma potência de 2, teremos usado 4 + 8 + 16 + ... + 2N acessos só para o redimensionamento. •  Ora 4 + 8 + 16 + ... + 2N = 4N – 4. •  A estes há que somar os N acessos normais, para acrescentar no novo elemento ao saco. •  Logo, ao todo teremos tido 5N – 4 acessos, para N elementos no array. •  Note que se tivéssemos alocado logo N elementos na criação, teria havido 2N acessos ao array, ao todo, mas em geral não sabemos o número de elementos exato e portanto temos de dimensionar por excesso. 20140529 Algoritmos e Estruturas de Dados 67
  68. 68. Exercício •  Dispomos de um ficheiro com as cotações na bolsa de Lisboa. •  Em cada linha vem o nome do “papel”, a cotação no início da sessão, o mínimo da sessão, o máximo da sessão, a cotação corrente e o número de transações desse papel. •  Queremos saber quais os papéis cuja variação (corrente menos inicial sobre inicial) é positiva e maior ou igual à média das variações e qual é a valorização total desses papéis. O valorização de cada papel é o volume vezes a diferença entre a cotação inicial e a cotação corrente. •  No ficheiro de saída vêm os nomes dos papéis cuja variação é é positiva e a maior que a média, por ordem alfabética, depois a variação média destes papéis e finalmente valorização total também destes papéis. •  Requisitos técnicos: guarde todas as cotações num saco; ordene os nomes dos papéis usando a função Arrays.sort(). •  Veja o problema aqui. 20140529 Algoritmos e Estruturas de Dados 68
  69. 69. Algoritmos e Estruturas de Dados Lição n.º 4 Pilhas
  70. 70. Pilhas •  Implementação com arrays redimensionáveis. •  Implementação com listas ligadas. •  Aplicação: avaliação de expressões aritméticas usando o algoritmo da pilha dupla, de Dijkstra. 20140529 Algoritmos e Estruturas de Dados 70
  71. 71. Pilhas •  As pilhas chamam-se pilhas porque funcionam como pilhas...; pilhas de livros, de pratos, de papéis, não pilhas no sentido de baterias elétricas! •  A ideia é que o elemento que sai é o último que entrou. •  Dizemos que as pilhas são coleções LIFO: last in first out. 20140529 Algoritmos e Estruturas de Dados 71
  72. 72. API da pilha •  Já sabemos: •  Comparando com Bag<T>, temos push em vez de add, temos pop e o iterador emitirá os elementos por ordem inversa de entrada na pilha (os que entraram mais recentemente surgem primeiro). 20140529 Algoritmos e Estruturas de Dados 72 public class Stack<T> implements Iterable<T> { public Stack() public void push(T x) public boolean isEmpty() public int size() public T pop() public Iterator<T> iterator() }
  73. 73. Implementação com arrays redimensionáveis •  É análoga à da classe Bag<T>: 20140529 Algoritmos e Estruturas de Dados 73 public class Stack<T> implements Iterable<T> { private T[] items; private int size; public Stack() { // ... } private void resize(int capacity) { // ... } public void push(T x) { if (size == items.length) resize(2 * items.length); items[size++] = x; } // ... Se o array cresce quando está cheio, será que deve encolher quando fica relativamente vazio?
  74. 74. Encolhendo no pop •  A estratégia será encolher para metade quando o tamanho estiver a 25% da capacidade: 20140529 Algoritmos e Estruturas de Dados 74 public class Stack<T> implements Iterable<T> { // ... public T pop() { T result = items[--size]; items[size] = null; if (size > 0 && size == items.length / 4) resize(items.length / 2); return result; } // ... Anula-se o apontador para que não permaneça no array uma referência para o elemento removido, o que atrasaria a garbage collection. Se não anulássemos, teríamos “vadiagem” (em inglês, loitering): referências no array para valores inúteis. Note bem: não seria sensato encolher para metade quando o array estivesse meio cheio, pois nesse caso ele ficava cheio logo após ter encolhido, e se houvesse um push a seguir, teria de crescer de novo.
  75. 75. Iterador reverso •  Nas pilhas, queremos iterar os elementos de maneira a ver primeiro os que entraram na pilha mais recentemente: 20140529 Algoritmos e Estruturas de Dados 75 // ... private class StackIterator implements Iterator<T> { private int i = size; public boolean hasNext() { return i > 0; } public T next() { return items[--i]; } // ... } public Iterator<T> iterator() { return new StackIterator(); } // ...
  76. 76. Função de teste •  Eis uma função de teste, que empilha os números lidos e desempilha com ‘-’. No fim mostra o tamanho, se é vazia e os elementos, por iteração: 20140529 Algoritmos e Estruturas de Dados 76 private static void testStackInteger() { Stack<Integer> s = new Stack<Integer>(); while (!StdIn.isEmpty()) { String t = StdIn.readString(); if (!"-".equals(t)) s.push(Integer.parseInt(t)); else if (!s.isEmpty()) { int x = s.pop(); StdOut.println(x); } } StdOut.println("size of stack = " + s.size()); StdOut.println("is stack empty? " + s.isEmpty()); StdOut.print("items:"); for (int x : s) StdOut.print(" " + x); StdOut.println(); } Ctrl-D aqui!
  77. 77. Implementação com listas ligadas •  A lista ligada é programada em termos da classe interna Node, a qual detém um campo para o valor e uma referência para o próximo nó: 20140529 Algoritmos e Estruturas de Dados 77 public class StackLists<T> implements Iterable<T> { private class Node { private T value; private Node next; Node(T x, Node z) { value = x; next = z; } } // ... Ver transparentes de POO, página 13-8 e seguintes.
  78. 78. O primeiro nó •  Na pilha, um dos membros é o primeiro nó da lista; o outro é o tamanho: •  Omitimos o construtor por defeito, uma vez que os valores iniciais automáticos (null para first e 0 para size) são os que nos interessam. 20140529 Algoritmos e Estruturas de Dados 78 public class StackLists<T> implements Iterable<T> { // ... private Node first; private int size; // ...
  79. 79. Empilhar, desempilhar •  Para empilhar, criamos um novo nó, que passa a ser o primeiro, ligando-o ao que estava em primeiro antes: •  Para desempilhar, devolvemos o valor do primeiro nó, e avançamos para o seguinte a referência: 20140529 Algoritmos e Estruturas de Dados 79 public void push(T x) { first = new Node(x, first); size++; } public T pop() { T result = first.value; first = first.next; size--; return result; }
  80. 80. isEmpty, size •  Os métodos isEmpty e size são muito simples: 20140529 Algoritmos e Estruturas de Dados 80 public boolean isEmpty() { return first == null; } public int size() { return size; } Poderíamos ter omitido o campo size e ter programado a função size contando iterativamente os nós, mas, por decisão de desenho preferimos que função size seja calculada em tempo constante.
  81. 81. Iterador de listas •  O cursor é uma referência para o nó corrente: 20140529 Algoritmos e Estruturas de Dados 81 private class StackIterator implements Iterator<T> { private Node cursor = first; public boolean hasNext() { return cursor != null; } public T next() { T result = cursor.value; cursor = cursor.next; return result; } // ... } public Iterator<T> iterator() { return new StackIterator(); } Repare que o nó first contém o valor que mais recentemente foi empilhado, de entre todos os que estão na pilha. Tal como na outra implementação, os valores são enumerados pela ordem inversa de entrada na pilha.
  82. 82. Avaliação de expressões aritméticas •  Como exemplo de aplicação das listas, estudemos a avaliação de expressões aritméticas usando o algoritmo da pilha dupla de Dijkstra. •  As expressões aritmética que nos interessam são completamente parentetizadas, para evitar as questões da precedência dos operadores. •  Admitiremos os quatro operadores básicos (“+”,“-”,“*” e “/”) e a raiz quadrada (“sqrt”). •  Os operandos são números decimais, com ou sem parte decimal. •  Exemplos: ((7*2)+5) ((sqrt(9+16))/3) (((6*3)+(7-3))/2) 20140529 Algoritmos e Estruturas de Dados 82
  83. 83. Pilha dupla de Dijkstra •  Usamos duas pilhas: uma pilha de cadeias, para os operadores, e uma pilha de números, para os operandos e para os resultados parciais. •  Varremos a expressão da esquerda para a direita, recolhendo os operadores, os operandos e os parênteses: •  Cada operador é empilhado na pilha dos operandos. •  Cada operando é empilhado na pilha dos números. •  Os parênteses a abrir são ignorados. •  Ao encontrar um parêntesis a fechar, desempilha-se um operador, depois desempilham-se os operandos (em número apropriado ao operador), aplica-se a operação a esses operando e empilha-se o resultado, na pilha dos números. •  Depois de o último parêntesis ter sido processado, a pilha dos números terá um único elemento, cujo valor é o valor da expressão. 20140529 Algoritmos e Estruturas de Dados 83
  84. 84. Classe DijkstraTwoStack •  Primeiro as operações de suporte: 20140529 Algoritmos e Estruturas de Dados 84 public final class DijkstraTwoStack { private DijkstraTwoStack() { } private static String[] knownOperators = {"+", "-", "*", "/", "sqrt"}; private static String[] binaryOperators = {"+", "-", "*", "/"}; private static String[] unaryOperators = {"sqrt"}; private static <T> boolean has(T[] a, T x) { for (T i : a) if (x.equals(i)) return true; return false; } private static boolean isOperator(String s) { return has(knownOperators, s); } private static boolean isBinaryOperator(String s) { return has(binaryOperators, s); } private static boolean isNumeric(String s) { return Character.isDigit(s.charAt(0)); } // ... Busca linear.
  85. 85. Avaliação da expressão •  A seguinte função meramente exprime as regras do algoritmo: 20140529 Algoritmos e Estruturas de Dados 85 private static double evaluationSimple(String[] ss) { Stack<String> operators = new Stack<String>(); Stack<Double> values = new Stack<Double>(); for (String s : ss) if ("(".equals(s)) { } // nothing to do else if (isOperator(s)) operators.push(s); else if (isNumeric(s)) values.push(Double.parseDouble(s)); else if (")".equals(s)) { String op = operators.pop(); double z; // ... values.push(z); } else throw new UnsupportedOperationException(); return values.pop(); } A expressão é repre- sentada pelo array dos tóquenes: operandos, operadores e parêntesis.
  86. 86. Aplicação dos operadores 20140529 Algoritmos e Estruturas de Dados 86 { String op = operators.pop(); // StdOut.println(op); double y = values.pop(); // StdOut.println(y); double x = isBinaryOperator(op) ? values.pop() : 0.0; // StdOut.println(x); double z; if ("+".equals(op)) z = x + y; else if ("-".equals(op)) z = x - y; else if ("-".equals(op)) z = x + y; else if ("*".equals(op)) z = x * y; else if ("/".equals(op)) z = x / y; else if ("sqrt".equals(op)) z = Math.sqrt(y); else throw new UnsupportedOperationException(); // will not happen; values.push(z); } Comprido e desinteressante.
  87. 87. Função de teste •  Para abreviar, determinamos que os tóquenes na expressão vêm separados por espaços. •  Assim, podemos obter o array de tóquenes diretamente, usando a função split: 20140529 Algoritmos e Estruturas de Dados 87 public static void testEvaluationSimple() { while (StdIn.hasNextLine()) { String line = StdIn.readLine(); double z = evaluationSimple(line.split(" ")); StdOut.println(z); } } split public String[] split(String regex) Splits this string around matches of the given regular expression. O método split, da classe String, devolve um array de String.
  88. 88. Experiência •  Na linha de comando: •  Próximas tarefas: •  Evitar ter de separar os tóquenes por espaços. •  Evitar ter de distinguir os operadores com if-else em cascata 20140529 Algoritmos e Estruturas de Dados 88 pedros-­‐imac-­‐4:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  DijkstraTwoStack   (  8  +  1  )   9.0   (  sqrt  2  )   1.4142135623730951   (  (  6  +  9  )  /  3  )   5.0   (  (  5  +  1  )  /  (  7  +  17  )  )   0.25   (  sqrt  (  1  +  (  sqrt  (  1  +  (  sqrt  (  1  +  (  sqrt  (  1  +  1  )  )  )  )  )  )  )  )   1.6118477541252516    
  89. 89. Algoritmos e Estruturas de Dados Lição n.º 5 Expressões regulares
  90. 90. Expressões regulares •  Toquenização de cadeias de caracteres. •  Expressões regulares. 20140529 Algoritmos e Estruturas de Dados 90 Esta aula antecipa parte da matéria de sobre cadeias de carateres, à qual voltaremos mais tarde.
  91. 91. Problema •  O nosso problema é obter a sequência de tóquenes de uma expressão aritmética, para depois poder avaliar a expressão. •  Por exemplo, em sqrt(3.0*3.0+4.0*4.0)*2, os tóquenes são “sqrt”,“(”,“3.0”,“*”,“3.0”,“+”,“4.0”,“*”,“4.0”,“)”,“*”, “2”. •  Quer dizer, um tóquene é ou um identificador ou um número ou um operador ou um parêntesis a abrir ou um parêntesis a fechar. •  Os espaços são apenas separadores, sem outro valor. •  Há números inteiros e números com parte decimal. •  No nosso caso, um operador é formado por um único caractere, que não é nem uma letra nem um algarismo. 20140529 Algoritmos e Estruturas de Dados 91
  92. 92. Classe Tokenization •  Programemos uma classe para fazer a toquenização que nos convém. •  Precisamos de uma função para cada uma das qualidades de tóquenes “compridos”: números e identificadores: 20140529 Algoritmos e Estruturas de Dados 92 public final class Tokenization { private Tokenization() { } //do not instantiate this class public static int findNumber(String s, int start) { ... } public static int findIdentifier(String s, int start) { ... } // ... } Cada função retorna o comprimento do tóquene da qualidade respetiva que começa na posição start da cadeia s. Não confundir com a antiga classe de biblioteca StringTokenizer.
  93. 93. Encontrar números •  A função findNumber encontra números inteiros e também números com parte decimal. •  Encontrar números inteiros é mais simples: •  Com esta, programamos a outra: 20140529 Algoritmos e Estruturas de Dados 93 public static int findInteger(String s, int start) { int size = s.length(); int i = start; while (i < size && Character.isDigit(s.charAt(i))) i++; return i - start; } public static int findNumber(String s, int start) { int result = findInteger(s, start); if (result > 0 && start + result + 1 < s.length() && s.charAt(start + result) == '.' && Character.isDigit(s.charAt(start + result + 1))) result += 1 + findInteger(s, start + result + 1); return result; } Note bem: os números com ponto têm de ter parte inteira e parte decimal ambas não vazias, por exemplo, 3.14, 0.5, 100.0.
  94. 94. Encontrar identificadores •  Um identificador começa por uma letra e depois tem zero ou mais letras ou algarismos: 20140529 Algoritmos e Estruturas de Dados 94 public static int findIdentifier(String s, int start) { int size = s.length(); int i = start; if (i < size && Character.isLetter(s.charAt(i))) do i++; while (i < size && Character.isLetterOrDigit(s.charAt(i))); return i - start; } Se aceitássemos caracteres de sublinhado seria ligeiramente diferente.
  95. 95. Toquenizando •  Se na posição corrente estiver um algarismo, aí começa um número; se estiver uma letra, aí começa um identificador; se estiver um espaço, ignora-se; se não, é um parêntesis ou é um operador: 20140529 Algoritmos e Estruturas de Dados 95 public static Bag<String> tokens(String s) { Bag<String> result = new Bag<String>(); int size = s.length(); int i = 0; while (i < size) { char c = s.charAt(i); if (Character.isDigit(c)) { // this is a number int n = findNumber(s, i); result.add(s.substring(i, i + n)); i += n; } else //... } return result; }
  96. 96. Toquenizando (continuação) 20140529 Algoritmos e Estruturas de Dados 96 public static Bag<String> tokens(String s) { // ... while (i < size) { // ... else if (Character.isLetter(c)) { // this is an identifier. Note: no underscores! int n = findIdentifier(s, i); result.add(s.substring(i, i + n)); i += n; } else if (Character.isWhitespace(c)) i++; // simply ignore white space else { // this is a single-character operator result.add(s.substring(i, i + 1)); i++; } } return result; } Também os parêntesis são apanhados aqui.
  97. 97. Testando a toquenização •  Primeiro, uma função para transformar um saco de cadeias numa cadeia única (pronta a ser escrita); depois a função de teste: 20140529 Algoritmos e Estruturas de Dados 97 public static String stringOf(Bag<String> b, String separator) { // I acknowledge this could be more efficient, // but it is simple and good enough for small tasks. String result = ""; String sep = ""; for (String s : b) { result += sep + s; sep = separator; } return result; } public static void testTokens() { while (StdIn.hasNextLine()) { String line = StdIn.readLine(); Bag<String> b = tokens(line); StdOut.println(stringOf(b, " ")); } } pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  Tokenization   sqrt(3.0*3.0+4.0*4.0)*2   sqrt  (  3.0  *  3.0  +  4.0  *  4.0  )  *  2   ((4+3)*(5-­‐1))   (  (  4  +  3  )  *  (  5  -­‐  1  )  )   (y1-­‐y2)/(x1-­‐x2)   (  y1  -­‐  y2  )  /  (  x1  -­‐  x2  )   Aqui usamos espaços como separadores.
  98. 98. Sacos e arrays •  Podemos agora melhorar a função de teste da classe DijkstraTwoStack, pois deixa de ser preciso apresentar os tóquenes separados por espaços na cadeia de entrada. •  Mas como a função evaluationSimple espera como argumento um array, precisamos de uma função para converter de saco para array: 20140529 Algoritmos e Estruturas de Dados 98 public final class Tokenization { // ... public static String[] arrayOf(Bag<String> b) { String[] result = new String[b.size()]; int i = 0; for (String s : b) result[i++] = s; return result; } // ...
  99. 99. Avaliação após toquenização •  Eis a nova função de teste, que toqueniza a linha de entrada antes da avaliação: 20140529 Algoritmos e Estruturas de Dados 99 public final class DijkstraTwoStack { // ... public static void testEvaluationTokenized() { while (StdIn.hasNextLine()) { String line = StdIn.readLine(); Bag<String> tokens = Tokenization.tokens(line); String[] expression = Tokenization.arrayOf(tokens); double z = evaluationSimple(expression); StdOut.println(z); } } // ... pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  DijkstraTwoStack   (sqrt  2.0)   1.4142135623730951   (3*5)   15.0   ((45+12)*(800-­‐700))   5700.0   (sqrt((3*3)+(4*4)))   5.0  
  100. 100. Expressões regulares •  Modernamente, a análise de cadeias para efeitos de toquenização faz-se com expressões regulares. •  Uma expressão regular é uma cadeia de caracteres que representa de forma compacta um conjunto de cadeias de caracteres. •  A função find da classe de biblioteca Matcher, procura a “próxima” subcadeia que pertence ao conjunto de cadeias especificado pela expressão regular, se houver uma tal cadeia. •  Essa cadeia é depois obtida pela função group. •  A expressão regular é compilada por meio da função compile da classe de biblioteca Pattern. 20140529 Algoritmos e Estruturas de Dados 100
  101. 101. Expressões regulares, exemplos •  “a”: representa o conjunto cujo único elemento é a cadeia “a”. •  “abc”: representa o conjunto cujo único elemento é a cadeia “abc”. •  “a+”: representa o conjunto das cadeias não vazias, formadas exclusivamente pela letra ‘a’. •  “(abc)+”: representa o conjunto das cadeias não vazias, formadas exclusivamente por uma ou mais repetições da cadeia “abc”. •  “abc+”: representa o conjunto das cadeias cujo primeiro caractere é ‘a’, cujo segundo caractere é ‘b’, cujos restantes caracteres são “c”, havendo pelo menos um ‘c’. •  “(a|e|i|o|u)”: representa o conjunto formado pelas cadeias “a”,“e”,“i”,“o” e “u”. •  “(a|e|i|o|u)+”: representa o conjunto das cadeias não vazias formadas exclusivamente pelas letra ‘a’,‘e’,‘i’,‘o’, e ‘u’. •  “[a-zA-Z]”: representa o conjunto das cadeias formadas por uma única letra, minúscula ou maiúscula. •  “[a-zA-Z]+”: representa o conjunto dos nomes, isto é, das cadeias não vazias formadas exclusivamente por letras, minúsculas ou maiúsculas. 20140529 Algoritmos e Estruturas de Dados 101
  102. 102. Expressões regulares, mais exemplos •  “[0-9]”: representa o conjunto das cadeias formadas por um único algarismo. •  “[0-9]+”: representa o conjunto dos numerais decimais, isto é, das cadeias não vazias formadas exclusivamente por algarismos decimais. •  “[a-zA-Z][a-zA-Z_0-9]*”: o conjunto dos identificadores, isto é, cadeias que começam por um letra seguida por zero ou mais letras, algarismos ou caractere de sublinhado. •  “[a-zA-Z]+|[0-9]+”: a reunião do nomes e dos numerais decimais. •  “S”: o conjunto das cadeias formadas por um único caractere que não é um espaço em branco (em inglês, whitespace). •  “d”: o mesmo que “[0..9]”. •  “w”: o mesmo que “[a-zA-Z_0-9]”. •  “d+”: o mesmo que “[0..9]+”. •  “w+”: o mesmo que “[a-zA-Z_0-9]+”. •  “d+.d+”: os numerais com parte inteira e parte decimal, ambas não vazias, separadas por um ponto. 20140529 Algoritmos e Estruturas de Dados 102 Para mais informação sobre expressões regulares em Java, veja http://docs.oracle.com/javase/tutorial/essential/regex/index.html.
  103. 103. Aprendendo as expressões regulares 20140529 Algoritmos e Estruturas de Dados 103 public static void learnRegularExpressions() { StdOut.print("Enter your regular expression: "); String regex = StdIn.readLine(); Pattern pattern = Pattern.compile(regex); StdOut.print("Enter a string to search: "); while (StdIn.hasNextLine()) { String line = StdIn.readLine(); Matcher matcher = pattern.matcher(line); int n = 0; // number of matches while (matcher.find()) { String group = matcher.group(); int start = matcher.start(); int end = matcher.end(); int size = end - start; StdOut.printf("Group: "%s". Start: %d. Length: %dn", group, start, size); n++; } StdOut.printf("Number of matches: %dn", n); StdOut.print("Enter another string to search: "); } } Esta função pertence à classe RegularExpressions. Aceita uma expressão regular, depois uma sequência de cadeias (até ao fim dos dados),“aplicando” a expressão regular a cada uma das cadeias. Adaptado de http://docs.oracle.com/javase/tutorial/essential/regex/test_harness.html.
  104. 104. Exemplos 20140529 Algoritmos e Estruturas de Dados 104 pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  RegularExpressions   Enter  your  regular  expression:  (a|e|i|o|u)+   Enter  a  string  to  search:  auuejjiiiapppahhhaip   Group:  "auue".  Start:  0.  Length:  4   Group:  "iiia".  Start:  6.  Length:  4   Group:  "a".  Start:  13.  Length:  1   Group:  "ai".  Start:  17.  Length:  2   Number  of  matches:  4   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  RegularExpressions   Enter  your  regular  expression:  [0-­‐9]+   Enter  a  string  to  search:  hhhh76lshf3yyy66666666a   Group:  "76".  Start:  4.  Length:  2   Group:  "3".  Start:  10.  Length:  1   Group:  "66666666".  Start:  14.  Length:  8   Number  of  matches:  3   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  RegularExpressions   Enter  your  regular  expression:  [0-­‐9]+|[a-­‐z]+|S   Enter  a  string  to  search:  aaa+15***34/(57)   Group:  "aaa".  Start:  0.  Length:  3   Group:  "+".  Start:  3.  Length:  1   Group:  "15".  Start:  4.  Length:  2   Group:  "*".  Start:  6.  Length:  1   Group:  "*".  Start:  7.  Length:  1   Group:  "*".  Start:  8.  Length:  1   Group:  "34".  Start:  9.  Length:  2   Group:  "/".  Start:  11.  Length:  1   Group:  "(".  Start:  12.  Length:  1   Group:  "57".  Start:  13.  Length:  2   Group:  ")".  Start:  15.  Length:  1   Number  of  matches:  11  
  105. 105. Grupos •  Podemos obter os grupos que quisermos, parametrizando convenientemente a expressão regular: 20140529 Algoritmos e Estruturas de Dados 105 public final class RegularExpressions { // ... public static Bag<String> groups(String s, String regex) { Bag<String> result = new Bag<String>(); Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(s); while (matcher.find()) { String z = matcher.group(); result.add(z); } return result; } // ...
  106. 106. Tóquenes •  Para obter os tóquenes de uma expressão aritmética, “basta” indicar a expressão regular apropriada: 20140529 Algoritmos e Estruturas de Dados 106 public final class RegularExpressions { // ... public static final String REGEX_TOKEN = "d+.d+|d+|[a-zA-Z]w+|S"; public static Bag<String> tokens(String s) { return groups(s, REGEX_TOKEN); } // ... Atenção: ao escrever expressões regulares literais num programa, temos de escapar o carácter ‘’, o qual tem um significado especial quando inserido numa cadeia literal.
  107. 107. Análise da expressão regular •  A expressão regular que usámos tem quatro alternativas: 20140529 Algoritmos e Estruturas de Dados 107 public static final String REGEX_INTEGER = "d+"; public static final String REGEX_DECIMAL = "d+.d+"; public static final String REGEX_IDENTIFIER = "[a-zA-Z]w+"; public static final String REGEX_SINGLE_NON_WHITESPACE = "S"; public static final String REGEX_TOKEN2 = REGEX_DECIMAL + "|" + REGEX_INTEGER + "|" + REGEX_IDENTIFIER + "|" + REGEX_SINGLE_NON_WHITESPACE; •  A expressão dos decimais tem de vir antes da dos inteiros. Caso contrário, a parte inteira seria considerada um grupo, e perdia-se a parte decimal. •  De facto, as alternativas são processadas da esquerda para a direita, até uma acertar.
  108. 108. Toque final •  Podemos agora substituir a toquenização da classe Tokenization pela da classe RegularExpressions: 20140529 Algoritmos e Estruturas de Dados 108 public static void testEvaluationTokenized() { while (StdIn.hasNextLine()) { String line = StdIn.readLine(); // Bag<String> tokens = Tokenization.tokens(line); Bag<String> tokens = RegularExpressions.tokens(line); String[] expression = Tokenization.arrayOf(tokens); double z = evaluationSimple(expression); StdOut.println(z); } }
  109. 109. Algoritmos e Estruturas de Dados Lição n.º 6 Filas
  110. 110. Filas •  Implementação com listas ligadas. •  Outros assuntos •  Objetos funcionais •  Functores •  Arrays de functores 20140529 Algoritmos e Estruturas de Dados 110
  111. 111. Filas •  As filas são a contrapartida informática das filas de espera que encontramos na vida corrente. •  A ideia é que o próximo elemento a ser atendido é o que está na fila há mais tempo •  Dizemos que as filas são coleções FIFO: first in first out. •  Mais adiante veremos filas com prioridade, em que um elemento que entrou mais tarde pode passar à frente de outros que já estavam na fila, por ter maior prioridade. 20140529 Algoritmos e Estruturas de Dados 111
  112. 112. API da fila •  Já a conhecemos: •  Comparando com Stack<T>, temos enqueue em vez de push, dequeue em vez de pop, e front em vez de top. O iterador emitirá os elementos por ordem de entrada na fila. (Na pilha, era ao contrário). 20140529 Algoritmos e Estruturas de Dados 112 public class Queue<T> implements Iterable<T> { public Queue() public void enqueue(T x) public boolean isEmpty() public int size( public T dequeue() public T front() public Iterator<T> iterator() } Nota: o método front não apareceu na lição 3. Nota: o método top da classe Stack não apareceu ainda nesta coleção de transparentes. No entanto, está na classe fornecida.
  113. 113. Implementação com listas •  É semelhante a StackLists<T>, com uma diferença essencial: os elementos são acrescentados no fim da lista e retirados do início da lista. •  Para isso, temos de manter uma referência para o último nó: 20140529 Algoritmos e Estruturas de Dados 113 public class Queue<T> implements Iterable<T> { private class Node { // ... } private Node first; private Node last; private int size; // ... } Nas pilhas os elementos eram acrescentados no início da lista e retirados também do início da lista. Por isso bastava a referência first. public Queue() { first = null; last = null; size = 0; }
  114. 114. Entrar na fila •  Para fazer um elemento entrar na fila, primeiro criamos um novo nó para esse elemento. •  Depois, das duas uma: •  A fila estava vazia: então o novo nó passa a ser o primeiro e último nó da fila. •  A fila não estava vazia: então o novo nó passa a ser o último. 20140529 Algoritmos e Estruturas de Dados 114 public void enqueue(T x) { Node z = new Node(x, null); if (first == null) { last = z; first = z; } else { last.next = z; last = z; } size++; } Aqui o novo nó é ligado ao anterior último nó.
  115. 115. Sair da fila •  Para fazer um elemento sair da fila, basta avançar a referência first para o nó seguinte. Se não houver nó seguinte, first fica a valer null, e, neste caso, last também deve ficar null, pois a fila terá esvaziado. •  A função front apenas retorna o primeiro elemento, sem o remover 20140529 Algoritmos e Estruturas de Dados 115 public T dequeue() { T result = first.value; first = first.next; if (first == null) last = null; size--; return result; } public T front() { return first.value; }
  116. 116. isEmpty, size, iterator •  Os métodos isEmpty, size e iterator são iguais aos das pilhas: 20140529 Algoritmos e Estruturas de Dados 116 public boolean isEmpty() { return first == null; } public int size() { return size; } private class QueueIterator implements Iterator<T> { private Node cursor = first; // ... } public Iterator<T> iterator() { return new QueueIterator(); }
  117. 117. Functores •  Em Java as funções não são conceitos de “primeira classe”. •  Por outras palavras, as funções não são objetos. •  Por exemplo, não há um tipo (i.e., uma classe Java) para as funções com um argumento Integer e resultado Integer. •  Podemos ter arrays, sacos, pilhas de Integer, de String, etc, mas não podemos ter arrays, sacos, pilhas de funções L. •  Isso prejudica um estilo de programação mais funcional, onde as funções seriam conceitos de “primeira classe”. •  Quando, por exemplo, queremos arrays de funções, recorremos aos functores. •  Um functor é um objeto que “contém” a função pretendida e que implementa uma interface conhecida. •  A função é depois chamada através da interface. 20140529 Algoritmos e Estruturas de Dados 117
  118. 118. Exemplo prático •  Na classe DijkstraTwoStack selecionamos a operação a usar observando o operador: •  OK, mas isto é muito rudimentar. 20140529 Algoritmos e Estruturas de Dados 118 private static double evaluationSimple(String[] ss) { // ... if ("+".equals(op)) z = x + y; else if ("-".equals(op)) z = x - y; else if ("-".equals(op)) z = x + y; else if ("*".equals(op)) z = x * y; else if ("/".equals(op)) z = x / y; else if ("sqrt".equals(op)) z = Math.sqrt(y); // ... }
  119. 119. Calculando a função a usar •  Funcionalmente, calcularíamos a função a usar, “em função” do operador, assim, em esquema: •  Aqui, a função functor (uma função privada da classe), calcularia a função a usar (adição, subtração, multiplicação ou divisão), a partir do operador op (que foi retirado da pilha dos operadores). Depois, chama a função resultante com os dois argumentos. 20140529 Algoritmos e Estruturas de Dados 119 private static double evaluation(String[] ss) { // ... Functor f = functor(op); z = f(values.pop(), y); // ... } Na verdade, será um pouco mais complicado do que isto...
  120. 120. Funções binárias e funções unárias •  Como temos dois tipos de funções (binárias e unárias) precisamos de dois tipos de functores. •  De novo em esquema, para ilustrar a ideia: •  Isto não pode ficar assim, porque f1 e f2 serão objetos e não funções e portanto não podem ser “chamados” como em f2(values.pop(), y) e f1(y). 20140529 Algoritmos e Estruturas de Dados 120 private static double evaluation(String[] ss) { // ... FunctorBinary f2 = functorBinary(op); FunctorUnary f1 = functorUnary(op); // f1 might not be necessary... if (f2 != null) z = f2(values.pop(), y); else if (f1 != null) z = f1(y); // ... }
  121. 121. Classes abstratas para os functores •  Implementaremos os functores por meio de classes abstratas: •  Depois meteremos cada uma das operações num objeto de uma classe derivada de uma destas. 20140529 Algoritmos e Estruturas de Dados 121 abstract class FunctorBinary { public abstract double evaluate(double x, double y); } abstract class FunctorUnary { public abstract double evaluate(double x); } As operações binárias vão para classes derivadas de FunctorBinary e as operações unárias vão para classes derivadas de FunctorUnary, claro.
  122. 122. Functores binários •  Eis as classes para os operadores binários: 20140529 Algoritmos e Estruturas de Dados 122 class Plus extends FunctorBinary { public double evaluate(double x, double y) { return x + y; } } class Minus extends FunctorBinary { public double evaluate(double x, double y) { return x - y; } } class Times extends FunctorBinary { public double evaluate(double x, double y) { return x * y; } } class Divide extends FunctorBinary { public double evaluate(double x, double y) { return x / y; } }
  123. 123. Functores unários •  No exercício, só usámos um operador unário, mas o esquema é o mesmo: 20140529 Algoritmos e Estruturas de Dados 123 class SquareRoot extends FunctorUnary { public double evaluate(double x) { return Math.sqrt(x); } }
  124. 124. Avaliação, melhor •  Na função de avaliação, fica assim: 20140529 Algoritmos e Estruturas de Dados 124 private static double evaluationBetter(String[] ss) { Stack<String> operators = new Stack<String>(); Stack<Double> values = new Stack<Double>(); for (String s : ss) if // ... else if (")".equals(s)) { String op = operators.pop(); double z; double y = values.pop(); FunctorBinary f2 = functorBinary(op); FunctorUnary f1 = functorUnary(op); // might not be necessary... if (f2 != null) z = f2.evaluate(values.pop(), y); else if (f1 != null) z = f1.evaluate(y); else throw new UnsupportedOperationException(); values.push(z); } else throw new UnsupportedOperationException(); return values.pop(); }
  125. 125. Arrays de functores •  Precisamos de dois arrays de functores, um para as operações binárias, outro para as operações unárias: •  Repare: cada elemento destes arrays é um objeto de uma classe derivada de FunctorBinary (no primeiro caso) ou de FunctorUnary (no segundo). •  Inserimos as funções pela mesma ordem dos respetivos operadores nos arrays respetivos: 20140529 Algoritmos e Estruturas de Dados 125 private static FunctorBinary[] functors2 = {new Plus(), new Minus(), new Times(), new Divide()}; private static FunctorUnary[] functors1 = {new SquareRoot()}; private static String[] binaryOperators = {"+", "-", "*", "/"}; private static String[] unaryOperators = {"sqrt"};
  126. 126. Seleção de functores •  Restam as funções de busca que obtêm o functor correspondente ao operador dado: 20140529 Algoritmos e Estruturas de Dados 126 private static FunctorBinary functorBinary(String op) { for (int i = 0; i < binaryOperators.length; i++) if (op.equals(binaryOperators[i])) return functors2[i]; return null; } private static FunctorUnary functorUnary(String op) { for (int i = 0; i < unaryOperators.length; i++) if (op.equals(unaryOperators[i])) return functors1[i]; return null; } Note bem: para isto funcionar, os functores têm de estar nos arrays nas posições correspondentes aos respetivos operadores.
  127. 127. Moral da história •  Com este esquema, a função de avaliação fica mais simples (mas mais sofisticada, é verdade). •  Acrescentar um operador, binário ou unário, faz-se sem mexer na função de avaliação. •  Por outro lado, parece excessivo criar classes, uma a uma, para cada operação. •  Linguagens mais modernas permitem aligeirar esse trabalho, tornando as funções conceitos de “primeira classe”. •  Provavelmente, a próxima versão do Java incluirá esta funcionalidade também. 20140529 Algoritmos e Estruturas de Dados 127
  128. 128. Algoritmos e Estruturas de Dados Lição n.º 7 Análise de Algoritmos
  129. 129. Análise de Algoritmos •  Questões centrais: tempo e memória. •  Exemplo: soma tripla. •  Determinação da frequência (absoluta) das instruções. •  Aproximação til. 20140529 Algoritmos e Estruturas de Dados 129
  130. 130. Questões centrais •  Quanto tempo levará o meu programa? •  Quanta memória gastará o meu programa? 20140529 Algoritmos e Estruturas de Dados 130 “Gastar” é apenas uma forma coloquial de falar, já que a memória não se gasta…
  131. 131. Exemplo: problema da soma tripla •  Dada uma lista de números, sem repetições, quantos triplos de números dessa lista somam X, para um dado X? •  Admitindo que os números residem num array, isso pode programar-se assim: 20140529 Algoritmos e Estruturas de Dados 131 public final class ThreeSum { public static int count0(int[] a, int x) { int result = 0; final int n = a.length; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) for (int k = 0; k < n; k++) if (a[i] + a[j] + a[k] == x) result++; return result; } // ... } Bom, mas não é bem isto, porque para efeitos do problema, queremos contar cada triplo só uma vez e, por exemplo <4, 6, 8> é o mesmo triplo que <6, 8, 4> ou <8, 6, 4>, por exemplo.
  132. 132. Soma tripla: primeiro algoritmo •  Só contamos um triplo se for o primeiro. Isto é, se encontramos um triplo nas posições de índices i0, j0, k0, não queremos contá-lo de novo, quando passarmos pelos índices j0, i0, k0, por exemplo: 20140529 Algoritmos e Estruturas de Dados 132 public static int countBrute(int[] a, int x) { int result = 0; final int n = a.length; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) for (int k = 0; k < n; k++) if (a[i] + a[j] + a[k] == x) if (i < j && j < k) result++; return result; } Claro que podemos fazer melhor...
  133. 133. Soma tripla: segundo algoritmo •  Podemos evitar a repetição da contagens, ajustando a variação dos índices diretamente, de maneira que o mesmo triplo não surja mais do que uma vez: 20140529 Algoritmos e Estruturas de Dados 133 public static int countBasic(int[] a, int x) { int result = 0; final int n = a.length; for (int i = 0; i < n; i++) for (int j = i + 1; j < n; j++) for (int k = j + 1; k < n; k++) if (a[i] + a[j] + a[k] == x) result++; return result; } Esta é melhor do que a anterior, mas quão melhor?
  134. 134. Comparando o tempo de execução •  A função countBasic faz nitidamente menos operações do que a função countBrute. •  Logo, chegará ao resultado mais depressa. •  Mas, quão mais depressa? •  Por outras palavras, para o mesmo input, qual é a razão entre os tempos de execução de uma e outra? •  Ou ainda, informalmente: quantas vezes é o countBasic mais rápido (a calcular...) do que o countBrute? 20140529 Algoritmos e Estruturas de Dados 134
  135. 135. Quantas vezes mais rápido? •  “Quantas vezes mais rápido” é uma forma de falar, pois o “número de vezes” pode depender da dimensão dos dados. •  Por exemplo, um certo algoritmo pode ser duas vezes mais rápido que do outro, se houver 1000 números, e quatro vezes mais rápido, se houver 2000 números. •  Para responder à pergunta podemos fazer medições e depois, perante os resultados, propor uma explicação para os valores observados. •  Ou então, analisar primeiro os programas, colocar a seguir uma hipótese sobre quantas vezes um é mais rápido do que o outro e só então fazer experiências para verificar a hipótese. 20140529 Algoritmos e Estruturas de Dados 135 Se, mesmo tentando afincadamente, não conseguirmos refutar a hipótese, aceitá-la-emos como válida.
  136. 136. Tempo de execução •  O tempo de execução de um programa é determinado por dois fatores: •  O tempo de execução de cada instrução. •  O número de vezes que cada instrução é executada. 20140529 Algoritmos e Estruturas de Dados 136 O tempo de execução de cada instrução depende do computador, da linguagem e do sistema operativo. O número de vezes que cada instrução é executada depende do programa e do input. Esta observação, aparentemente trivial, foi feita originalmente por Knuth. Para Knuth, o desafio, era, bem entendido, determinar o número de vezes que cada instrução é executada.
  137. 137. O ciclo interno •  Na prática, em muitos casos apenas as instruções que são executadas com maior frequência têm peso significativo no tempo de execução total. •  Essas instruções constituem o ciclo interno. •  No caso da função countBrute, o ciclo interno é constituído pela seguinte instrução: •  É fácil concluir, do contexto, que esta instrução é executada N3 vezes (representando por N o valor da variável n, que contém o número de elementos do array). 20140529 Algoritmos e Estruturas de Dados 137 if (a[i] + a[j] + a[k] == x) if (i < j && j < k) result++;
  138. 138. O ciclo interno, em countBasic •  Na função countBasic, o ciclo interno contém a instrução. •  Quantas vezes é executada? •  Para cada valor de i e j, a instrução é executada para k valendo j+1, j+2, …, n-1. Logo, é executada (n-1)-(j+1)+1 = (n-1)+j vezes. •  Para cada valor de i, a instrução é executada para j valendo i +1, i+2, …, n-1. Logo, é executada, ((n-1)+(i+1))+((n-1)+(i+2)) + … +((n-1)+(n-1)) vezes. •  Ora i vale sucessivamente 0, 1, …, n-1. Portanto,“basta” somar a expressão anterior n vezes, substituindo i por 0, 1, …, n-1, o que dará uma expressão só envolvendo n. 20140529 Algoritmos e Estruturas de Dados 138 “É só fazer as contas!” if (a[i] + a[j] + a[k] == x) result++;
  139. 139. O ciclo interno, ao contrário •  A seguinte variante é equivalente à anterior, mas faz as variáveis variar descendentemente e torna a análise mais fácil: 20140529 Algoritmos e Estruturas de Dados 139 public static int countBack(int[] a, int x) { int result = 0; final int n = a.length; for (int i = n - 1; i >= 0; i--) for (int j = i - 1; j >= 0; j--) for (int k = j - 1; k >= 0; k--) if (a[i] + a[j] + a[k] == x) result++; return result; }
  140. 140. O ciclo interno, ao contrário •  O ciclo interno é o mesmo: •  Quantas vezes é executada esta instrução? •  Para cada valor de i e j, a instrução é executada para j vezes. •  Para cada valor de i, a instrução é executada para j valendo i-1, i-2, …, 0. Logo, é executada, (i-1)+(i-2)+ … +0 vezes. •  Ora esta expressão vale i*(i-1)/2 ou i2/2-i/2 (usando a fórmula da soma da progressão aritmética. •  Ora i vale sucessivamente 0, 1, …, n-1. Portanto,“basta” somar a expressão anterior n vezes, substituindo i por 0, 1, …, n-1. •  A segunda parcela, i/2, dá n2/4-n/4, claro (usando a mesma fórmula de há pouco). •  Para a fórmula da soma dos quadrados, podemos consultar o Wolfram Alpha, por exemplo. •  Feitas as contas, no fim dá n3/6-n2/2+n/3. 20140529 Algoritmos e Estruturas de Dados 140 if (a[i] + a[j] + a[k] == x) result++;
  141. 141. Aproximação til •  A expressão N3/6-N2/2+N/3 é complicada... •  É mais prático usar uma aproximação, notando que para valores grandes de N (os que nos interessam) o valor da primeira parcela é muito maior do que os valores das outras. •  Em vez de dizer rigorosamente, mas complicativamente que a instrução é executada N3/6- N2/2+N/3 vezes, diremos que é executada ~N3/6 vezes. 20140529 Algoritmos e Estruturas de Dados 141 De certa forma, a expressão ~f(N) representa todas as funções g(N) tais que g(N)/f(N) tende para 1, à medida que N aumenta. Para significar que g(N) é uma dessas funções, escrevemos g(N) ~ f(N).
  142. 142. Comparação countBrute e countBasic •  O número de vezes que a instrução if é executada em countBrute é ~N3. •  Em countBasic é ~N3/6. •  Pela observação de Knuth, o segundo algoritmo é 6 vezes mais rápido do que o primeiro. •  Façamos medições, para verificar essa hipótese. 20140529 Algoritmos e Estruturas de Dados 142
  143. 143. Utilização de functores •  Primeiro a classe abstrata: •  Um functor para cada algoritmo: 20140529 Algoritmos e Estruturas de Dados 143 abstract class ThreeSumAlgorithm { public abstract int count(int[] a, int x); } class ThreeSumBrute extends ThreeSumAlgorithm { public int count(int[] a, int x) { return ThreeSum.countBrute(a, x); } } class ThreeSumBasic extends ThreeSumAlgorithm { public int count(int[] a, int x) { return ThreeSum.countBasic(a, x); } }
  144. 144. Medição do tempo •  Medimos o tempo do algoritmo com uma função da classe abstrata: 20140529 Algoritmos e Estruturas de Dados 144 abstract class ThreeSumAlgorithm { public abstract int count(int[] a, int x); public double timeTrial(int[] a, int x) { Stopwatch timer = new Stopwatch(); count(a, x); // result of this computation is not used. double result = timer.elapsedTime(); return result; } } A classe Stopwatch vem na biblioteca stdlib.jar.
  145. 145. Função de teste •  A função de teste lê os números do ficheiro cujo nome é indicado no primeiro argumento da linha de comando e conta os triplos cuja soma é igual ao segundo argumento da linha de comando. •  Depois invoca o método timeTrial para cada um dos algoritmos e mostra os tempos medidos, e a razão entre eles: 20140529 Algoritmos e Estruturas de Dados 145 public static void testCompareBruteBasic(String[] args) { String filename = args[0]; int x = Integer.parseInt(args[1]); int[] a = new In(filename).readAllInts(); double t1 = new ThreeSumBrute().timeTrial(a, x); double t2 = new ThreeSumBasic().timeTrial(a, x); StdOut.printf("%.3f %.3fn", t1, t2); StdOut.printf("%.2fn", t1 / t2); }
  146. 146. Resultados da observação •  Corremos com ficheiros com 1000, 2000, 3000, 4000 e 5000 números. Em cada ficheiro há números aleatórios entre 0 (inclusive) e 10 vezes o número de números (exclusive), sem repetição: •  Concluímos que estes resultados estão globalmente de acordo com a hipótese, designadamente quando os tempos medidos são maiores. 20140529 Algoritmos e Estruturas de Dados 146 pedros-­‐imac-­‐5:work  pedro$  java  -­‐cp  ../stdlib.jar:../bin  ThreeSum  t_1000.txt  10000   0.424  0.082   5.17   pedros-­‐imac-­‐5:work  pedro$  java  -­‐cp  ../stdlib.jar:../bin  ThreeSum  t_2000.txt  20000   3.339  0.576   5.80   pedros-­‐imac-­‐5:work  pedro$  java  -­‐cp  ../stdlib.jar:../bin  ThreeSum  t_3000.txt  30000   11.215  1.890   5.93   pedros-­‐imac-­‐5:work  pedro$  java  -­‐cp  ../stdlib.jar:../bin  ThreeSum  t_4000.txt  40000   26.390  4.399   6.00   pedros-­‐imac-­‐5:work  pedro$  java  -­‐cp  ../stdlib.jar:../bin  ThreeSum  t_5000.txt  50000   51.420  8.616   5.97  
  147. 147. Algoritmos e Estruturas de Dados Lição n.º 8 Ordem de crescimento do tempo de execução
  148. 148. Ordem de crescimento do tempo de execução •  Classificação por ordem de crescimento do tempo de execução. •  Ensaios de razão dobrada. •  Desenvolvimento de algoritmos mais rápidos. •  Consumo de memória. 20140529 Algoritmos e Estruturas de Dados 148
  149. 149. Ordem de crescimento •  Em análise de algoritmos, as aproximações til que nos interessam são geralmente da forma g(N) ~ a*f(N), onde f(N) = Nb(log N)c, sendo a, b e c constantes. •  Dizemos que f(N) é a ordem de crescimento de g(N). •  Portanto, em countBrute e em countBasic a ordem de crescimento da frequência da instrução if é N3. 20140529 Algoritmos e Estruturas de Dados 149 Nota técnica: quando usamos logaritmos neste contexto, a base é irrelevante, pois uma mudança de base será sempre absorvida pela constante a, por aplicação da fórmula da mudança de base: logbx = logba * logax.
  150. 150. Ordens de crescimento habituais 20140529 Algoritmos e Estruturas de Dados 150 Descrição Função Constante 1 Logarítmica log N Linear N Linearítmica N log N Quadrática N2 Cúbica N3 Exponencial 2N Troca do valor de duas variáveis Busca dicotómica Máximo de um array Quicksort, Mergesort Selectionsort, Insertionsort Soma tripla... Busca exaustiva: por exemplo, encontrar todos os subconjuntos cuja soma é X.
  151. 151. Ordem de crescimento do tempo •  As medições de há pouco parecem indicar que o tempo de execução cresce 8 vezes quando o tamanho do array duplica, para ambos os algoritmos. •  Isso é um reflexo da ordem de crescimento cúbica da frequência da instrução if. •  Analisemos isso sistematicamente. 20140529 Algoritmos e Estruturas de Dados 151
  152. 152. Ensaios de razão dobrada •  Primeiro, construímos um gerador de inputs apropriado ao problema. •  Depois experimentamos o programa repetidamente, de cada vez dobrando o tamanho dos dados. •  Medido o tempo numa experiência, calculamos a razão para o tempo da experiência anterior. •  Em muitos casos, observaremos que a razão tende para 2b, para uma certa constante b. 20140529 Algoritmos e Estruturas de Dados 152
  153. 153. Array de números aleatórios •  Para o problema da soma tripla precisamos de arrays de números arbitrários, sem duplicados. •  Comecemos por gerar arrays de size números aleatórios, em geral, no intervalo [0..max[. •  Em cada caso, indicamos os valores de size e de max: 20140529 Algoritmos e Estruturas de Dados 153 public static int[] randomArray(int size, int max) { int[] result = new int[size]; for (int i = 0; i < size; i++) result[i] = StdRandom.uniform(max); return result; } Cada chamada da função StdRandom.uniform() devolve um número aleatório no intervalo [0..max[. A classe StdRandom vem na biblioteca stdlib.jar.
  154. 154. Testando a criação de números aleatórios •  A função de teste obtém os argumentos da linha de comando. Se não houver, usa valores por defeito: 20140529 Algoritmos e Estruturas de Dados 154 public static void testRandomArray(String[] args) { int size = 10; int max = 20; if (args.length >= 1) { size = Integer.parseInt(args[0]); max = 2 * size; } if (args.length >= 2) max = Integer.parseInt(args[1]); int[] a = randomArray(size, max); StdOut.println("[" + stringOf(a, " ") + "]"); } Normalmente, os valores por defeito estarão especificados por constantes estáticas da classe. public static void main(String[] args) { // ... testRandomArray(args); } A função stringOf vem na página seguinte.
  155. 155. Função stringOf para arrays de inteiros •  Função utilitária para observar o conteúdo de arrays de int: 20140529 Algoritmos e Estruturas de Dados 155 public static String stringOf(int[] a, String separator) { // I acknowledge this could be more efficient, // but it is simple and good enough for small tasks. String result = ""; String sep = ""; for (int x : a) { result += sep + x; sep = separator; } return result; }
  156. 156. Números aleatórios, sem duplicados •  Observe: 20140529 Algoritmos e Estruturas de Dados 156 public static int[] randomArrayUnique(int size, int max) { int[] result = new int[size]; int[] candidates = new int[max]; for (int i = 0; i < max; i++) candidates[i] = i; int maxRandom = max; for (int i = 0; i < size; i++) { int r = StdRandom.uniform(maxRandom); result[i] = candidates[r]; candidates[r] = candidates[maxRandom - 1]; maxRandom--; } return result; } Inicialmente, todos os números em [0..max[ são candidatos. Em cada passo, recolhe-se um candidato aleatoriamente, colocando na sua posição o último candidato e decrementando o número de candidatos.
  157. 157. Observando os números aleatórios •  Chamamos as duas funções de teste para os mesmos argumentos na linha de comando: 20140529 Algoritmos e Estruturas de Dados 157 public static void main(String[] args) { // ... testRandomArray(args); testRandomArrayUnique(args); } pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  10  16   [3  12  9  0  0  3  13  6  14  2]   [5  13  2  3  14  11  4  6  8  9]   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  25  50   [31  24  40  4  0  38  37  45  44  44  13  28  7  2  1  2  25  49  21  38  8  9  39  35  13]   [25  45  35  14  43  40  9  22  17  32  31  48  4  21  6  42  7  1  23  33  5  3  46  28  41]   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  10  10   [6  2  8  3  5  4  6  8  8  9]   [6  2  4  8  1  0  5  3  7  9]   public static void testRandomArrayUnique(String[] args) { int size = 10; int max = 20; if (args.length >= 1) { size = Integer.parseInt(args[0]); max = 2 * size; } if (args.length >= 2) max = Integer.parseInt(args[1]); int[] a = randomArrayUnique(size, max); StdOut.println("[" + stringOf(a, " ") + "]"); } A função testRandomArrayUnique é igualzinha à outra, mas agora chamando randomArrayUnique. A primeira linha pode ter duplicados; a segunda não tem.
  158. 158. Medição do tempo, com arrays aleatórios •  Acrescentamos à classe abstrata uma função que faz n ensaios, com arrays com size números aleatórios em [0..max[, contando os triplos que somam x: 20140529 Algoritmos e Estruturas de Dados 158 abstract class ThreeSumAlgorithm { // ... public double timeTrialRandom(int size, int max, int x, int n) { double total = 0.0; for (int i = 0; i < n; i++) { int[] a = ThreeSum.randomArrayUnique(size, max); total += timeTrial(a, x); } return total / n; } } Como só nos interessa o tempo, o resultado do algoritmo propriamente dito é ignorado. Fazemos vários ensaios e tiramos a média dos tempos, para o resultado ser mais representativo. A função timeTrial vem da lição anterior, página 17.
  159. 159. Testando com o algoritmo escolhido •  A seguinte função de teste usa o algoritmo countBasic: 20140529 Algoritmos e Estruturas de Dados 159 public static void testTimeTrialRandom(String[] args) { if (args.length < 4) return; int size = Integer.parseInt(args[0]); int max = Integer.parseInt(args[1]); int x = Integer.parseInt(args[2]); int n = Integer.parseInt(args[3]); double t = new ThreeSumBasic().timeTrialRandom(size, max, x, n); StdOut.printf("%.3fn", t); } Neste exemplo, é preciso indicar os quatro argumentos na linha de comando. pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  1000  10000  15000  10   0.075   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  2000  20000  30000  10   0.567   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  4000  40000  60000  10   4.427   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  8000  80000  120000  10   34.782  
  160. 160. Razão dobrada •  Podemos automatizar os cálculos da página anterior, em que a dimensão dos cálculos dobra de cada vez: 20140529 Algoritmos e Estruturas de Dados 160 abstract class ThreeSumAlgorithm { // ... public double[] doublingTrial(int size, int max, int sum, int n, int nTrials) { double[] result = new double[nTrials]; for (int i = 0; i < nTrials; i++, size *= 2, max *= 2, sum *= 2) result[i] = timeTrialRandom(size, max, sum, n); return result; } } Note que esta é uma função não abstrata da classe abstrata. O resultado é um array de doubles, com o tempo médio dos ensaios com cada tamanho.
  161. 161. Testando a razão dobrada •  Para maior conveniência, a função de teste seleciona o algoritmo pelo seu índice no array dos algoritmos: 20140529 Algoritmos e Estruturas de Dados 161 public final class ThreeSum { // ... private static ThreeSumAlgorithm[] tsa = {new ThreeSumBrute(), new ThreeSumBasic(), new ThreeSumBack(), new ThreeSumFaster()}; // public static void testDoublingRatio(String[] args) { if (args.length < 6) return; int size = Integer.parseInt(args[0]); int max = Integer.parseInt(args[1]); int x = Integer.parseInt(args[2]); int n = Integer.parseInt(args[3]); int nTrials = Integer.parseInt(args[4]); int i = Integer.parseInt(args[5]); ThreeSumAlgorithm a = tsa[i]; double[] t = a.doublingTrial(size, max, x, n, nTrials); ThreeSumAlgorithm.printTable(size, t); } // ... Neste exemplo, é preciso indicar seis argumentos na linha de comando. O sexto argumento é o que seleciona o algoritmo. A função printTable vem na página seguinte.
  162. 162. Função printTable •  Mostra os números no array dos tempos, linha a linha, precedidos pelo tamanho do array usado no ensaio, e seguidos pela razão em relação ao ensaio anterior (se não for zero), com um formato minimalista: 20140529 Algoritmos e Estruturas de Dados 162 abstract class ThreeSumAlgorithm { // ... public static void printTable(int size, double[] t) { double t0 = 0.0; for (int i = 0; i < t.length; i++, size *= 2) { StdOut.printf("%d %.3f", size, t[i]); if (t0 > 0) StdOut.printf(" %.2f", t[i] / t0); StdOut.println(); t0 = t[i]; } } }
  163. 163. Invocando a função de teste 20140529 Algoritmos e Estruturas de Dados 163 public static void main(String[] args) { // testRandomArray(args); // testRandomUniqueArray(args); // testTimeTrialRandom(args); testDoublingRatio(args); } pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  250  2500  3750  5  6  1   250  0.003   500  0.010  3.13   1000  0.074  7.42   2000  0.576  7.76   4000  4.524  7.86   8000  35.738  7.90   pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  100  1000  1500  3  5  0   100  0.002   200  0.004  1.71   400  0.029  7.17   800  0.220  7.69   1600  1.746  7.92   Este é o countBasic. Este é o countBrute. É cúbico, como o outro, mais mais lento, como sabemos.
  164. 164. Um algoritmo mais rápido •  Vimos que countBasic é 6 vezes mais rápido do que countBrute; no entanto, ambos são algoritmos cúbicos, isto é, em ambos o tempo de execução é proporcional ao cubo do tamanho do array. •  Será que conseguimos encontrar um algoritmo mais rápido, isto é, com ordem de crescimento subcúbica? •  Ora bem: dois valores do array a[i] e a[j], com i < j, farão parte de um triplo que soma x, se existir no array, à direita de j, um elemento de valor x-(a[i] +a[j]). •  Se o array estiver ordenado, podemos procurar esse valor usando busca dicotómica. •  Na verdade, de certa forma, os anteriores algoritmos procuram usando busca linear. •  Para decidir se um dado valor existe num array ordenado com N elementos bastam no máximo floor(log2 N) + 1 iterações, usando busca dicotómica. •  Portanto, descontando a ordenação, o algoritmo deve ser ~aN2 log2 N, para um certo a. •  A ordem de crescimento será N2 log2 N. 20140529 Algoritmos e Estruturas de Dados 164
  165. 165. Custo da ordenação •  Os algoritmos de ordenação elementares—insertionsort, selectionsort, bubblesort—são quadráticos. •  Portanto, ordenando com um desses, o tempo de execução do novo algoritmo seria ~(aN2 log2 N + bN2). •  Ora isto é o mesmo que ~aN2 log2 N. •  Repare, quando N for muito grande, a segunda parcela é desprezável face à primeira. •  Ainda por cima, os algoritmos de ordenação usados pela biblioteca do Java—o mergesort, para objetos e quicksort para tipos primitivos—são N log N, que é melhor que N2. •  Portanto, esta estratégia promete um algoritmo mais rápido. •  Experimentemos. 20140529 Algoritmos e Estruturas de Dados 165 No Java 7, o mergesort é substituído por um outro algoritmo, chamado timsort.
  166. 166. Busca dicotómica •  A função rank calcula o índice de um elemento de valor x no array a, se houver, ou -1, se não. 20140529 Algoritmos e Estruturas de Dados 166 public final class BinarySearch { public static int rank(int x, int[] a) { int result = -1; int i = 0; int j = a.length - 1; while (result == -1 && i <= j) { int m = i + (j - i) / 2; if (x < a[m]) j = m - 1; else if (x > a[m]) i = m + 1; else result = m; } return result; } } No caso de arrays sem duplicados, o índice calculado representa o renque do elemento de valor x, caso exista, isto é o número de elementos no array de valor menor que x. Nota: renque é uma palavra portuguesa legítima, que corresponde ao inglês “rank”. Porquê escrever i+(j-i)/2 e não, mais simplesmente, (i+j)/2? Resposta aqui.
  167. 167. Função countFaster •  Eis o novo algoritmo para o problema da soma tripla: 20140529 Algoritmos e Estruturas de Dados 167 public static int countFaster(int[] a, int x) { Arrays.sort(a); int result = 0; final int n = a.length; for (int i = 0; i < n; i++) for (int j = i + 1; j < n; j++) { int r = BinarySearch.rank(x - (a[i] + a[j]), a); if (r > j) result++; } return result; } Note bem: este algoritmo, ao contrário dos anteriores, modifica o array. Se isso não for conveniente, cabe ao cliente fornecer uma cópia do seu array. Porquê escrever i+(j-i)/2 e não, mais simplesmente, (i+j)/2? Resposta aqui.
  168. 168. Razão dobrada para o countFaster •  Criamos uma nova classe derivada de ThreeSumAlgorithm: •  Acrescentamos o objeto respetivo no array de algoritmos: •  E pronto: 20140529 Algoritmos e Estruturas de Dados 168 class ThreeSumFaster extends ThreeSumAlgorithm { public int count(int[] a, int x) { return ThreeSum.countFaster(a, x); } } private static ThreeSumAlgorithm[] tsa = {new ThreeSumBrute(), new ThreeSumBasic(), new ThreeSumBack(), new ThreeSumFaster()}; pedros-­‐imac-­‐5:bin  pedro$  java  -­‐cp  ../stdlib.jar:.  ThreeSum  250  2500  3750  5  6  3   250  0.005   500  0.005  1.04   1000  0.022  4.15   2000  0.092  4.27   4000  0.383  4.15   8000  1.592  4.16   Confirmamos que o algoritmo é bem mais rápido do que o countBasic. Observamos que a razão dos tempos é um pouco maior do que 4, o que é coerente com o facto de o algoritmo ser N2 log N.
  169. 169. Memória: tipos primitivos 20140529 Algoritmos e Estruturas de Dados 169 Tipo Bytes boolean 1 byte 1 char 2 int 4 float 4 long 8 double 8 (referência) 8 O número de bytes de uma referência é para uma arquitetura de 64 bits.
  170. 170. Memória: objetos •  Num objeto, há a memória dos membros (instance variables), mais 16 bytes de overhead (referência para a classe, informação para a garbage collection e informação para sincronização).Além disso, a memória é “arredondada” para cima, para um múltiplo de 8 bytes. •  Por exemplo, um objeto Integer ocupa 24 bytes: 4 para o int, 16 de overhead e 4 de arredondamento. Quer dizer: um Integer gasta 6 vezes mais memória do que um int. •  Os arrays são objetos; logo, aplica-se a regra acima, mas há que contar com mais 4 bytes para registar o tamanho. •  Por exemplo, um array de int, usa 24 bytes de “cabeçalho”, sendo 16 bytes de overhead, e 4 para o comprimento e ainda 4 de arredondamento, mais o espaço para os valores, que é N * 4 bytes (sendo N o tamanho). •  Um array de objetos é um array de referências.Ao espaço usado pelo array, há que adicionar o espaço dos objetos referenciados. •  Um array a duas dimensões é um array de arrays. 20140529 Algoritmos e Estruturas de Dados 170
  171. 171. Memória: cadeias de carateres •  As cadeias de carateres são objetos. •  Cada objeto String tem 4 membros: uma referência para o array de carateres e três ints. O primeiro int é um deslocamento no array de carateres, o segundo é o número de carateres da cadeia (não confundir com o número de carateres do array de carateres, o qual está registado no cabeçalho desse array); o terceiro é usado para evitar cálculos desnecessários em certas circunstâncias. •  Portanto, cada cadeia gasta 40 bytes para o objeto (16+8+4+4+4 = 36, mais 4 de arredondamento) e ainda o espaço para o array de carateres. •  Mas note-se que o array de carateres pode ser partilhado por várias cadeias. •  Por exemplo, quando se usa o método substring(), cria-se um novo objeto String, que usa o mesmo array de carateres do que o objeto do qual é uma subcadeia. •  Repare que este funcionamento é possível porque as cadeias de carateres são objetos imutáveis em Java. 20140529 Algoritmos e Estruturas de Dados 171
  172. 172. Algoritmos e Estruturas de Dados Lição n.º 9 Union-Find
  173. 173. Union-Find •  Problema da conectividade dinâmica. •  Union-Find, em geral. •  Quick-Find. •  Quick-Union. •  Quick-Union-Weighted. 20140529 Algoritmos e Estruturas de Dados 173
  174. 174. Conectividade dinâmica •  Temos um conjunto de nós: x1, x2, ... xn. •  Alguns desses nós estão ligados, dois a dois. •  Esta relação de “estar ligado” é uma relação de equivalência: •  Reflexiva •  Simétrica •  Transitiva •  Queremos um programa que, perante um par de nós, xi e xj, nos diga se esses nós estão conectados, isto é, se pertencem à mesma classe de equivalência. •  E queremos que o programa recalcule as classes de equivalência quando estabelecemos uma nova ligação entre dois nós que não estavam conectados. 20140529 Algoritmos e Estruturas de Dados 174 Recorde: uma relação de equivalência particiona o universo em classes de equivalência disjuntas. Na nossa gíria, uma classe de equivalência de nós é uma “componente”.
  175. 175. Exemplo: reticulado dinâmico •  Visualizar um reticulado em que cada vez que ligamos dois nós vizinhos, as conexões aos vizinhos que pertencem à mesma componente são recalculadas e mostradas. 20140529 Algoritmos e Estruturas de Dados 175 Note bem: o programa desenha uma conexão entre dois nós vizinhos se eles estiverem na mesma componente. Outra coisa seria desenhar uma conexão entre dois nós da mesma componente se eles fossem vizinhos, ainda que o efeito visual fosse o mesmo.

×