Classificação por Troca

Método da Agitação Shakesort. Raramente, é possível efetuar de maneira totalmente clara a classificação de um método de ordenação. Os métodos podem ser vistos também como ordenações por permutação. Vou apresentar agora um método em que a permutação entre dois elementos é a principal característica do processo. O algoritmo subseqüente de permutação direta é baseado na comparação e permutação de pares de elementos adjacentes até que todos tenham sido ordenados. Método da Agitação (Shake sort) e uma melhoria do método ordenaçã Bolha (Bubblesort) bem sendo assim não da pra falar do ShakeSort sem antes falar no BobbleSort. Neste método, efetuam-se varreduras repetidas sobre o vetor, deslocando-se, a cada passo, para a sua extremidade direita, o maior dos elementos do conjunto que restou. Se para uma troca, o vetor for visualizado na posição vertical ao invés de na horizontal, e com o auxílio da imaginação, os elementos formem bolhas em um tanque de água, com densidades inversamente proporcionais ao valor de suas respectivas chaves, então cada varredura efetuada sobre o vetor resultaria na ascensão de uma bolha para o seu nível apropriado, de acordo com sua densidade. Este método é conhecido como Bubblesort (ordenação por bolhas). Sua forma mais simples é mostrada na rotina abaixo:
/* Procedimento que ordena o vetor pelo método de ordenação Bubblesort */

void bubbleSort(int a[], int n) { int i; // Passos do algoritmo de ordenação int j; // Índice do vetor int aux; // Variável auxiliar para a permutação de elementos for(i = 0; i < n-1; i++) { for(j = 0; j < n-1-i; j++) { if(a[j] > a[j+1]) { // Se o elemento a esquerda for maior que o a direita aux = a[j]; // Troca-se a posição destes elementos a[j] = a[j+1]; a[j+1] = aux; } } }}

A i=0 i=1 i=2 i=3 i=4 i=5 i=6

44 55 12 42 6 94 18 44 12 42 6 55 18 67 12 42 6 44 18 55 67 12 6 42 18 44 55 67 6 12 18 42 44 55 67 6 12 18 42 44 55 67 6 12 18 42 44 55 67 6 12 18 42 44 55 67 Exemplo de uma ordenação com BobbleSort.

67 94 94 94 94 94 94 94

pode-se observar que os três últimos passos do algoritmo não afetaram a ordem dos elementos do vetor. cujos demais elementos estejam ordenados será posicionada corretamente em um único passo. Seu comportamento é ilustrado a baixo. é evidente que todos os pares de elementos adjacentes de índice maior que k já se encontram na ordem desejada.Este algoritmo sugere por si próprio. mas um elemento incorretamente posicionado na extremidade direita irá deslocar-se de apenas uma posição por vez em direção a sua posição correta. ao invés de se prosseguir até o limite superior n – i predeterminado. } Entretanto. colocada de modo incorreto na extremidade esquerda de um vetor. um programador cuidadoso percebe uma assimetria peculiar: uma única bolha. Uma técnica óbvia para melhorar este algoritmo consiste em manter uma indicação informando se houve ou não ocorrência de uma permutação. Esta assimetria não natural sugere uma segunda melhoria: alterar a direção dos sucessivos passos de ordenação. Entretanto. mesmo esta melhoria pode ser por sua vez aperfeiçoada. . // sinaliza que houve uma troca } } } while(trocou && ++i < n-1). mas a posição (índice) k do vetor em que ocorreu a última permutação realizada. for(j = 0. Por exemplo. a[j+1] = aux. guardando-se não a simples informação da ocorrência de uma permutação. // Índice do vetor int aux. alguns aperfeiçoamentos. Por exemplo. // Troca-se a posição destes elementos a[j] = a[j+1]. Portanto. mas o vetor requer sete passos para a sua ordenação. aplicando-se o algoritmo para as mesmas oito chaves que foram utilizadas o exemplo anterito. Sendo assim. para determinar precocemente o término do algoritmo. No exemplo anterior. int n) { int i = 0. O algoritmo resultante desta prática se chama Shakesort (Ordenação por agitação). o algoritmo para ordenação bolha aperfeiçoado ficaria assim: /* Rotina para ordenar um vetor de n elementos pelo método Bolha aperfeiçoado */ void BubbleSort2(int a[]. a partir deste índice. j++) { if(a[j] > a[j+1]) { // Se o elemento a esquerda for maior que o a direita aux = a[j]. o prosseguimento dos exames pode ser interrompido. j < n-1-i. // Passos do algoritmo de ordenação int j. // Indica se houve permutação em um passo do { trocou = FALSE. // Variável auxiliar para a permutação de elementos int trocou. o vetor 94 12 06 18 12 42 18 44 42 55 44 67 55 94 67 06 é ordenado pelo método Bubblesort aperfeiçoado em um único passo. pois estes já encontravam-se ordenados. trocou = TRUE.

d = n-1. j < d. }while(e < d). // Limites esquerdo e direito do vetor int aux.Varredura Interação Vetor Salto esquerdo para direita Salto direto para esquerda Par comparado Ação 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2 3 4 28 26 30 24 25 26 28 30 24 25 26 28 30 24 25 26 28 24 30 25 26 28 24 25 30 26 28 24 25 30 26 28 24 25 30 26 24 28 25 30 24 26 28 25 30 24 26 28 25 30 24 26 28 25 30 24 26 25 28 30 24 26 25 28 30 24 25 26 28 30 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 28.25 24. // sinaliza a posição da última troca } } e = k. a[j-1] = a[j]. } .24 26. a[j+1] = aux.25 26.30 30. int n) { int j.25 Exemplo de aplicação do Shakesort. j++) { if(a[j] > a[j+1]) { aux = a[j]. // Variável auxiliar para a permutação de elementos do { /* Varre o vetor da esquerda para a direita */ for(j = e.25 Troca Não troca Troca Troca Fim da ação Não troca Troca Troca Fim da ação Não troca Troca Fim da ação Troca Fim da ação 28. // Índice do vetor int k = 0.24 26. /* Varre o vetor da direita para a esquerda */ for(j = d.26 28. /* Rotina para ordenar um vetor de n elementos pelo método Shakesort */ void ShakeSort(int a[]. a[j] = a[j+1]. k = j. // sinaliza a posição da última troca } } d = k.28 28. // Indica a posição da última permutação int e = 0.24 30. j > e. j--) { if(a[j-1] > a[j]) { aux = a[j-1]. k = j. a[j] = aux.

O fator 1.Eficiência da permutação direta (Bubblesort)e ( Shakesort): Mínimo Comparações (n – n) / 2 Movimentos 0 2 Médio (n – n) / 2 2 Máximo (n2 – n) / 2 3 (n2 – n) / 4 3 (n2 – n) / 2 Método do Pente – CombSort. Cada varredura rapidamente conduz as chaves para locais próximos do definitivo por meio de grandes saltos.24 26. embora também tenha consumido 9 iterações.26 24. aproximando-se da unidade.3 de redução dos saltos foi obtido pelos autores do algoritmo por simulação.26 26.30 25. quando então o bubblesort se tornará eficiente. mas pares formados por chaves que distam umas das outras uma certa distância h. Daí a denominação do método dada pelos autores. Como vemos a classificação do vetor. sendo que o de valor 1.3 foi o que apresentou melhores resultados. proposta por Lacey e Box. esta distância (também denominada salto) é progressivamente diminuída do fator 1.25 25.. Exemplo: Como o vetor possui 5 chaves.3. Um ganho significativo no método bubblesort pode ser obtido usando a estratégia de promover as chaves em direção às suas posições definitivas por saltos maiores do que apenas uma casa de cada vez.25 24. Varredura 1 2 Interação 1 2 3 4 5 6 7 8 9 Vetor 28 26 30 24 25 24 26 30 28 25 24 25 30 28 26 24 25 30 28 26 24 25 30 28 26 24 25 26 28 30 24 25 26 28 30 24 25 26 28 30 24 25 26 28 30 Salto 3 3 2 2 2 1 1 1 1 Par Comparado 28.30 Ação Troca Troca Não troca Não troca Troca Não troca Não troca Não troca Não troca 3 Exemplo de aplicação do CombSort.3. A medida que os saltos vão diminuindo de comprimento.1 a 1. as chaves estarão já bem próximas de suas posições corretas. É justamente neste ponto que reside a vantagem do combsort em relação ao bubblesort. Neste momento. o salto inicial é igual a 3.45. efetua 8 trocas.28 28. Na primeira varredura essa distância é dada pelo valor h = n div 1. As sucessivas reduções dos saltos são análogas ao ato de pentear cabelos longos e embaraçados. A redução do tempo de classificação desse método em relação ao bubblesort tradicional (sem qualquer tipo de otimização) foi da ordem de 27 vezes.28 30. o método se confunde com o bubblesort tradicional. Foram testados fatores que variavam de 1. para ordenar o mesmo vetor. demandou apenas três trocas. em média. Essa alternativa. consiste em comparar não os pares consecutivos de chaves. Nas varreduras subseqüentes. uma vez . inicialmente apenas com os dedos e depois usando pentes com espaços entre os dentes cada vez menores. até que seja igual a unidade. enquanto o bubblesort.

i++) { if(a[i] > a[i + gap]) { swapped = true. mantendo a ordem original dos elementos repetidos.gap). O algoritmo de ordenação MergeSort utiliza duas funções: Merge e MergeSort. a[i + gap] = temp. é a que vai determinar a velocidade de classificação. i < (a. O algoritmo quebra um array original em dois outros de tamanhos menores. dividindo a sequência original em pares de dados ordenados. a[i] = a[i + gap].length .3 a cada varredura. Possui complexidade logarítmica e faz parte de um grupo de algoritmos do tipo “dividir para conquistar”. Vantagens: é O(n log n) no pior caso. ou seja. for(int i = 0.que a operação de troca. Depois „h‟ é diminuído do fator 1. return gap. MergeSort é um algoritmo recursivo de ordenação que roda em O(n log n). e é estável. O conceito por trás deste algoritmo é combinar duas listas já ordenadas. e depois juntando-os. do{ swapped = false. gerando um gasto extra de memória (o dobro). ou seja.3. h := h div 1. Desvantagem: faz uso de um array auxiliar com uma série de chamadas recursivas. int temp = a[i]. no pior e melhor caso. if(gap == 9 || gap == 10) gap = 11. . recursivamente. até obter arrays de tamanho 1. Classificação por Intercalação Método de Intercalação – Mergesort. Retorna então da recursão combinando os resultados. } Na primeira varredura h := n_elem div 1. } } } while(gap > 1 || swapped).length. if(gap < 1) return 1. private static int newGap(int gap){ gap = gap * 10 / 13. boolean swapped. gap = newGap(gap). } private static void conbSort(int a[]){ int gap = a. por envolver vários acessos à memória.3.

nesse caso não é preciso fazer nada. intercala (p. v). q. até ficar com todos os elementos separados. v). ou seja. também conhecido como ordenação por fusão ou por intercalação.. 0 111 111 111 1 999 999 999 2 222 222 222 3 999 999 999 4 333 333 333 5 888 888 888 6 444 444 444 7 777 777 777 8 555 555 555 9 666 666 666 10 555 555 555 .. O algoritmo recebe um vetor v[p.r-1] e rearranja o vetor em ordem crescente. Por exemplo. q. retornando no final uma única lista com todos os elementos ordenados. mergesort (p.. Intercalação (= merge) de vetores ordenados Dados vetores crescentes v[p .r-1] sem dar atenção ao estado ordenado das duas "metades". p q-1 q r-1  111 333 555 555 777 999 999 222 444 777 888 É fácil resolver o problema em tempo proporcional ao quadrado de r-p: basta ordenar o vetor v[p. Para rearranjar v[0. r-1] em ordem crescente.A função Merge tem como entrada dois vetores ordenados e retorna um único vetor ordenado. mergesort (q.. Após subdividir recursivamente o vetor inicial em n partes.. O algoritmo Merge Sort.  Então as recursões são retornadas. utiliza como estratégia a divisão do vetor a ser ordenado em duas partes. O algoritmo merge sort consiste em ir separando em metades uma lista de elementos. Este método de ordenação é altamente eficiente e tem uma formulação recursiva muito elegante. v). q-1] e v[q . v).n-1] em ordem crescente basta executar mergesort (0. // A função mergesort rearranja o vetor v[p. Essa divisão acontece recursivamente até que não seja mais possível qualquer divisão dos vetores resultantes. void mergesort (int p. combinado as metades já ordenadas. (3+6)/2 vale 4. rearranjar v[p ..r-1] // em ordem crescente.. o algoritmo procede à sua junção (merge). A base da recursão é o caso p ≥ r-1. r. até o momento em que os vetores resultantes da divisão tenham tamanho igual a 1. Após estes elementos estarem separados. n.. q-1] e v[q . O algoritmo é recursivo. Basta tratar do caso em que os vetores v[p . até que as metades tenham tamanho 1. int r. o algoritmo faz uma intercalação entre os elementos desses vetores até finalizar a pilha de processos recursivos. r.. int v[]){ if (p < r-1) { int q = (p + r)/2. r-1] não são vazios. } } O resultado da divisão por 2 na expressão (p+r)/2 é automaticamente truncado.  A função MergeSort “quebra” um vetor em duas metades e se chama recursivamente. r-1].

merge(data.. // Sort data[first+n1] to the end // Merge the two sorted halves. first + n1. int n2){ int[ ] temp = new int[n1+n2]. } while (copied1 < n1) temp[copied++] = data[first + (copied1++)].n/4-1]. E assim por diante. Na primeira rodada. int copied1 = 0. Na segunda rodada temos quatro problemas: ordenar v[0. ele só é realmente mais rápido na prática quando n é suficientemente grande. first. int I. o menor valor é armazenado no vetor de resultado e o contador do vetor auxiliar que continha o elemento é incrementado. n2). . //Implementação em Java do metodo Merge sort public static void mergeSort(int[ ] data. n/2-1] e ordenar v[n/2 . int n){ int n1 int n2 if (n > 1){ n1 = n / 2.111 999 222 333 999 444 777 888 555 555 666 111 222 333 999 999 444 555 555 666 777 888 111 222 333 444 555 555 666 777 888 999 999 Como o número de elementos do vetor é reduzido à metade em cada chamada do mergesort. while (copied2 < n2) temp[copied++] = data[first + n1 + (copied2++)]. n1). while ((copied1 < n1) && (copied2 < n2)) { if (data[first + copied1] < data[first + n1 + copied2]) temp[copied++] = data[first + (copied1++)]. } A cada comparação...n1.. n1. i < n1+n2...n/2-1]. i++) data[first + i] = temp[i]. // Sort data[first] through data[first+n1-1] mergesort(data. first. n-1].n-1]. mergesort(data.3n/4-1] e v[3n/4. dando início a uma nova comparação. n2). } } private static void merge(int[ ] data. int copied2 = 0. for (i = 0. int n1. v[n/2. o número total de "rodadas" é log2n. Por último. nosso problema original é reduzido a dois outros: ordenar v[0 . else temp[copied++] = data[first + n1 + (copied2++)]. v[n/4. int first. Como mergesort é mais complexo. int copied = 0. o algoritmo avalia qual o vetor auxiliar cujos elementos ainda não foram inseridos no vetor resultado e faz a inserção dos mesmos. int first. n2 = n .

first + n1. mergeSort(v. n1. int fim) { int tamanho = fim . posicao < tamanho.inicio) { if (temp[i] < temp[j]) { temp[i++] } temp[j++] } temp[j++]) } temp[i++]. first. usando * índices i e j para cada trecho de vetor da mesclagem */ int i = 0. first.private int[] vetor. n2 = n . 0. n2).1) { if(i <= meio . /* * Método recursivo que divide o vetor em dois e depois os mescla e ordena */ public static void mergeSort(int[ ] v. } } . utilizando o vetor temporário. n2). int j = meio . mergeSort(v. temp. merge(v. recebe um elemento de um trecho ou outro for (int posicao = 0. inicio + posicao.inicio + 1. int first. int meio.n1. tamanho). //A depender das condições. } } /* * Ordena dois trechos ordenados e adjacente de vetores e ordena-os conjuntamente */ private void mesclar(int inicio. int n){ int n1 int n2 if (n > 1){ n1 = n / 2.inicio + 1. posicao++) { vetor[inicio + posicao] = if (j <= tamanho . /* * Inicialização de um vetor temporario para auxiliar na ordenação o * vetor temporário é uma cópia do trecho que será ordenado */ int[] temp = new int[tamanho]. System.arraycopy(vetor. /* * Laço para ordenação do vetor. n1).