SlideShare uma empresa Scribd logo
1 de 544
Baixar para ler offline
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 de programação
20140529 Algoritmos e Estruturas de Dados 2
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
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.princeton.edu/
20140529 Algoritmos e Estruturas de Dados 5
http://www.amazon.co.uk/Algorithms-Robert-Sedgewick/dp/032157351X/
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
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
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
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
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
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
Algoritmos da escola primária
•  Adição
•  Subtração
•  Multiplicação
•  Divisão
•  (Sucessor)
20140529 Algoritmos e Estruturas de Dados 12
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
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.
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.
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!
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.
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
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();
}
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();
}
}
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).
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.
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);
}
}
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;
}
}
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 do algoritmo de Euclides
20140529 Algoritmos e Estruturas de Dados 26
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
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
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().
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 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.
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()});
}
}
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;
}
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
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.
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++;
}
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;
}
// ...
}
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();
}
// ...
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;
}
// ...
}
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.
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);
}
// …
}
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);
}
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()});
}
}
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
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çã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.
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.
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>
{
// ...
}
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()
}
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.
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.
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.
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];
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!
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.
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;
}
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;
}
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.
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);
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
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.
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.
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());
}
// ...
}
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.
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();
}
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.
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
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
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 expressões
aritméticas usando o algoritmo da pilha
dupla, de Dijkstra.
20140529 Algoritmos e Estruturas de Dados 70
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
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()
}
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?
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.
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();
}
// ...
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!
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.
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;
// ...
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;
}
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.
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.
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
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
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.
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.
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.
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.
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	
  
	
  
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 de Dados 90
Esta aula antecipa parte da matéria de
sobre cadeias de carateres, à qual
voltaremos mais tarde.
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
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.
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.
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.
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;
}
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.
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.
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;
}
// ...
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	
  
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
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
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.
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.
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	
  
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;
}
// ...
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.
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.
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);
}
}
Algoritmos e Estruturas de Dados
Lição n.º 6
Filas
Filas
•  Implementação com listas ligadas.
•  Outros assuntos
•  Objetos funcionais
•  Functores
•  Arrays de functores
20140529 Algoritmos e Estruturas de Dados 110
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
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.
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;
}
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ó.
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;
}
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();
}
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
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);
// ...
}
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...
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);
// ...
}
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.
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;
}
}
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);
}
}
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();
}
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"};
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.
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
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 (absoluta) das
instruções.
•  Aproximação til.
20140529 Algoritmos e Estruturas de Dados 129
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…
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.
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...
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?
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
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.
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.
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++;
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++;
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;
}
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++;
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).
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
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);
}
}
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.
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);
}
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	
  
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 razão dobrada.
•  Desenvolvimento de algoritmos mais rápidos.
•  Consumo de memória.
20140529 Algoritmos e Estruturas de Dados 148
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.
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.
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
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
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.
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.
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;
}
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.
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.
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.
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	
  
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.
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.
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];
}
}
}
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.
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
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.
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.
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.
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.
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.
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
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
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-Weighted.
20140529 Algoritmos e Estruturas de Dados 173
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”.
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.
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014

Mais conteúdo relacionado

Destaque

Aula 1 aed - lógica de programação
Aula 1   aed - lógica de programaçãoAula 1   aed - lógica de programação
Aula 1 aed - lógica de programaçãoElaine Cecília Gatto
 
Estruturas de dados dinâmicos em linguagem C
Estruturas de dados dinâmicos em linguagem CEstruturas de dados dinâmicos em linguagem C
Estruturas de dados dinâmicos em linguagem Cpjclima
 
Estruturas de dados em Python
Estruturas de dados em PythonEstruturas de dados em Python
Estruturas de dados em PythonRicardo Paiva
 
Algoritmos - Lógica de Programação
Algoritmos - Lógica de ProgramaçãoAlgoritmos - Lógica de Programação
Algoritmos - Lógica de ProgramaçãoElaine Cecília Gatto
 
1 provas anpad_rq+rl_2010
1 provas anpad_rq+rl_20101 provas anpad_rq+rl_2010
1 provas anpad_rq+rl_2010Andre Somar
 
Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)
Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)
Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)Adriano Teixeira de Souza
 
Estrutura de Dados em Java (Revisão de Algoritimos em Java)
Estrutura de Dados em Java (Revisão de Algoritimos em Java)Estrutura de Dados em Java (Revisão de Algoritimos em Java)
Estrutura de Dados em Java (Revisão de Algoritimos em Java)Adriano Teixeira de Souza
 
Estrutura de dados em Java - Filas com lista encadeada
Estrutura de dados em Java - Filas com lista encadeada Estrutura de dados em Java - Filas com lista encadeada
Estrutura de dados em Java - Filas com lista encadeada Adriano Teixeira de Souza
 
Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)
Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)
Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)Adriano Teixeira de Souza
 
Estrutura de Dados em Java (Funções e Procedimentos)
Estrutura de Dados em Java (Funções e Procedimentos)Estrutura de Dados em Java (Funções e Procedimentos)
Estrutura de Dados em Java (Funções e Procedimentos)Adriano Teixeira de Souza
 
Estrutura de dados em Java - Árvores Binárias
Estrutura de dados em Java - Árvores BináriasEstrutura de dados em Java - Árvores Binárias
Estrutura de dados em Java - Árvores BináriasAdriano Teixeira de Souza
 
Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...
Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...
Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...Adriano Teixeira de Souza
 
Estruturas de dados com C++ e STL
Estruturas de dados com C++ e STLEstruturas de dados com C++ e STL
Estruturas de dados com C++ e STLMarcos Castro
 

Destaque (20)

Pi1415 tudo
Pi1415 tudoPi1415 tudo
Pi1415 tudo
 
Aula 1 aed - lógica de programação
Aula 1   aed - lógica de programaçãoAula 1   aed - lógica de programação
Aula 1 aed - lógica de programação
 
Estruturas de dados dinâmicos em linguagem C
Estruturas de dados dinâmicos em linguagem CEstruturas de dados dinâmicos em linguagem C
Estruturas de dados dinâmicos em linguagem C
 
Estruturas de dados em Python
Estruturas de dados em PythonEstruturas de dados em Python
Estruturas de dados em Python
 
Algoritmos - Lógica de Programação
Algoritmos - Lógica de ProgramaçãoAlgoritmos - Lógica de Programação
Algoritmos - Lógica de Programação
 
1 provas anpad_rq+rl_2010
1 provas anpad_rq+rl_20101 provas anpad_rq+rl_2010
1 provas anpad_rq+rl_2010
 
Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)
Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)
Estrutura de Dados em Java (Introdução à Programação Orientada a Objetos)
 
Estrutura de Dados em Java (Revisão de Algoritimos em Java)
Estrutura de Dados em Java (Revisão de Algoritimos em Java)Estrutura de Dados em Java (Revisão de Algoritimos em Java)
Estrutura de Dados em Java (Revisão de Algoritimos em Java)
 
Estrutura de dados em Java - Filas com lista encadeada
Estrutura de dados em Java - Filas com lista encadeada Estrutura de dados em Java - Filas com lista encadeada
Estrutura de dados em Java - Filas com lista encadeada
 
Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)
Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)
Estrutura de Dados em Java (Variáveis Compostas - Vetores e Matrizes)
 
Estrutura de Dados em Java (Introdução)
Estrutura de Dados em Java (Introdução)Estrutura de Dados em Java (Introdução)
Estrutura de Dados em Java (Introdução)
 
Estrutura de Dados em Java (Funções e Procedimentos)
Estrutura de Dados em Java (Funções e Procedimentos)Estrutura de Dados em Java (Funções e Procedimentos)
Estrutura de Dados em Java (Funções e Procedimentos)
 
Estrutura de dados em Java - Árvores Binárias
Estrutura de dados em Java - Árvores BináriasEstrutura de dados em Java - Árvores Binárias
Estrutura de dados em Java - Árvores Binárias
 
Estrutura de dados em Java - Recursividade
Estrutura de dados em Java - RecursividadeEstrutura de dados em Java - Recursividade
Estrutura de dados em Java - Recursividade
 
Estrutura de dados em Java - Filas
Estrutura de dados em Java - Filas Estrutura de dados em Java - Filas
Estrutura de dados em Java - Filas
 
Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...
Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...
Aplicação de Integração Contínua para viabilizar a rastreabilidade de artefat...
 
Estrutura de dados em Java - Pilhas
Estrutura de dados em Java - PilhasEstrutura de dados em Java - Pilhas
Estrutura de dados em Java - Pilhas
 
Estrutura de dados em Java - Filas
Estrutura de dados em Java - FilasEstrutura de dados em Java - Filas
Estrutura de dados em Java - Filas
 
Estruturas de dados com C++ e STL
Estruturas de dados com C++ e STLEstruturas de dados com C++ e STL
Estruturas de dados com C++ e STL
 
Estrutura de dados - Árvores Binárias
Estrutura de dados - Árvores BináriasEstrutura de dados - Árvores Binárias
Estrutura de dados - Árvores Binárias
 

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

Mini-curso Programação Paralela e Distribuída
Mini-curso Programação Paralela e DistribuídaMini-curso Programação Paralela e Distribuída
Mini-curso Programação Paralela e DistribuídaDeivid Martins
 
14-programacao-bd-Object Relational Mapper.pdf
14-programacao-bd-Object Relational Mapper.pdf14-programacao-bd-Object Relational Mapper.pdf
14-programacao-bd-Object Relational Mapper.pdfgabriel-colman
 
Computação Manycore: Uma Arquitetura muito além do Multicore!
Computação Manycore: Uma Arquitetura muito além do Multicore!Computação Manycore: Uma Arquitetura muito além do Multicore!
Computação Manycore: Uma Arquitetura muito além do Multicore!Intel Software Brasil
 
Design Patterns (MSDN Webcast)
Design Patterns (MSDN Webcast)Design Patterns (MSDN Webcast)
Design Patterns (MSDN Webcast)Giovanni Bassi
 
[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...
[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...
[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...Marcio Machado Pereira
 
Aprendizado de máquina
Aprendizado de máquinaAprendizado de máquina
Aprendizado de máquinaparasite
 
[PRJ32][Christopher] Aula 14 – controle
[PRJ32][Christopher] Aula 14 – controle[PRJ32][Christopher] Aula 14 – controle
[PRJ32][Christopher] Aula 14 – controleChristopher Cerqueira
 
Arquitetura de Computadores - Lecom - UFMG
Arquitetura de Computadores - Lecom - UFMGArquitetura de Computadores - Lecom - UFMG
Arquitetura de Computadores - Lecom - UFMGdjonatascostsa
 
Curso java 01 - molhando os pés com java
Curso java   01 - molhando os pés com javaCurso java   01 - molhando os pés com java
Curso java 01 - molhando os pés com javaMaurício Linhares
 
Domain Driven Design (DDD) - DevIsland, BH
Domain Driven Design (DDD) - DevIsland, BHDomain Driven Design (DDD) - DevIsland, BH
Domain Driven Design (DDD) - DevIsland, BHGiovanni Bassi
 
Simulador Eletromagnético em um Ambiente de Grades Computacionais
Simulador Eletromagnético em um Ambiente de Grades ComputacionaisSimulador Eletromagnético em um Ambiente de Grades Computacionais
Simulador Eletromagnético em um Ambiente de Grades ComputacionaisIgor José F. Freitas
 
Android: testes automatizados e TDD
Android: testes automatizados e TDDAndroid: testes automatizados e TDD
Android: testes automatizados e TDDDextra
 

Semelhante a Algoritmos e Estruturas de Dados, edição de 2013/2014 (20)

Palestra
PalestraPalestra
Palestra
 
01-Paradigmas.pdf
01-Paradigmas.pdf01-Paradigmas.pdf
01-Paradigmas.pdf
 
Mini-curso Programação Paralela e Distribuída
Mini-curso Programação Paralela e DistribuídaMini-curso Programação Paralela e Distribuída
Mini-curso Programação Paralela e Distribuída
 
Introdução Play framework
Introdução Play frameworkIntrodução Play framework
Introdução Play framework
 
14-programacao-bd-Object Relational Mapper.pdf
14-programacao-bd-Object Relational Mapper.pdf14-programacao-bd-Object Relational Mapper.pdf
14-programacao-bd-Object Relational Mapper.pdf
 
CubeSats e Software Embarcado
CubeSats e Software EmbarcadoCubeSats e Software Embarcado
CubeSats e Software Embarcado
 
Computação Manycore: Uma Arquitetura muito além do Multicore!
Computação Manycore: Uma Arquitetura muito além do Multicore!Computação Manycore: Uma Arquitetura muito além do Multicore!
Computação Manycore: Uma Arquitetura muito além do Multicore!
 
Design Patterns (MSDN Webcast)
Design Patterns (MSDN Webcast)Design Patterns (MSDN Webcast)
Design Patterns (MSDN Webcast)
 
[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...
[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...
[Pereira, IC'2013] Uma nova abordagem para detecção e extracao de paralelismo...
 
Aprendizado de máquina
Aprendizado de máquinaAprendizado de máquina
Aprendizado de máquina
 
11-codigo-limpo-parte-4.pdf
11-codigo-limpo-parte-4.pdf11-codigo-limpo-parte-4.pdf
11-codigo-limpo-parte-4.pdf
 
[PRJ32][Christopher] Aula 14 – controle
[PRJ32][Christopher] Aula 14 – controle[PRJ32][Christopher] Aula 14 – controle
[PRJ32][Christopher] Aula 14 – controle
 
Arquitetura de Computadores - Lecom - UFMG
Arquitetura de Computadores - Lecom - UFMGArquitetura de Computadores - Lecom - UFMG
Arquitetura de Computadores - Lecom - UFMG
 
Curso java 01 - molhando os pés com java
Curso java   01 - molhando os pés com javaCurso java   01 - molhando os pés com java
Curso java 01 - molhando os pés com java
 
Domain Driven Design (DDD) - DevIsland, BH
Domain Driven Design (DDD) - DevIsland, BHDomain Driven Design (DDD) - DevIsland, BH
Domain Driven Design (DDD) - DevIsland, BH
 
Threads 09: Paralelismo
Threads 09: ParalelismoThreads 09: Paralelismo
Threads 09: Paralelismo
 
Simulador Eletromagnético em um Ambiente de Grades Computacionais
Simulador Eletromagnético em um Ambiente de Grades ComputacionaisSimulador Eletromagnético em um Ambiente de Grades Computacionais
Simulador Eletromagnético em um Ambiente de Grades Computacionais
 
Aula 3-lógica.pptx
Aula 3-lógica.pptxAula 3-lógica.pptx
Aula 3-lógica.pptx
 
Android: testes automatizados e TDD
Android: testes automatizados e TDDAndroid: testes automatizados e TDD
Android: testes automatizados e TDD
 
Treinamento ORM .Net
Treinamento ORM .NetTreinamento ORM .Net
Treinamento ORM .Net
 

Último

RedacoesComentadasModeloAnalisarFazer.pdf
RedacoesComentadasModeloAnalisarFazer.pdfRedacoesComentadasModeloAnalisarFazer.pdf
RedacoesComentadasModeloAnalisarFazer.pdfAlissonMiranda22
 
[Bloco 7] Recomposição das Aprendizagens.pptx
[Bloco 7] Recomposição das Aprendizagens.pptx[Bloco 7] Recomposição das Aprendizagens.pptx
[Bloco 7] Recomposição das Aprendizagens.pptxLinoReisLino
 
Mapa mental - Classificação dos seres vivos .docx
Mapa mental - Classificação dos seres vivos .docxMapa mental - Classificação dos seres vivos .docx
Mapa mental - Classificação dos seres vivos .docxBeatrizLittig1
 
11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...
11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...
11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...licinioBorges
 
Modelos de Desenvolvimento Motor - Gallahue, Newell e Tani
Modelos de Desenvolvimento Motor - Gallahue, Newell e TaniModelos de Desenvolvimento Motor - Gallahue, Newell e Tani
Modelos de Desenvolvimento Motor - Gallahue, Newell e TaniCassio Meira Jr.
 
Nova BNCC Atualizada para novas pesquisas
Nova BNCC Atualizada para novas pesquisasNova BNCC Atualizada para novas pesquisas
Nova BNCC Atualizada para novas pesquisasraveccavp
 
Manual da CPSA_1_Agir com Autonomia para envio
Manual da CPSA_1_Agir com Autonomia para envioManual da CPSA_1_Agir com Autonomia para envio
Manual da CPSA_1_Agir com Autonomia para envioManuais Formação
 
trabalho wanda rocha ditadura
trabalho wanda rocha ditaduratrabalho wanda rocha ditadura
trabalho wanda rocha ditaduraAdryan Luiz
 
A horta do Senhor Lobo que protege a sua horta.
A horta do Senhor Lobo que protege a sua horta.A horta do Senhor Lobo que protege a sua horta.
A horta do Senhor Lobo que protege a sua horta.silves15
 
GÊNERO TEXTUAL - TIRINHAS - Charges - Cartum
GÊNERO TEXTUAL - TIRINHAS - Charges - CartumGÊNERO TEXTUAL - TIRINHAS - Charges - Cartum
GÊNERO TEXTUAL - TIRINHAS - Charges - CartumAugusto Costa
 
Livro O QUE É LUGAR DE FALA - Autora Djamila Ribeiro
Livro O QUE É LUGAR DE FALA  - Autora Djamila RibeiroLivro O QUE É LUGAR DE FALA  - Autora Djamila Ribeiro
Livro O QUE É LUGAR DE FALA - Autora Djamila RibeiroMarcele Ravasio
 
Pedologia- Geografia - Geologia - aula_01.pptx
Pedologia- Geografia - Geologia - aula_01.pptxPedologia- Geografia - Geologia - aula_01.pptx
Pedologia- Geografia - Geologia - aula_01.pptxleandropereira983288
 
Slide língua portuguesa português 8 ano.pptx
Slide língua portuguesa português 8 ano.pptxSlide língua portuguesa português 8 ano.pptx
Slide língua portuguesa português 8 ano.pptxssuserf54fa01
 
UFCD_10392_Intervenção em populações de risco_índice .pdf
UFCD_10392_Intervenção em populações de risco_índice .pdfUFCD_10392_Intervenção em populações de risco_índice .pdf
UFCD_10392_Intervenção em populações de risco_índice .pdfManuais Formação
 
E agora?! Já não avalio as atitudes e valores?
E agora?! Já não avalio as atitudes e valores?E agora?! Já não avalio as atitudes e valores?
E agora?! Já não avalio as atitudes e valores?Rosalina Simão Nunes
 
D9 RECONHECER GENERO DISCURSIVO SPA.pptx
D9 RECONHECER GENERO DISCURSIVO SPA.pptxD9 RECONHECER GENERO DISCURSIVO SPA.pptx
D9 RECONHECER GENERO DISCURSIVO SPA.pptxRonys4
 
Apresentação | Eleições Europeias 2024-2029
Apresentação | Eleições Europeias 2024-2029Apresentação | Eleições Europeias 2024-2029
Apresentação | Eleições Europeias 2024-2029Centro Jacques Delors
 

Último (20)

RedacoesComentadasModeloAnalisarFazer.pdf
RedacoesComentadasModeloAnalisarFazer.pdfRedacoesComentadasModeloAnalisarFazer.pdf
RedacoesComentadasModeloAnalisarFazer.pdf
 
[Bloco 7] Recomposição das Aprendizagens.pptx
[Bloco 7] Recomposição das Aprendizagens.pptx[Bloco 7] Recomposição das Aprendizagens.pptx
[Bloco 7] Recomposição das Aprendizagens.pptx
 
Mapa mental - Classificação dos seres vivos .docx
Mapa mental - Classificação dos seres vivos .docxMapa mental - Classificação dos seres vivos .docx
Mapa mental - Classificação dos seres vivos .docx
 
11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...
11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...
11oC_-_Mural_de_Portugues_4m35.pptxTrabalho do Ensino Profissional turma do 1...
 
Modelos de Desenvolvimento Motor - Gallahue, Newell e Tani
Modelos de Desenvolvimento Motor - Gallahue, Newell e TaniModelos de Desenvolvimento Motor - Gallahue, Newell e Tani
Modelos de Desenvolvimento Motor - Gallahue, Newell e Tani
 
XI OLIMPÍADAS DA LÍNGUA PORTUGUESA -
XI OLIMPÍADAS DA LÍNGUA PORTUGUESA      -XI OLIMPÍADAS DA LÍNGUA PORTUGUESA      -
XI OLIMPÍADAS DA LÍNGUA PORTUGUESA -
 
Nova BNCC Atualizada para novas pesquisas
Nova BNCC Atualizada para novas pesquisasNova BNCC Atualizada para novas pesquisas
Nova BNCC Atualizada para novas pesquisas
 
Manual da CPSA_1_Agir com Autonomia para envio
Manual da CPSA_1_Agir com Autonomia para envioManual da CPSA_1_Agir com Autonomia para envio
Manual da CPSA_1_Agir com Autonomia para envio
 
trabalho wanda rocha ditadura
trabalho wanda rocha ditaduratrabalho wanda rocha ditadura
trabalho wanda rocha ditadura
 
A horta do Senhor Lobo que protege a sua horta.
A horta do Senhor Lobo que protege a sua horta.A horta do Senhor Lobo que protege a sua horta.
A horta do Senhor Lobo que protege a sua horta.
 
GÊNERO TEXTUAL - TIRINHAS - Charges - Cartum
GÊNERO TEXTUAL - TIRINHAS - Charges - CartumGÊNERO TEXTUAL - TIRINHAS - Charges - Cartum
GÊNERO TEXTUAL - TIRINHAS - Charges - Cartum
 
Orientação Técnico-Pedagógica EMBcae Nº 001, de 16 de abril de 2024
Orientação Técnico-Pedagógica EMBcae Nº 001, de 16 de abril de 2024Orientação Técnico-Pedagógica EMBcae Nº 001, de 16 de abril de 2024
Orientação Técnico-Pedagógica EMBcae Nº 001, de 16 de abril de 2024
 
Livro O QUE É LUGAR DE FALA - Autora Djamila Ribeiro
Livro O QUE É LUGAR DE FALA  - Autora Djamila RibeiroLivro O QUE É LUGAR DE FALA  - Autora Djamila Ribeiro
Livro O QUE É LUGAR DE FALA - Autora Djamila Ribeiro
 
CINEMATICA DE LOS MATERIALES Y PARTICULA
CINEMATICA DE LOS MATERIALES Y PARTICULACINEMATICA DE LOS MATERIALES Y PARTICULA
CINEMATICA DE LOS MATERIALES Y PARTICULA
 
Pedologia- Geografia - Geologia - aula_01.pptx
Pedologia- Geografia - Geologia - aula_01.pptxPedologia- Geografia - Geologia - aula_01.pptx
Pedologia- Geografia - Geologia - aula_01.pptx
 
Slide língua portuguesa português 8 ano.pptx
Slide língua portuguesa português 8 ano.pptxSlide língua portuguesa português 8 ano.pptx
Slide língua portuguesa português 8 ano.pptx
 
UFCD_10392_Intervenção em populações de risco_índice .pdf
UFCD_10392_Intervenção em populações de risco_índice .pdfUFCD_10392_Intervenção em populações de risco_índice .pdf
UFCD_10392_Intervenção em populações de risco_índice .pdf
 
E agora?! Já não avalio as atitudes e valores?
E agora?! Já não avalio as atitudes e valores?E agora?! Já não avalio as atitudes e valores?
E agora?! Já não avalio as atitudes e valores?
 
D9 RECONHECER GENERO DISCURSIVO SPA.pptx
D9 RECONHECER GENERO DISCURSIVO SPA.pptxD9 RECONHECER GENERO DISCURSIVO SPA.pptx
D9 RECONHECER GENERO DISCURSIVO SPA.pptx
 
Apresentação | Eleições Europeias 2024-2029
Apresentação | Eleições Europeias 2024-2029Apresentação | Eleições Europeias 2024-2029
Apresentação | Eleições Europeias 2024-2029
 

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

  • 1. Algoritmos e Estruturas de Dados Lição n.º 1 “Algoritmos e Estruturas de Dados”
  • 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. 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. Aulas •  Teóricas: Pedro Guerreiro. •  Práticas: Hamid Shahbazkia. 20140529 Algoritmos e Estruturas de Dados 4
  • 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. 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. 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. 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. 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. 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. 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. Algoritmos da escola primária •  Adição •  Subtração •  Multiplicação •  Divisão •  (Sucessor) 20140529 Algoritmos e Estruturas de Dados 12
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 2 Animação algorítmica
  • 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. 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. 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. 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. setup e draw 20140529 Algoritmos e Estruturas de Dados 30
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 3 Coleções: sacos, pilhas e filas
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 4 Pilhas
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 5 Expressões regulares
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 6 Filas
  • 110. Filas •  Implementação com listas ligadas. •  Outros assuntos •  Objetos funcionais •  Functores •  Arrays de functores 20140529 Algoritmos e Estruturas de Dados 110
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 7 Análise de Algoritmos
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 8 Ordem de crescimento do tempo de execução
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Algoritmos e Estruturas de Dados Lição n.º 9 Union-Find
  • 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. 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. 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.