Ordenação Prof. Túlio A. M. Toffolo Prof. Marco Antonio M. Carvalho http://www.toffolo.com.br
BCC402 – Aula 04
Algoritmos e Programação Avançada
Aplicações
• Como testar se todos os elementos de um conjunto S são únicos?
1. Ordene o conjunto – O(n log n)
2. Percorra o conjunto uma única vez buscando por repetições entre os elementos adjancentes O(n)
2
Aplicações
• Remover duplicações de um elemento
1. Ordene o conjunto – O(n log n)
2. Encontre o elemento no vetor – O(log n)
3. Para cada repetição adjacente, troque o elemento com o último do vetor (utilize um índice para indicar o final)
3
Aplicações
• Remover todas as duplicações de um vetor
1. Ordene o conjunto – O(n log n)
2. Teste cada item i com i+1. Em caso de repetição, remova-o. – O(n)
3. Uma estrutura auxiliar pode facilitar as operações.
4
Aplicações
• Priorizar elementos
• Suponha que seja dado um conjunto de tarefas, cada uma com um deadline específico.
• Ordenamos as tarefas de acordo com sua prioridade.
• Ordenar é uma forma de implementar uma fila de prioridade.
5
Aplicações
• Encontrar o k-ésimo maior item de um conjunto.
1. Ordene o conjunto – O(n log n)
2. Retorne o k-ésimo elemento vetor – O(1)
• Encontrar a mediana de um conjuntos
1. Se o tamanho n for ímpar, basta encontrar o (n/2)-ésimo maior elemento do vetor.
2. Se n for par, basta retorna a média do (n/2)-ésimo e do (n/2+1)-ésimo maiores elementos do vetor.
6
Aplicações
• Contar frequências
• Por exemplo: qual o item que mais ocorre em um vetor?
• Ordenamos o vetor – O(n log n).
• Em seguida, basta percorrer o vetor e contar as ocorrências adjacentes – O(n).
7
Aplicações
• Reconstruir a ordem original de um vetor
• Como restaurar a ordem original de um vetor após uma série de permutação.
1. Adicione um item extra no registro (indicando a posição original) – O(n).
2. Ordene de acordo com este item – O(n log n)
8
Aplicações
• Interseção e união de conjuntos
• Se ambos conjuntos estiverem ordenados, podemos fazer em O(n).
• Basta percorrer os vetores pegando sempre o menor item de cada vetor e colocá-lo no conjunto.
9
Aplicações
• Busca eficiente
• Vetores ordenados podem ser pesquisados em tempo O(log n) através de busca binária.
• Para um dado número z, encontrar dois elementos x e y em que x + y = z.
• Se os vetores estiverem ordenados, basta testar extremos (dois índices i e j), incrementando i e reduzindo j de acordo com os resultados.
• Custo: O(n)
10
Complexidade assintótica
• Algoritmos de ordenação por comparação
• Quicksort – O(n2); O(n log n) no caso médio
• Heapsort – O(n log n)
• MergeSort – O(n log n)
• Shellsort – O(n2) limite forte desconhecido :)
• InsertSort – O(n2)
• SelectSort – O(n2)
• Bubblesort – O(n2)
11
Ordenação estável e não-estável
• Ordenação estável:
• A ordem dos elementos em que há empate é preservada.
• Ordenação não-estável:
• Não há garantia de que a ordem os elementos de empate será preservada.
12
Ordenação estável e não-estável
• Algoritmos de ordenação estáveis:
• MergeSort – O(n log n)
• InsertSort – O(n2)
• Bubblesort – O(n2)
• Algoritmos ordenação não-estáveis:
• Quicksort – O(n2); O(n log n) no caso médio
• Heapsort – O(n log n)
• Shellsort – O(n2) limite forte desconhecido :)
• SelectSort – O(n2)
13
Maratona de Programação:
Quais algoritmos utilizar?
MergeSort
• Método estável.
• Custo O(n log n).
• Disponível na STL: função stable_sort
• Exemplos:
15
MergeSort (STL)
16
#include <iostream>#include <algorithm>#include <vector>using namespace std;int main () { double mydoubles[] = {3.14, 1.41, 2.72, 4.67, 1.73}; vector<double> myvector(mydoubles,mydoubles+5); stable_sort (myvector.begin(), myvector.end()); for (it=myvector.begin(); it!=myvector.end(); ++it) cout << " " << *it; cout << endl; return 0;}
QuickSort
• Algoritmo mais rápido para entradas maiores.
• Método não é estável.
• Método qsort da stdlib:
17
#include <stdlib.h>int main() { ... qsort(vetor, n, sizeof(int), &comparador);}int comparador(const void *a, const void *b) { if (*(int*)a < *(int*)b) return -1; if (*(int*)a > *(int*)b) return 1; return 0;}
QuickSort (STL)
18
#include <iostream>#include <algorithm>#include <vector>using namespace std;int main () { double mydoubles[] = {3.14, 1.41, 2.72, 4.67, 1.73}; vector<double> myvector(mydoubles,mydoubles+5); sort (myvector.begin(), myvector.end()); for (it=myvector.begin(); it!=myvector.end(); ++it) cout << " " << *it; cout << endl; return 0;}
QuickSort (ordenação personalizada)
19
#include <iostream>#include <algorithm>#include <vector>using namespace std;bool compare_as_ints (double i, double j) { return (int(i)<int(j));}int main () { double mydoubles[] = {3.14, 1.41, 2.72, 4.67, 1.73}; vector<double> myvector(mydoubles,mydoubles+5); sort (myvector.begin(), myvector.end(), compare_as_ints); for (it=myvector.begin(); it!=myvector.end(); ++it) cout << " " << *it; cout << endl; return 0;}
QuickSort (ordenação multi-critério)
20
typedef struct { int nro_problemas; int penalty; char[50] equipe;} Equipe;bool equipe_compare(Equipe &a, Equipe &b) { if (a.nro_problemas < b.nro_problemas) return true; else if (b.nro_problemas < a.nro_problemas) return false; else if (a.penalty < b.penalty) return true; return false;}
Busca em vetores ordenados
• Método bsearch da stdlib:
21
#include <stdlib.h>int comparador(const void *a, const void *b) { if (*(int*)a < *(int*)b) return -1; if (*(int*)a > *(int*)b) return 1; return 0;}int main() { int vetor[] = { 1,3,15,50 }; int n = 4; int k = 15; int *p = bsearch(&k, vetor, n, sizeof(int), &comparador); // (*p) será igual ao elemento}
Busca em vetores ordenados
• Método binary_search da STL:
• Retorna true ou false… Pouco útil em alguns casos…
22
int main() { int vetor[] = { 1, 3, 15, 50 }; vector<int> v(vetor, vetor+4); if (binary_search (v.begin(), v.end(), 3)) cout << "found!\n"; else cout << "not found.\n";}
Busca em vetores ordenados
• STL: lower_bound, upper_bound e equal_range
• Fazem busca binária e retornam iteradores. Uso varia em caso de haver mais de um elemento com a mesma chave:
• lower_bound: retorna o primeiro elemento
• equal_range: retorna um pair contendo a primeira e a última ocorrência do elemento
• upper_bound: retorna o última elemento
23
Busca em vetores ordenados
24
#include <iostream>#include <algorithm>#include <vector>using namespace std;int main () { int myints[] = {10,20,30,30,20,10,10,20}; vector<int> v(myints,myints+8); // 10 20 30 30 20 10 10 20 vector<int>::iterator low,up; sort (v.begin(), v.end()); // 10 10 10 20 20 20 30 30 low=lower_bound (v.begin(), v.end(), 20); up= upper_bound (v.begin(), v.end(), 20); cout << "lower_bound at position " << int(low - v.begin()) << endl; cout << "upper_bound at position " << int(up - v.begin()) << endl; return 0;}
Ordenação em tempo linear
Ordenação em tempo linear
• Algoritmos de ordenação por comparação: • Insertion Sort (Método de Inserção);
• Quicksort;
• Merge Sort (Ordenação por Intercalação);
• Heapsort...
• Possuem limite assintótico superior: O(n lg n);
• Devem efetuar pelo menos Ω(n lg n) comparações no pior caso;
• Podem existir algoritmos melhores?
26
Ordenação em tempo linear
• A resposta é SIM, desde que: • A entrada possua características especiais;
• Algumas restrições sejam respeitadas;
• O algoritmo não seja puramente baseado em comparações;
• A implementação seja feita da maneira adequada.
• Tempo linear: Θ(n);
• Algoritmos: • Ordenação por contagem (Counting sort);
• Radix sort;
• Bucket sort. 27
Ordenação por contagem
• Pressupõe que cada elemento da entrada é um inteiro na faixa de 0 a k, para algum inteiro k;
• Idéia básica:
• Determinar para cada elemento da entrada x o número de elementos maiores que x;
• Com esta informação, determinar a posição de cada elemento
• Ex.: Se 17 elementos forem menores que x então x ocupa a posição de saída 18.
28
Ordenação por contagem
• Algoritmo:
• Assumimos que o vetor de entrada é A[1,...,n];
• Outros dois vetores são utilizados:
• B[1,...,n] – armazena a saída ordenada;
• C[1,...,k] – é utilizado para armazenamento temporário.
29
Ordenação por contagem
1 for i ← 0 to k
2 do C[i] ← 0
3 for j ← 1 to length[A]
4 do C[A[j]] ← C[A[j]] + 1
5 for i ← 1 to k
6 do C[i] ← C[i] + C[i −1]
7 for j ← length[A] down to 1
8 do B[C[A[j]]] ← A[j]
9 C[A[j]] ← C[A[j]] −1 30
1 2 3 4 5 6 7 8
A 2 5 3 0 2 3 0 3
k = 5
0 1 2 3 4 5
C
1 2 3 4 5 6 7 8
B
Ordenação por contagem
1 for i ← 0 to k
2 do C[i] ← 0
3 for j ← 1 to length[A]
4 do C[A[j]] ← C[A[j]] + 1
5 for i ← 1 to k
6 do C[i] ← C[i] + C[i −1]
7 for j ← length[A] down to 1
8 do B[C[A[j]]] ← A[j]
9 C[A[j]] ← C[A[j]] −1 31
1 2 3 4 5 6 7 8
A 2 5 3 0 2 3 0 3
k = 5
0 1 2 3 4 5
C 0 0 0 0 0 0
1 2 3 4 5 6 7 8
B
Ordenação por contagem
1 for i ← 0 to k
2 do C[i] ← 0
3 for j ← 1 to length[A]
4 do C[A[j]] ← C[A[j]] + 1
5 for i ← 2 to k
6 do C[i] ← C[i] + C[i −1]
7 for j ← length[A] down to 1
8 do B[C[A[j]]] ← A[j]
9 C[A[j]] ← C[A[j]] −1 32
1 2 3 4 5 6 7 8
A 2 5 3 0 2 3 0 3
k = 5
0 1 2 3 4 5
C 2 0 2 3 0 1
1 2 3 4 5 6 7 8
B
Ordenação por contagem
1 for i ← 0 to k
2 do C[i] ← 0
3 for j ← 1 to length[A]
4 do C[A[j]] ← C[A[j]] + 1
5 for i ← 1 to k
6 do C[i] ← C[i] + C[i −1]
7 for j ← length[A] downto 1
8 do B[C[A[j]]] ← A[j]
9 C[A[j]] ← C[A[j]] −1 33
1 2 3 4 5 6 7 8
A 2 5 3 0 2 3 0 3
k = 5
0 1 2 3 4 5
C 2 2 4 7 7 8
1 2 3 4 5 6 7 8
B
Ordenação por contagem
1 for i ← 0 to k
2 do C[i] ← 0
3 for j ← 1 to length[A]
4 do C[A[j]] ← C[A[j]] + 1
5 for i ← 1 to k
6 do C[i] ← C[i] + C[i −1]
7 for j ← length[A] down to 1
8 do B[C[A[j]]] ← A[j]
9 C[A[j]] ← C[A[j]] −1 34
1 2 3 4 5 6 7 8
A 2 5 3 0 2 3 0 3
k = 5
0 1 2 3 4 5
C 0 2 2 4 7 7
1 2 3 4 5 6 7 8
B 0 0 2 2 3 3 3 5
Ordenação por contagem
• O tempo de execução é dado em função do valor de k;
• Roda em tempo Θ(n + k);
• Se tivermos k = O(n), então o algoritmo executa em tempo Θ(n);
• Exemplo prático de uso: vídeo locadora
35
36
Radix Sort
• Pressupõe que as chaves de entrada possuem limite no valor e no tamanho (quantidade de dígitos);
• Ordena em função dos dígitos (um de cada vez): • A partir do mais significativo; • Ou a partir do menos significativo?
• É essencial utilizar um segundo algoritmo estável para realizar a ordenação de cada dígito.
37
• A partir dos dígitos menos significativos:
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
Radix Sort – Funcionamento
38
• A partir dos dígitos menos significativos:
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
7 2 03 5 54 3 64 5 76 5 73 2 98 3 9
Radix Sort – Funcionamento
39
• A partir dos dígitos menos significativos:
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
7 2 03 5 54 3 64 5 76 5 73 2 98 3 9
Radix Sort – Funcionamento
40
• A partir dos dígitos menos significativos:
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
7 2 03 5 54 3 64 5 76 5 73 2 98 3 9
7 2 03 2 94 3 68 3 93 5 54 5 76 5 7
Radix Sort – Funcionamento
41
• A partir dos dígitos menos significativos:
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
7 2 03 5 54 3 64 5 76 5 73 2 98 3 9
7 2 03 2 94 3 68 3 93 5 54 5 76 5 7
Radix Sort – Funcionamento
42
• A partir dos dígitos menos significativos:
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
7 2 03 5 54 3 64 5 76 5 73 2 98 3 9
7 2 03 2 94 3 68 3 93 5 54 5 76 5 7
3 2 93 5 54 3 64 5 76 5 77 2 08 3 9
Radix Sort – Funcionamento
43
• A partir dos dígitos menos significativos: Como ficaria a partir o dígito mais significativo?
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
7 2 03 5 54 3 64 5 76 5 73 2 98 3 9
7 2 03 2 94 3 68 3 93 5 54 5 76 5 7
3 2 93 5 54 3 64 5 76 5 77 2 08 3 9
Radix Sort – Funcionamento
44
• A partir dos dígitos menos significativos: E se a ordenação não fosse estável?
3 2 94 5 76 5 78 3 94 3 67 2 03 5 5
7 2 03 5 54 3 64 5 76 5 73 2 98 3 9
7 2 03 2 94 3 68 3 93 5 54 5 76 5 7
3 2 93 5 54 3 64 5 76 5 77 2 08 3 9
Radix Sort – Funcionamento
45
Radix Sort – Pseudo Código
• Como dito anteriormente, o Radix Sort consiste em usar um outro método de ordenação (estável) para ordenar as chaves em relação a cada dígito;
• O código, portanto, é muito simples: 1 for i ← 1 to d 2 do utilize um algoritmo estável para ordenar o array A pelo i-ésimo dígito
• Onde: • d é número de dígitos; • A é o array de entrada.
46
Bucket Sort
• Assume que a entrada é gerada por um processo aleatório que distribui os elementos de forma uniforme sobre o intervalo [0,1);
• A idéia do Bucket Sort é dividir o intervalo [0,1) em n subintervalos de mesmo tamanho (baldes), e então distribuir os n números nos baldes;
• Uma vez que as entradas são uniformemente distribuídas não se espera que muitos números caiam em cada balde;
47
Bucket Sort
• Para produzir a saída ordenada, basta ordenar os números em cada balde, e depois examinar os baldes em ordem, listando seus elementos;
• A função para determinação do índice do balde correto é ;
• Vamos a um exemplo com 10 números
• A é o array de entrada;
• B é o array com os baldes.
⎣ ⎦][iAn×
48
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / 9 /
A
B
49
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 / 2 / 3 / 4 / 5 / 6 / 7 8 / 9 /
A
B
0,78 /
50
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 / 3 / 4 / 5 / 6 / 7 8 / 9 /
A
B
0,78 /
0,17 /
51
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 / 3 4 / 5 / 6 / 7 8 / 9 /
A
B
0,78 /
0,17 /
0,39 /
52
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 / 7 8 / 9 /
A
B
0,78 /
0,17 /
0,39 /
0,26 /
53
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 / 7 8 / 9 /
A
B
0,72
0,17 /
0,39 /
0,26 /
0,78 /
54
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 / 7 8 / 9
A
B
0,72
0,17 /
0,39 /
0,26 /
0,78 /
0,94 /
55
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 / 7 8 / 9
A
B
0,72
0,17 /
0,39 /
0,21
0,78 /
0,94 /
0,26 /
56
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 / 7 8 / 9
A
B
0,72
0,12
0,39 /
0,21
0,78 /
0,94 /
0,26 /
0,17 /
57
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 / 7 8 / 9
A
B
0,72
0,12
0,39 /
0,21
0,78 /
0,94 /
0,26 /
0,17 /
0,23
58
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 7 8 / 9
A
B
0,72
0,12
0,39 /
0,21
0,78 /
0,94 /
0,26 /
0,17 /
0,23
0,68 /
59
Bucket Sort – Funcionamento
0,78 0,17 0,39 0,26 0,72 0,94 0,21 0,12 0,23 0,68
0 / 1 2 3 4 / 5 / 6 7 8 / 9
A
B
0,72
0,12
0,39 /
0,21
0,78 /
0,94 /
0,26 /
0,17 /
0,23
0,68 /
Ordenação em tempo linear
• Foram vistos três algoritmos de ordenação linear (tempo Θ(n)). Que são então melhores que os algoritmos de ordenação por comparação (tempo O(n lg n));
?????
• Entretanto, nem sempre é interessante utilizar um destes três algoritmos:
• Todos eles pressupõem algo sobre os dados de entrada a serem ordenados.
60
Perguntas?
Top Related