OShellsort ´e uma varia¸c˜ao do Insertion sort que faz compara¸c˜ao de elementos mais distantes e n˜ao apenas vizinhos.
algoritmo. Dizemos que um vetor est´a h-ordenado se, a partir de qualquer posi¸c˜ao, considerar todo elemento a cada h posi¸c˜oes leva a uma sequˆencia ordenada. Por exemplo, o vetor A = (1,3,5,8,4,15,20,7,9,6) est´a 5-ordenado, pois as sequˆencias de elementos (1,15), (3,20), (5,7), (8,9) e (4,6) est˜ao ordenadas. J´a o vetor A = (1,3,5,6,4,9,8,7,15,20) est´a 3-ordenado, pois (1,6,8,20), (3,4,7), (5,9,15), (6,8,20), (4,7), (9,15) e (8,20) s˜ao sequˆencias ordenadas de elementos que est˜ao `a distˆancia 3
entre si. Note que um vetor 1-ordenado est´a totalmente ordenado.
A ideia do Shellsort ´e iterativamente h-ordenar o vetor de entrada com uma sequˆencia de valores de h que termina em 1. Ele usa o fato de que ´e f´acil h0-ordenar um vetor que j´a est´ah-ordenado, parah0 < h. Esse algoritmo se comporta exatamente como oInsertion sort quando h= 1. O procedimento Shellsort ´e formalizado no Algoritmo 28. Ele recebe o vetor A com n n´umeros a serem ordenados e um vetor
H com m inteiros. Ele assume queH mant´em uma sequˆencia decrescente de inteiros menores do quen tal que H[m] = 1.
Algoritmo 28: Shellsort(A, n,H, m)
1 para t= 1 at´e m fa¸ca
2 para i=H[t] + 1 at´e n fa¸ca
3 aux=A[i]
4 j =i−1
5 enquanto j ≥H[t] e A[j −H[t] + 1]> aux fa¸ca
6 A[j+ 1] =A[j−H[t] + 1]
7 j =j−H[t]
8 A[j+ 1] =aux
Note que o tempo de execu¸c˜ao do Shellsort depende drasticamente dos valores emH. Uma quest˜ao em aberto ainda hoje ´e determinar sua complexidade de tempo. Knuth por exemplo propˆos a sequˆencia 1,4,13,40,121,246, . . . e ela d´a bons resultados na pr´atica e faz O(n3/2) compara¸c˜oes. Uma sequˆencia do tipo 1,2,4,8,16, . . . d´a resultados muito ruins, j´a que elementos em posi¸c˜oes ´ımpares n˜ao s˜ao comparados com elementos em posi¸c˜oes pares at´e a ´ultima itera¸c˜ao.
Cap´ıtulo
12
Ordena¸c˜ao por intercala¸c˜ao
O algoritmo que veremos nesse cap´ıtulo usa a ideia de ordena¸c˜ao por intercala¸c˜ao e faz uso do paradigma de divis˜ao e conquista. Dado um vetorA com n n´umeros, esse algoritmo divide A em duas partes de tamanho bn/2c edn/2e, ordena as duas partes recursivamente e depois intercala o conte´udo as duas partes ordenadas em uma ´unica parte ordenada. Esse algoritmo foi inventado por Jon von Neumann em 1945.
O procedimento, MergeSort, ´e dado no Algoritmo 29, onde Combina ´e um procedimento para combinar duas partes ordenadas em uma s´o parte ordenada e ser´a visto com mais detalhes adiante. Como o procedimento recursivamente acessa partes do vetor, ele recebeA e duas posi¸c˜oes inicioef im, e seu objetivo ´e ordenar o subvetor
A[inicio..f im]. Assim, para ordenar um vetor A inteiro de n posi¸c˜oes, basta executar
MergeSort(A, 1, n).
Algoritmo 29: MergeSort(A, inicio,f im)
1 se inicio < f im ent˜ao
2 meio=b(inicio+f im)/2c
3 MergeSort(A, inicio, meio)
4 MergeSort(A, meio+ 1, f im)
5 Combina(A, inicio, meio,f im)
Na Figura 12.1 ilustramos uma execu¸c˜ao do algoritmo MergeSort. Note que a metade superior da figura corresponde `as chamadas recursivas das linhas 3 e 4. A metade inferior da figura corresponde `as chamadas recursivas ao procedimento
Figura 12.1: Execu¸c˜ao de MergeSort(A, 1, 8) para A= (7,3,1,10,2,8,15,6).
Combina(linha 5).
Veja que a execu¸c˜ao do MergeSort´e realmente simples. A opera¸c˜ao chave aqui ´e realizada peloCombina. Esse algoritmo recebe o vetor A e as posi¸c˜oes inicio, meio,
f im, e considera queA[inicio..meio] eA[meio+ 1..f im] est˜ao ordenados. Seu objetivo ´e deixar A[inicio..f im] ordenado com os mesmos elementos. Como o conte´udo a ser deixado emA[inicio..f im] j´a est´a armazenado nesse mesmo subvetor, esse procedimento faz uso de dois vetores auxiliaresB eC, que ir˜ao manter uma c´opia de A[inicio..meio] eA[meio+ 1..f im], respectivamente.
O fato dos dois vetoresB eC j´a estarem ordenados nos d´a algumas garantias. Veja que o menor de todos os elementos que est˜ao em B eC, que ser´a colocado em A[inicio], s´o pode ser B[1] ou C[1], o que for menor dentre os dois. Se B[1] < C[1], ent˜ao o elemento a ser colocado em A[inicio+ 1] s´o pode ser B[2] ou C[1], o que for menor dentre esses dois. Mas se C[1]< B[1], ent˜ao o elemento que vai para A[inicio+ 1] s´o pode ser B[1] ou C[2], o que for menor dentre esses. E, a garantia mais importante ´e que uma vez que um elemento B[i] ou C[j] ´e copiado para sua posi¸c˜ao final em A, esse elemento n˜ao precisa mais ser considerado. ´E poss´ıvel, portanto, realizar todo esse procedimento fazendo uma ´unica passagem por cada elemento de B e C.
Pela discuss˜ao acima, vemos que precisamos manter um ´ındice i para acessar elementos a serem copiadas deB, um ´ındicej para acessar elementos emC e um ´ındice
k para acessar o vetor A. A cada itera¸c˜ao, precisamos colocar um elemento em A[k], que ser´a o menor dentre B[i] e C[j]. Se B[i] (resp. C[j]) for copiado, incrementamosi
(resp. j) para que esse elemento n˜ao seja considerado novamente. Veja o procedimento
Combinaformalizado no Algoritmo 30.
Algoritmo 30: Combina(A, inicio, meio,f im)
1 n1 =meio−inicio+ 1
2 n2 =f im−meio
3 Crie vetores auxiliares B[1..n1] eC[1..n2]
4 para i= 1 at´e n1 fa¸ca
5 B[i] =A[inicio+i−1]
6 para j = 1 at´e n2 fa¸ca
7 C[j] =A[meio+j] 8 i= 1 9 j = 1 10 j =inicio 11 enquanto i < n1 e j < n2 fa¸ca 12 se B[i]≤C[j] ent˜ao 13 A[k] =B[i] 14 i=i+ 1 15 sen˜ao 16 A[k] =C[j] 17 j =j+ 1 18 k =k+ 1 19 enquanto i < n1 fa¸ca 20 A[k] =B[i] 21 i=i+ 1 22 k =k+ 1 23 enquanto j < n2 fa¸ca 24 A[k] =C[j] 25 j =j+ 1 26 k =k+ 1
Note que como o procedimento Combina usar vetores auxiliares, o MergeSort n˜ao ´e um algoritmo in-place.
Na Figura 12.2 temos uma simula¸c˜ao da execu¸c˜ao do Combina.
Considere uma execu¸c˜ao deCombina ao receber um vetor A e parˆametros inicio,
meio e f im como entrada. Note que al´em das linhas que s˜ao executadas em tempo constante, o la¸co para na linha 4 ´e executado meio−inicio+ 1 vezes, o la¸co para
Figura 12.2: Execu¸c˜ao de Combina(A, p, q, r) sobre o vetor A = (1,3,7,10,2,6,8,15,28,19,2) com parˆametrosp= 1, q = 4 e r= 8.
na linha 6´e executado f im−meio vezes, e os la¸cos enquantodas linhas 11, 19 e23
s˜ao executados ao todof im−inicio+ 1 vezes (podemos notar isso pela quantidade de valores diferentes que k assume). Se R(n) ´e o tempo de execu¸c˜ao de Combina(A,
inicio, meio, f im) onden =f im−inicio+ 1, ent˜ao claramente temosR(n) = Θ(n). Vamos agora analisar o tempo de execu¸c˜ao do algoritmo MergeSort quando ele ´e utilizado para ordenar um vetor com n elementos. Vimos que o tempo para combinar as solu¸c˜oes recursivas ´e Θ(n). Como os vetores em quest˜ao s˜ao sempre divididos ao meio no algoritmo MergeSort, seu tempo de execu¸c˜ao T(n) ´e dado por T(n) =T(bn/2c) +T(dn/2e) + Θ(n). Como estamos preocupados em fazer uma an´alise assint´otica, podemos substituir Θ(n) por n apenas, pois isso n˜ao far´a diferen¸ca no resultado obtido. Tamb´em podemos desconsiderar pisos e tetos, como visto na Se¸c˜ao 3.2.1, de forma que o tempo do MergeSort pode ser descrito por
T(n) = 2T(n/2) +n ,
para n > 1, e T(n) = 1 para n = 1. Assim, como visto no Cap´ıtulo 3, o tempo de execu¸c˜ao de MergeSort´e T(n) = Θ(nlogn).
Cap´ıtulo
13
Ordena¸c˜ao por sele¸c˜ao
Neste cap´ıtulo vamos introduzir dois algoritmos para o problema de ordena¸c˜ao que utilizam a ideia de ordena¸c˜ao por sele¸c˜ao. Em ambos, consideramos uma posi¸c˜ao i do vetor por vez, selecionamos o i-´esimo menor elemento do vetor e o colocamos em i, posi¸c˜ao final desse elemento no vetor ordenado.
13.1 Selection sort
OSelection sort ´e um algoritmo que sempre mant´em o vetor de entradaA[1..n] dividido em dois subvetores cont´ıguos separados por uma posi¸c˜ao i, um `a direita e outro `a esquerda, estando um deles ordenado. Aqui consideraremos uma implementa¸c˜ao onde o subvetor da esquerda,A[1..i], cont´em os menores elementos da entrada ainda n˜ao ordenados e o subvetor da direita, A[i+ 1..n], cont´em os maiores elementos da entrada j´a ordenados. A cada itera¸c˜ao, o maior elementox do subvetor A[1..i] ´e encontrado e colocado na posi¸c˜ao i, de forma que o subvetor da direita ´e aumentado em uma unidade1.
O Algoritmo 31 descreve o procedimento SelectionSort e possui uma estrutura muito simples, contendo dois la¸cos para aninhados. O primeiro la¸co, indexado por
i, ´e executado n−1 vezes e, em cada itera¸c˜ao, aumenta o subvetor da direita que j´a
1N˜ao ´e dif´ıcil adaptar toda a discuss˜ao que faremos considerando que o subvetorA[1..i−1] da esquerda cont´em os menores elementos ordenados e o da direita cont´em os elementos n˜ao ordenados. Com isso, a cada itera¸c˜ao, o menor elemento do subvetorA[i..n] deve ser encontrado e colocado na posi¸c˜aoi.
estava ordenado em uma unidade. Ademais, esse subvetor da direita sempre cont´em os maiores elementos deA. Para manter essa propriedade, a cada passo, o maior elemento que n˜ao est´a nesse subvetor j´a ordenado ´e adicionado ao in´ıcio dele.
Algoritmo 31: SelectionSort(A, n)
1 para i=n at´e 2fa¸ca
2 indiceM ax=i
3 para j = 1 at´e i−1 fa¸ca
4 se A[j]> A[indiceM ax] ent˜ao
5 indiceM ax=j
6 trocaA[indiceM ax] com A[i]
7 retorna A
Note que todas as linhas s˜ao executadas em tempo constante e cada um dos la¸cos
para´e executado Θ(n) vezes cada. Como um dos la¸cos est´a dentro do outro, temos que o tempo de execu¸c˜ao de SelectionSort(A, n) ´e Θ(n2).
Na Figura 13.1 temos um exemplo de execu¸c˜ao do algoritmo SelectionSort. No que segue vamos utilizar a seguinte invariante de la¸co para mostrar que o algoritmoSelectionSort funciona corretamente, isto ´e, para qualquer vetor A e n
dados na entrada, ele corretamente deixa osnelementos deAem ordem n˜ao-decrescente.
Invariante: SelectionSort
Antes de cada itera¸c˜ao do primeiro la¸co para indexado por i, o subvetor
A[i+ 1..n] est´a ordenado de modo n˜ao-decrescente e cont´em os maiores elementos deA.
Teorema 13.2
O algoritmo SelectionSort ordena qualquer vetor A com n elementos de modo n˜ao-decrescente.
Demonstra¸c˜ao. Como inicialmente i = n, a invariante ´e trivialmente satisfeita pois trata-se de um vetor sem elementos.
Fixe agora um valor de ientre 2 en e que a invariante ´e v´alida imediatamente antes da itera¸c˜ao correspondente a i do primeiro la¸co para, i.e., o subvetorA[i+ 1..n] est´a ordenado de modo n˜ao-decrescente e cont´em os maiores elementos de A. Precisamos mostrar que antes da pr´oxima itera¸c˜ao, quando teremosi−1, o subvetor A[i..n] estar´a ordenado de modo n˜ao-decrescente e conter´a os maiores elementos de A.
Note que na itera¸c˜ao correspondente ai, o segundo la¸copara (da linha3) encontra o ´ındice indiceM ax do maior elemento do vetor A[1..i] (isso pode ser formalmente provado por outra invariante de la¸co!). Na linha6, o maior elemento deA[1..i] ´e trocado de lugar com o elementoA[i]. Como, pela invariante, todos os elementos de A[i+ 1..n] s˜ao maiores do queA[i], temos queA[i..n] est´a ordenado e cont´em os maiores elementos deA, valendo assim a invariante antes da pr´oxima itera¸c˜ao.
Por fim, note que na ´ultima vez que a linha ´e executada, temosi= 1. Assim, pela invariante de la¸co, o vetor A[2..n] est´a ordenado com os maiores elementos de A. Logo, conclu´ımos que o vetor A[1..n] est´a ordenado.