Aula Prática #13 - Dicionários e Filas de Prioridade
(semana de 24/05 a 29/05)
Conteúdos das teóricas
Nesta aula iremos abordar conceitos de árvores binárias de pesquisa e de Filas de Prioridade. Será por isso conveniente ver o que foi falado nas teóricas:
Um TAD dicionário (também conhecido como mapa ou array associativo) é uma estrutura de dados que guarda pares (chave,valor), associando a cada chave um determinado valor. Uma implementação possível é precisamente usar uma árvore binária de pesquisa que usa a chave para decidir a posição na árvore (garantindo que só existe uma cópia de cada chave).
Código de dicionários
Faça download do código de dado nas teóricas e compile-o:
BSTMap - Dicionário de pares (chave,valor) implementado com árvores binárias de pesquisas (ex. métodos: put, get e keys)
Espreite o ficheiro BSTMapNode.java para ver como um nó é descrito, e note na assinatura como o tipo K (a chave) tem de ser comparável, mas o tipo V não (o valor).
Um primeiro teste.
Execute a classe de teste dos dicionários: $ java TestBSTMap
Espreite e acompanhe a implementação do método put da classe BSTMap, muito semelhante ao insert de uma árvore binária de pesquisa normal. Note também a assinatura dos principais métodos e o que fazem (get, put, remove e keys). Veja também como são percorridas as chaves do mapa (que são devolvidas numa lista ligada de Java). De um modo geral, quanto temos algo iterável no Java, podemos usar a sintaxe for-each:
for (TipoElemento variavel : Sequencia) {
InstrucoesCiclo
}
Um exemplo seria algo como:
importjava.util.LinkedList;importjava.util.TreeSet;importjava.util.Set;publicclassTestForEach{publicstaticvoidmain(String[] args){// Criação de Lista Ligada usando a estrutura de dados do próprio Java// Exemplo de inicialização usando sintaxe "diamond" (<>). Se não// colocarmos o tipo no construtor, o Java consegue inferir qual é// olhando para a variável que estamos a inicializar
LinkedList<String> list =new LinkedList<>();// Adicionando algumas strings à lista ligada
list.addLast("testing");
list.addLast("just");
list.addLast("for");
list.addLast("fun");// Para percorrer a lista podemos usar um for-eachfor(String s : list)
System.out.println(s);// Qualquer TAD iterável pode ser usado com um for each// Aqui fica um exemplo com... conjuntos (interface Set)// TreeSet implementa um conjunto usando árvores binárias de pesquisa
Set<Integer> set =new TreeSet<>();
set.add(42);
set.add(24);
set.add(0);// Podemos usar int por causa do autounboxingfor(int i : set)
System.out.println(i);}}
Um pequeno exercício para usar dicionários
Resolva e submeta o problema [ED172] Contagem de palavras, usando como estrutura de dados base um BSTMap.
Este problema é semelhante a problema resolvido nas aulas teóricas (ver slide 79 do capítulo 7)
Crie um dicionario do tipo <String,Integer>
Para ler as palavras use os métodos hasNext() (para saber se ainda existem palavras para ler) e next() (para ler a próxima palavra)
Para cada palavra lida:
Se a palavra ainda não existe no dicionário (usar método get()), então colocá-la no dicionário com valor igual a 1 (usar método put())
Se palavra já existe, incrementar o seu valor (usar método get() para ler valor e depois método put() para actualizar o valor)
No final é só percorrer a lista de chaves (método keys()) usando um for-each e imprimir os valores. Como é uma árvore binária de pesquisa e o keys é feito inorder, as chaves já vêm por ordem alfabética.
Exercício 2) Filas de Prioridade (Heaps)
Um TAD fila de prioridade mantém uma coleção de elementos mantendo maneira de eficiente de inserir elementos e de retirar o elemento mais prioritário (vamos assumir que o mais prioritário é o menor).
Uma maneira de implementar filas de prioridade com operações eficientes em tempo logarítmico é usar um tipo especial de árvores chamadas de heaps. Numa (min)heap, qualquer nó deve sempre ser menor que os seus filhos (o que significa que o menor de todos está na raíz). Além disso, uma heap é uma árvore completa (todos os níveis cheios, excepto potencialmente o último, com os nós colocados o mais à esquerda possível - o que implica que é equilibrada e tem alura O(log n)) e é tipicamente implementada usando um array (onde o nó na posição i tem como filhos os nós nas posições i*2 e i*2+1) [pode espreitar os slides para relembrar]
Compreendo heaps.
Use a seguinte página para visualizar e interagir um pouco com uma (min)heap, tentando depois prever o que vai acontecer ao inserir e remover:
Execute a classe de teste das filas de prioridade: $ java TestMinHeap, acompanhando a execução dos métodos, e espreitando também o ficheiro MinHeap.java para ver como é implementada a heap de mínimo.
Um problema no Mooshak com filas de prioridade .
Resolva e submeta o problema [ED215] Leilões.
Crie uma classe Person para conter um comprador com nome e preço, e implemente nela o interface Comparable, que compara tendo em conta o preço.
Depois de ler o inteiro N, crie uma MinHeap de... compradores, com capacidade para N elementos do tipo Person
Leia as linhas e:
Se for uma oferta, insera a pessoa na heap
Se for uma venda, remova a melhor oferta da heap (como temos uma heapMin, basta que o compareTo dos comparadores reflita isso) e imprima o nome da pessoa que "saiu" da heap.
Exercícios extra para consolidação de conhecimentos
Para consolidar os seus conhecimentos sobre dicionários, experimente resolver novamente o ED172 mas desta vez usando um TAD do próprio Java, o TreeMap.
Para consolidar os seus conhecimentos sobre filas de prioridade, experimente resolver novamente o ED215 mas desta vez usando um TAD do próprio Java, a PriorityQueue.
Experimente implementar uma MaxHeap, usando como base de codigo a MinHeap.
Tentem resolver o desafio desta semana (ONI_TE), que é um problema um pouco mais simples e de aplicação quase directa de uma das estruturas de dados desta aula :)
Revisite o problema [ED098] Triagem de Manchester e volte a submetê-lo mas desta vez usando uma única fila de prioridade (ao invés de uma fila por cada cor).
Exercício de Desafio
Para esta semana vou colocar um exercício da minha autoria que envolve uma das estruturas de dados que foi falada nesta aula e que que foi usado nas Olimpíadas de Informática "há muito muito tempo":
Para estes problemas de desafio não vou dar logo dicas, para vos deixar pensar, mas se quiserem mesmo resolver o problema e não estejam a conseguir (mesmo depois de terem realmente tentado), podem falar comigo para obter pistas, ou ter uma ideia de como os "atacar".
Espero que se tenham divertido com estes desafios extra que vos fui colocando e que tenham ficado com o "bichinho" de problem solving e de programação competitiva :)