You are on page 1of 16

Métodos Internos Avançados

Shellsort
Estruturas de Dados „
„ O(n1.3)
Profa. Flávia Linhalis „ Quicksort
„ O(n log n) – pior caso: O(n2)
„ Mergesort
Ordenação Interna – Métodos Avançados „ O(n log n)
Cota Inferior para ordenação
Shell Sort „ Ordenação com ABB
Quick Sort „ O(n log n)
Merge Sort
Ordenação com ABB
„ Heapsort
Heap Sort „ O(n log n)

Shell Sort Shell Sort


„ É uma extensão do algoritmo de „ Problema com o algoritmo de ordenação por
ordenação por inserção. inserção:
„ Troca itens adjacentes para determinar o ponto de
„ É considerado um método avançado inserção.
São efetuadas n-1 comparações e movimentações
Sua ordem de complexidade é O(n1.3)
„
„ quando o menor item está na posição mais à direita no
vetor.
„ O método Shell contorna este problema permitindo
trocas de registros distantes um do outro.
Shell Sort Shell Sort
0 1 2 3 4 5 6 7

„ Os itens separados de h posições são 25 57 48 37 12 92 86 33 h=4


rearranjados.
12 57 48 37 25 92 86 33 h=4
„ Quando h = 1 o Shellsort corresponde ao
12 57 48 33 25 92 86 37 h=1
algoritmo de inserção.
12 48 57 33 25 92 86 37 h=1

12 33 48 57 25 92 86 37 h=1

12 25 33 48 57 92 86 37 h=1

12 25 33 48 57 86 92 37 h=1

12 25 33 37 48 57 86 92

Algoritmo Shell Sort Shell Sort


Procedimento shellSort(vetor de inteiro: vet, inteiro: N)
Início
i, j, h, value: inteiro;
h Å 1;
„ A razão da eficiência do algoritmo ainda não
enquanto (h < N) faça
h Å 3 * h + 1;
/* calcula h */ é conhecida.
enquanto (h > 1) faça
Início
„ A sua análise contém alguns problemas
h Å h div 3;
para i Å h até N faça
matemáticos difíceis.
Início
value Å vet[i];
„ Uma aproximação é O(n1.3)
j Å i - h;
enquanto (j >= 0) e (value < vet[j]) faça
Início
vet[j+h] Å vet[j];
j Å j - h;
FimEnquanto;
vet[j+h] = value;
FimPara;
FimEnquanto;
Fim;
Ordenação Rápida: Quick Sort Quick Sort
„ Idéia básica: dividir para conquistar. „ A parte mais delicada do método é o processo
„ Dividir (particionar) o vetor em dois vetores de partição.
menores que serão ordenados „ O vetor A é particionado em duas partes:
independentemente e combinados para „ A é rearranjado por meio da escolha arbitrária de
produzir o resultado final. um pivô x.
„ A parte esquerda com chaves menores ou iguais a x.
„ A parte direita com chaves maiores ou iguais a x.

Quick Sort Quick Sort


„ Algoritmo para o particionamento:
25 57 48 37 12 92 86 33 pivô = 37
1. Escolha arbitrariamente um pivô x.
2. Percorra o vetor a partir da esquerda até que A[i] ≥ x. 25 33 12 37 48 92 86 57 pivô = 33
3. Percorra o vetor a partir da direita até que A[j] ≤ x.
4. Troque A[i] com A[j]. 25 12 33 37 48 92 86 57 pivô = 25

5. Continue este processo até que i e j se cruzarem. 12 25 33 37 48 92 86 57 pivô = 92


„ Ao final, o vetor A[Esq..Dir] está particionado de
tal forma que: 12 25 33 37 48 57 86 92 pivô = 57

„ Os itens em A[Esq], A[Esq + 1], … , A[j] são ≤ x. 12 25 33 37 48 57 86 92


„ Os itens em A[i], A[i + 1], … , A[Dir] são ≥ x.
Quick Sort Quick Sort
procedimento QuickSort(vet[ ], IniVet, FimVet)
„ Todo elemento à esquerda de 37 é ≤ 37. var
i, j, pivo, aux: inteiro;
„ Todo elemento à direita de 37 é ≥ 37. início
i <- IniVet; j <- FimVet;

„ Ordenar os dois subvetores [25, 33, 12] e pivo <- vet[(IniVet + FimVet) div 2];
faça
[48, 92, 86, 57]. enquanto (vet[i] < pivo) faça i <- i + 1;
enquanto (vet[j] > pivo) faça j <- j – 1;
se (i <= j) então
início
aux <- vet[i]; vet[i] <- vet[j]; vet[j] <- aux;
i <- i + 1; j <- j – 1;
fim
enquanto (i <= j)
se (j > IniVet) então QuickSort(vet, IniVet, j);
se (i < FimVet) então QuickSort(vet, i, FimVet);
fim

Quick Sort Quick Sort


„ Custo no melhor caso: pivô mediano. „ Custo no pior caso: vetor ordenado.
„ O vetor de tamanho n é dividido ao meio, „ O pivô é escolhido como sendo um dos
cada metade é dividida ao meio, ..., m extremos de um arquivo já ordenado.
vezes. => n = 2m, logo, m = log2 n. „ Cada partição produz um subvetor com 0
„ Cada parte do vetor realiza n comparações elementos e outro com n -1 elementos.
(com n = partição atual do vetor). „ O vetor será particionado n vezes.
„ custo = n * m => custo = O(n log2 n). „ Cada partição fará n comparações.
„ Custo = O(n2).
Ordenação por Intercalação:
Quick Sort Merge Sort
„ O pior caso pode ser evitado empregando „ Idéia básica: dividir para conquistar.
pequenas modificações no algoritmo. „ Um vetor v é dividido em duas partes,
„ Para isso basta escolher três itens quaisquer do recursivamente.
vetor e usar a mediana dos três como pivô. „ Cada metade é ordenada e ambas são
„ É o algoritmo de ordenação interna mais intercaladas formando o vetor ordenado.
rápido que se conhece. „ Usa um vetor auxiliar para intercalar.
„ Os laços internos são extremamente simples. „ Quick sort usa um pivô vet[i] que possui o valor
„ Razão pela qual o algoritmo Quicksort é tão mediano de vet. Merge sort divide o vetor em
rápido. dois, não importando os valores.

Merge Sort Merge Sort


5 2 4 7 1 3 2 6 Algoritmo MergeSort (inicio, fim : inteiro)
Variável meio : inteiro;
5 2 4 7 1 3 2 6
1 Início
5 2 4 7 1 3 2 6 2 se (inicio < fim) então
3 meio := (inicio + fim) div 2;
5 2 4 7 1 3 2 6 divisão
4 MergeSort(inicio, meio);
intercalação 5 MergeSort(meio+1, fim);
2 5 4 7 1 3 2 6
6 Intercala(inicio, meio, fim);
2 4 5 7 1 2 3 6 7 fimse
8 Fim
1 2 2 3 4 5 6 7
Merge Sort
Merge Sort
procedimento Intercala (inicio, meio, fim) // B é o vetor auxiliar
h Å inicio; I Å inicio; j Å meio + 1;
enquanto (h <= meio) e (j <= fim) faça // laço que intercala
se A[h] <= A[j] então
5 2 4 7 1 3 2 6 B[i] Å A[h];
senão
h Å h + 1;

Só para a parte em azul B[i] Å A[j]; j Å j + 1;


5 2 4 7 deste exemplo, serão: fim se
I Å i + 1;
• 7 chamadas recursivas
5 2 4 7
fim enquanto
• 3 chamadas ao se h > meio então // copia o restante dos dados
procedimento para k Å j até fim faça
5 2 4 7 divisão intercada com os valores: B[i] Å A[k]; I Å i + 1;
fim para
(0,0,1)
intercalação senão
2 5 4 7 (2,2,3) para k Å h até meio faça
(0,1,3) B[i] Å A[k]; i Å i + 1;
fim para
2 4 5 7 fim se
para k Å inicio até fim faça // copia conjunto ordenado de volta para A
A[k] Å B[k];
1 2 2 3 4 5 5 7 fim para
fim Intercala

Merge Sort Ordenação com ABB


„ Custo: „ Esse método requer uma verredura de cada
„ Como no Quick Sort, o vetor de tamanho n é elemento de entrada e sua colocação na posição
dividido ao meio, cada metade é dividida ao meio, correta em uma árvore binária de busca (ABB).
e assim, sucessivamente „ Uma ABB é aquela onde todo filho à esquerda tem uma
„ O (n log2 n) chave menor que a do pai e todo filho à direita tem uma
chave maior (ou igual) que a do pai.
„ Utiliza muita memória:
„ Assim que cada elemento de entrada estiver na
„ Muitas chamadas recursivas posição correta na árvore, os elementos ordenados
„ Vetor auxiliar podem ser obtidos por meio de um percurso “em
ordem” pela árvore.
Ordenação com ABB Ordenação com ABB - Complexidade
„ A eficiência desse método depende da ordem inicial „ Nesse caso, o primeiro nó não exige compa-
dos elementos. rações, o segundo requer uma comparação, o
Dados originais:
Dados originais:
4 8 12 17 26 26 17 12 8 4 terceiro requer duas, e assim por diante
4 26
„ 1 + 2 + 3 + … + n-1 = (n-1) * n/2
8 17
„ Æ O(n2)
12 12

17 8

26 4

Estrutura de dados para trabalhar


Ordenação com ABB - Complexidade com ordenação em ABB

Dados originais:
12 8 17 4 26 • Todos os N elementos serão int N;
comparados deste a raiz para serem int *vet;
inseridos na árvore. Æ O(n) struct no{
12 • As comparações são reduzidas à n o {
i n t * v e t

int elem;
;

metade Æ O(log n)
i r ;
s q ;

n o * t r e e ;

8 17 struct no *dir;
• Este método é portanto O(n log n) no struct no *esq;
4 26
caso da árvore estar balanceada. };
struct no *tree;
Algoritmo para criar uma ABB Percurso em ordem
void createTree() {
criar a raiz (tree) com o primeiro elemento do vetor (vet[0])
criar um ponteiro auxiliar para percorrer a árvore (aux)
void emOrdem(no *tree) {
criar ponteiro auxiliar para armazenar o ponto de inserção (onde)
for (int i = 1; i < N; i++) {
struct no *aux = tree;
criar o novo nó if (tree != NULL) {
colocar vet[i] no novo nó emOrdem(aux->esq);
aux = tree; cout << tree->elem << " ";
while (aux != NULL) { emOrdem(aux->dir);
if (vet[i] >= aux->elem) { }
onde = &aux->dir; aux = aux->dir;
}
} else {
onde = &aux->esq; aux = aux->esq;
}
}
*onde = novo;
}
}

Quando usar o método com ABB? Heap Sort


„ Este método é indicado quando já se tem „ Utiliza uma estrutura de dados - um
uma árvore de busca binária (ABB) na heap – para ordenar.
aplicação. „ Assim como Merge Sort tem custo, no
Usada para fazer busca.
„
pior caso, O(n log2 n). Porém, não
utiliza vetor auxiliar, exigindo menos
espaço.
Heap Sort Heap Sort
„ Um heap é um vetor (array) que representa „ Uma árvore binária de profundidade/nível d é uma
uma árvore binária quase completa. árvore binária quase completa se:
0 „ Cada folha estiver no nível d ou d – 1
16 0 1 2 3 4 5 6 7 8 9 „ Para cada nó v da árvore com descendente direito no nível
16 14 10 8 7 9 3 2 4 1 d, todos os descendentes esquerdos de v que forem folhas
1 2 estão também no nível d.
14 10
„ Isto é: a árvore binária tem seus nós-folha, no
4 5 6 3
máximo, em dois níveis, sendo que as folhas devem
3 8 7 9
estar o mais à esquerda possível.

2 4 1
7 8 9

Heap Sort Heap Sort


„ Exemplo de árvore binárias quase completas. „ Exemplos de árvores binárias que não são quase completas.
Heap Sort Heap Sort
„ Uma heap observa conceitos de ordem e de „ Como acessar os elementos (pai e filhos de
forma cada nó) na heap?
„ Ordem: o item de qualquer nó deve satisfazer uma 0 0 1 2 3 4 5 6 7 8 9
relação de ordem com os itens dos nós filhos 16 14 10 8 7 9 3 2 4 1
16
„ Heap máxima (ou descendente): pai >= filhos, sendo
que a raiz é o maior elemento
„ Propriedade de heap máxima
1
14 2 10 Filhos do nó k:
„ Heap mínima (ou heap ascendente): pai <= filhos, • filho esquerdo = 2k + 1
sendo que a raiz é o menor elemento • filho direito = 2k + 2
„ Propriedade de heap mínima 3 8 4 7 5 9 6 3

„ Forma: a árvore binária tem seus nós-folha, no Pai do nó k: (k-1) div 2


máximo, em dois níveis, sendo que as folhas devem
estar o mais à esquerda possível 2 4 1
7 8 9

Heap Sort Heap Sort


„ Assume-se que: „ A idéia para ordenar usando uma heap é:
„ A raiz está sempre na posição 0 do vetor „ Construir uma heap máxima
„ comprimento(vetor) indica o número de elementos „ Trocar a raiz – o maior elemento – com o
do vetor elemento da última posição do vetor
„ tamanho_da_heap(vetor) indica o número de „ Diminuir o tamanho da heap em 1
elementos na heap armazenados no vetor „ Rearranjar a heap máxima a partir da nova raiz
„ Ou seja, embora A[1..comprimento(A)] contenha „ Repetir o processo n-1 vezes
números válidos, nenhum elemento além de
A[tamanho_do_heap(A)] é um elemento da heap,
sendo que tamanho_da_heap(A)<=comprimento(A)
Heap Sort : exemplo Heap Sort
„ O processo continua até todos os elementos
terem sido incluídos no vetor de forma
ordenada
„ É necessário:
„ Saber construir uma heap a partir de um vetor
qualquer
„ Procedimento build_max_heap
„ Saber como rearranjar a heap, i.e., manter a
propriedade de heap máxima
„ Procedimento max_heapify

Heap Sort Heap Sort


„ Procedimento max_heapify: manutenação da
propriedade de heap máxima
„ Recebe como entrada um vetor A e um índice i
„ Assume que as árvores binárias com raízes nos filhos
esquerdo e direito de i são heap máximas, mas que A[i]
pode ser menor que seus filhos, violando a propriedade de
heap máxima
„ A função do procedimento max_heapify é deixar A[i]
“escorregar” para a posição correta, de tal forma que a
subárvore com raiz em i torne-se uma heap máxima
„ Chamada para uma heap hipotética:
max_heapify(A,1), depois max_heapify(A,3),
Heap Sort Algoritmo Heap Sort
„ Retomando algoritmo do heap sort.
„ Na realidade, trabalhando-se com o vetor A
HeapSort(A)
Build_Max_Heap(A);
heap_size := length[A];
for i := length[A] -1 downto 1 do
troca (A[0], A[i]);
heap-size := heap-size – 1;
Max_Heapify(A,0,heap-size);
fim-for
fim

Algoritmo Heap Sort Algoritmo Heap Sort


Build_Max_Heap Max_Heapify
Max_Heapify(A, pos, n)
max Å 2 * pos + 1;
Build_Max_Heap(A) right Å max + 1;
for i := length[A]/2 – 1 downto 0 do if (max < n)
Max_Heapify(A, i , length[A]); if ((right < n) and (A[max] < A[right]))
max Å right;
fim-for
fim-if
fim
if (A[max] > A[pos])
troca(A[max], A[pos]);
Chama Max_Heapify para os nós que não são Max_Heapify(A, max, n);
folhas, começando do meio do vetor e indo até o fim-if
início. fim-if
fim
Heap Sort : exemplo Heap Sort : exemplo
1 1
„ Dado o vetor desordenado: 16 14 10

0 1 2 3 4 5 6 7 8 9 3 10 8 10 2 9
14 8
4 1 3 2 16 9 10 14 8 7
3 4 7 9 3 5 6 3
8 7 9 4 7 1
„ Chamar build_max_heap e obter:
0 1 2 3 4 5 6 7 8 9 2 4 1 2 1 2
16 14 10 8 7 9 3 2 4 1
...
„ Executar o laço N-1 vezes 0 1 2 3 4 5 6 7 8 9
1 2 3 4 7 8 9 10 14 16
Vetor ordenado!

Heap Sort: análise Comparação entre os Métodos


„ Custo de max_heapify: „ Algoritmos in-loco e estáveis:
„ Altura da árvore quase completa de n nós: log(n) „ Um algortitmo é in-loco se a memória adicional
„ A cada rearranjo da heap, a chave desce no máximo requerida é independente do número de registros
até as folhas, ou seja, há no máximo log(n) para ordenar.
comparações Æ log(n) „ ex: Quicksort, Heapsort, algoritmos diretos são in-loco.
Mergesort não é in-loco.
„ São n-1 passos de rearranjos Æ O(n log(n))
„ Custo de build_max_heap: „ Um método de ordenação é estável se a ordem
„ n/2 chaves são consideradas, e comparadas com seus dos itens de chaves iguais não se altera na
filhos Æ O(n) ordenação.
„ Logo, o método é O(n log(n)), sendo eficiente „ Ex: Quicksort, Inserção são estáveis. Seleção, Shellsort,
mesmo quando o vetor já está ordenado Heapsort não são estáveis.
Comparação entre os Métodos Comparação entre os Métodos
Observações sobre os métodos: „ Influência da ordem inicial do registros:
1. Shellsort, Quicksort e Heapsort têm a mesma ordem de grandeza.
1. O Shellsort é bastante sensível à ordenação ascendente ou
2. O Quicksort é o mais rápido para todos os tamanhos aleatórios
experimentados. descendente da entrada.
3. Para arquivos pequenos (500 elementos), o Shellsort é mais rápido que o 2. Em arquivos do mesmo tamanho, o Shellsort executa mais rápido
Heapsort. para arquivos ordenados.
4. Quando o tamanho da entrada cresce, o Heapsort é mais rápido que o
Shellsort. 3. O Quicksort é sensível à ordenação ascendente ou descendente da
5. O Inserção é o mais rápido para qualquer tamanho se os elementos estão entrada.
ordenados. 4. Em arquivos do mesmo tamanho, o Quicksort executa mais rápido
6. O Inserção é o mais lento para qualquer tamanho se os elementos estão em para arquivos ordenados.
ordem descendente.
7. Entre os algoritmos de custo O(n2), o Inserção é melhor para todos os 5. O Quicksort é o mais rápido para qualquer tamanho para arquivos na
tamanhos aleatórios experimentados. ordem ascendente.
6. O Heapsort praticamente não é sensível à ordenação da entrada.

Comparação entre os Métodos Comparação entre os Métodos


„ Shellsort: „ Quicksort:
É o algoritmo mais eficiente que existe para uma grande
É muito eficiente para arquivos de tamanho
„
„ variedade de situações.
moderado. „ É um método bastante frágil no sentido de que qualquer erro de
„ Mesmo para arquivos grandes, o método é cerca implementação pode ser difícil de ser detectado.
O algoritmo é recursivo, o que demanda uma pequena
de apenas duas vezes mais lento do que o „
quantidade de memória adicional.
Quicksort. „ Seu desempenho é da ordem de O(n2) operações no pior caso.
„ Sua implementação é simples e geralmente „ O principal cuidado a ser tomado é com relação à escolha do
resulta em um programa pequeno. pivô.
„ A escolha do elemento do meio do vetor melhora muito o
„ Não possui um pior caso ruim e quando encontra desempenho quando o arquivo está total ou parcialmente
um arquivo parcialmente ordenado trabalha ordenado.
menos. „ O pior caso tem uma probabilidade muito remota de ocorrer
quando os elementos forem aleatórios.
Comparação entre os Métodos Cota Inferior para Ordenação
„ Heapsort: „ Cota Inferior do Problema:
„ É um método de ordenação elegante e eficiente. „ Prova-se que é impossível resolver o
„ Apesar de ser cerca de duas vezes mais lento do problema em menos que C(n) passos para
que o Quicksort, não necessita de nenhuma
uma entrada de tamanho n
memória adicional.
„ Executa sempre em tempo proporcional a n log n. „ Algoritmo ótimo: resolve problema em
„ Aplicações que não podem tolerar eventuais tempo igual à cota inferior.
variações no tempo esperado de execução devem
usar o Heapsort.

Cota Inferior para Ordenação Cota Inferior para Ordenação


„ Cota inferior dos métodos de ordenação por comparação de „ No mínimo, quantas folhas existem nessa árvore,
elementos
„ Pode-se montar uma árvore de decisão para representar o problema
assumindo um vetor de tamanho n?
„ Exemplo: ordenação de três elementos a,b e c „ Ou: quantas possibilidades de ordenação existem?
„ n!
a<b
sim não „ Quantas comparações devem ser feitas para ordenar
n elementos?
b<c b<c
sim não sim não „ A altura máxima da árvore de decisão,
aproximadamente
a<b<c sim a<c sim a<c não c≤b≤a
não
„ Sabe-se que uma árvore binária de altura h não tem
a<c≤b c≤a<b b≤a<c b<c≤a mais do que 2h-1 folhas
„ Altura 4: 23 Æ 8 folhas no máximo
Cota Inferior para Ordenação
„ Número máximo de folhas de uma árvore binária de
altura h: 2h-1 ≈ 2h
„ Número de folhas de uma árvore de decisão para FIM
ordenação por comparação: n!
„ Portanto:
„ 2h-1 ≥ n! ou ≈ 2h ≥ n!
„ Representando via logaritmo: log(n!) ≤ h
„ Aproximação de Stirling: log(n!) = O(n log(n))
„ Resultando que h ≥ O(n log(n)) Æ h = Ω(n log(n))
„ OBS: a notação Ω (ômega) é utilizada para estudo do melhor caso

You might also like