6. Pesquisa e Ordenação Fernando Silva DCC-FCUP Estruturas de Dados Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 1 / 30 Pesquisa de Informação A pesquisa eficiente de informação é extremamente relevante, seja: pesquisa num catálogo indexado por uma relação de ordem, e.g. alfabética, como seja uma lista telefónica; pesquisa interna nos registos de uma base de dados: registos ordenados ou não ordenados; pesquisa de informação em páginas Web (pesquisa de texto); pesquisa de informação em dados binários, normalmente imagens, etc. A pesquisa sequencial é dos casos mais simples e é normalmente usada quando não se tem uma relação de ordem na informação a pesquisar. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 2 / 30 Pesquisa Sequencial A pesquisa sequencial consiste em procurar um valor x numa sequência de valores v [] com dimensão N. Vejamos a pesquisa com valores inteiros e strings (veremos mais tarde como fazer o mesmo com apenas um método): Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 3 / 30 Pesquisa Binária A pesquisa binária é uma estratégia eficiente de pesquisa de um valor x numa sequência ordenada de valores v [] indexado no intervalo (e, d): Ideia do algoritmo: Um exemplo: Seja m a posição média de v[] Comparar x com v[m] I I I Se x==v[m] então encontrou senão se x<v[m], procura x no intervalo [e,m-1] senão (x>v[m]), procura x no intervalo [m+1,d] Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 4 / 30 Pesquisa Binária: versão recursiva Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 5 / 30 Estruturas de Dados 6 / 30 Pesquisa Binária: versão não recursiva Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Pesquisa Indirecta Seja int o[] um vector que determina a ordem dos elementos de uma sequência de nomes String nomes[]. Como pesquisar a string nome no vector nomes[]? Efectuar pesquisa binária dos nomes através do vector auxiliar o[] que define a ordem dentro de nomes[]. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 7 / 30 6. Pesquisa e Ordenação Estruturas de Dados 8 / 30 Pesquisa Indirecta Fernando Silva (DCC-FCUP) Algoritmos de Ordenação Em que é que a ordenação pode ajudar para resolver tarefas importantes de programação? Verificar unicidade – Como verificar se os elementos de um conjunto S são todos distintos? I Ordenando S, os elementos repetidos ficam seguidos, pelo que basta verificar se S[i] = S[i + 1] para algum 1 ≤ i < n. Remover repetições – Como remover todas as repetições de um dado elemento de S? I Ordenar S e depois percorrer os seus elementos com 2 cursores. Prioritizar eventos – Suponha que são dados um conjunto de tarefas para fazer, cada uma com o seu prazo. Ordenar os eventos de acordo com a data de “deadline” permite colocar as tarefas na ordem correcta. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 9 / 30 Algoritmos de Ordenação (cont.) Média/Selecção – suponha-se que se pretende o k-ésimo item do conjunto S. I Ordenar por ordem crescente. O valor procurado está em S[k]. Contagem/frequência – Qual é o elemento mais frequente de S. Intersecção/Reunião – a operação é trivial se os conjuntos estiverem ordenados. Procura eficiente – como verificar se um dado elemento x pertence a um conjunto S? I Se esta for uma operação frequente, compensa ordenar primeiro para depois usar pesquisa binária. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 10 / 30 Método de ordenação: selecção de máximo/mínimo Na posição i do vector v [], coloca-se o maior (ou menor) elemento entre os ainda não ordenados, i.e. que se encontram entre i + 1 e n − 1 Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 11 / 30 Ordenação Indirecta Considere um vector com 4 objectos, cada um representando uma pessoa com nome, ano e local de nascimento, ordenados por ano de nascimento. Como obter esta informação ordenada, por ordem alfabética dos nomes das pessoas, sem alterar o vector original? Usar um vector auxiliar O[] que contenha a ordenação do vector original? Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 12 / 30 Ordenação Indirecta (cont.) Usando o método de ordenação por selecção de mínimo e admitindo que a classe Pessoa tem atributos nome, anonasc e concelho. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 13 / 30 Método de ordenação: inserção Este método de ordenação é designado de “inserção” porque, em cada passo, procura inserir um novo elemento na posição correcta de uma sequência já ordenada. Os passos associados ao método são: Supor que o vector v[] está já parcialmente ordenado (posições 0 a p-1); adicionar um novo elemento não ordenado é simples: I I I basta ir de v[p] até v[0] para determinar a posição k do novo valor; deslocar os elementos entre v[k+1] e v[p]; adicionar o valor em v[k] Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 14 / 30 Método de ordenação: inserção O algoritmo pode ser simplificado se usarmos a posição 0 do vector com um valor de paragem (mais pequeno que qualquer outro do vector; supondo ordenação crescente): Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 15 / 30 Método de ordenação1: bolha (bubble-sort) Este método consiste em comparar os elementos do vector dois a dois e trocar os elementos que não estiverem na ordem desejada. O nome vem do facto de em cada iteração as comparações sucessivas de elementos deslocarem o maior (ou menor) elemento para as últimas posições do vector como se fosse o deslizar de uma bolha. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 16 / 30 Método de ordenação2: bolha (bubble-sort) O algoritmo 1, faz sempre n(n − 1)/2 comparações. Se o vector ficar ordenado a meio das iterações, faz comparações desnecessárias. Uma optimização consiste em usar uma flag que sinaliza a ocorrência de trocas numa iteração. O algoritmo termina se não ocorrerem trocas na iteração anterior. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 17 / 30 Método de ordenação3: bolha (bubble-sort) Uma melhoria adicional é recordar em cada iteração onde se verificou a última troca e fazer o ciclo-j ir só até essa posição na iteração seguinte: As diferenças na eficiência destes 3 algoritmos de bubblesort só serão visíveis para valores grandes de n. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 18 / 30 Ordenação: Método Quick-Sort O quicksort é um dos algoritmos de ordenação mais eficiente (O(n log n)). Baseia-se numa estratégia recursiva dividir-para-conquistar. Supondo que se pretende ordenar um vector S, consiste em: 1 2 3 4 Se o número de elementos em S é 0 ou 1, S está ordenado; Escolher um elemento v ∈ S. Chamamos-lhe pivot. Dividir S − v em dois vectores: S1 = {x ∈ S − {v } | x < v }, elementos menores que o pivot S2 = {x ∈ S − {v } | x ≥ v }, elementos maiores que o pivot Note-se que os elementos em S1 e S2 não estão necessariamente ordenados. Aplicar recursivamente o algoritmo a S1 e S2 Neste algoritmo, o esforço está na partição do vector, sendo a estratégia de escolha do pivot importante. A junção dos sub-vectores já ordenados é uma simples colagem. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 19 / 30 Estruturas de Dados 20 / 30 Ilustração do Algoritmo Quicksort Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Implementação do quick-sort Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 21 / 30 Implementação do quick-sort (cont.) Um método para concretizar a partição do vector, consiste em usar dois cursores a e b que inicialmente correspondem às extremidades do vector. Os cursores são deslocados em direcções opostas de forma a separarem os elementos menores que o pivot dos elementos maiores ou iguais. As decisões a tomar são: Se v [a] < pivot então a = a + 1 Se v [b] ≥ pivot então b = b − 1 Troca v [a] com v [b] e incrementa a e b A partição está completa quando b < a. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 22 / 30 Implementação do quick-sort (cont.) Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 23 / 30 Optimização do Algoritmo Quicksort O quicksort é menos eficiente do que outros métodos para valores pequenos de N (número de elementos no vector). Assim, uma estratégia para melhorar ainda mais o desempenho do quicksort, é evitar que ele seja aplicado quando os sub-intervalos atingem um valor relativamente baixo 10 a 15 elementos, e nesses casos usar outro método. Teríamos algo como: Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 24 / 30 Ordenação: Método Mergesort A ideia base do algoritmo consiste em: subdivide o vector em duas partes, aplicar o algoritmo a cada parte, quando as duas partes estiverem ordenadas (2 vectores ordenados), faz-se a junção para produzir um vector final ordenado. 6 24 28 3 13 10 7 30 22 16 8 25 12 5 separa em 2 vectores 3 6 7 10 13 24 28 5 8 12 16 22 25 30 mergesort de cada vector 3 6 7 10 13 24 28 5 8 12 16 22 25 30 junta as listas ja ordenadas. 3 5 6 7 8 10 12 13 16 22 24 25 28 30 Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 25 / 30 Algoritmo mergesort - exemplo divide-and-conquer Exemplo típico da técnica de programação dividir-para-conquistar. procuramos dividir o nosso problema em sub-problemas mais pequenos que sabemos resolver e depois juntamos os resultados dos sub-problemas para obter o resultado final. subdividir o vector inicial em sub-vectores até termos dimensão 1. Juntar ordenadamente dois vectores de dimensão 1 é fácil, podemos depois juntar dois vectores de dimensão 2, etc. O método principal consiste em: Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 26 / 30 Mergesort - junção das partes Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 27 / 30 Funções de ordenação do Java Usar a class java.util.Arrays que contém vários métodos para ordenar, e.g. static void sort(Object[] a) static void sort(Object[] a, Comparator c) Existem outros métodos específicos para inteiros ou strings. Em Java, a classe Arrays permite a invocação do método sort() para ordenar não apenas inteiros mas outro tipo de objectos. Para tal, os objectos têm de pertencer a classes que implementem a interface Comparable: public interface Comparable { int compareToObject(Object o); } Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 28 / 30 Funções de ordenação do Java (cont.) Como exemplo, suponhamos que temos a seguinte classe: class Pessoa implements Comparable { String nome; int idade; ... public int compareTo(Object o) { Pessoa outra= (Pessoa) o; if (idade < outra.idade) return -1; if (idade > outra.idade) return 1; return 0; } } Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 29 / 30 Funções de ordenação do Java Deste modo, se tivermos um vector de objectos do tipo Pessoa é possível ordenar: Pessoa p[]= new Pessoa[N]; ... Arrays.sort(p); //ordena o vector de objectos Pessoa por idade É ainda possível melhorar este código com os genéricos do Java. Fernando Silva (DCC-FCUP) 6. Pesquisa e Ordenação Estruturas de Dados 30 / 30