CENTRO DE CIÊNCIAS EXATAS DEPARTAMENTO DE COMPUTAÇÃO TEORIA DA COMPUTAÇÃO Algoritmos de Ordenação Alunos: André Ricardo Gonçalves Luiz Gustavo Andrade dos Santos Paulo Roberto Silla Profa. Linnyer Beatrys Ruiz LONDRINA - PR 2007
André Ricardo Gonçalves Luiz Gustavo Andrade dos Santos Paulo Roberto Silla Algoritmos de Ordenação Trabalho apresentado à Universidade Estadual de Londrina, como parte de requisito de avaliação do 3 o Bimestre da disciplina de Teoria da Computação, do curso de Ciência da Computação sob orientação da Profa. Linnyer Beatrys Ruiz. LONDRINA - PR 2007
Sumário 1 Introdução 4 2 Algoritmos de Ordenação 5 2.1 Bubble Sort................................... 6 2.2 Insertion Sort.................................. 7 2.3 Selection Sort.................................. 8 2.4 Merge Sort.................................... 9 2.5 Quick Sort.................................... 10 2.6 Heap Sort.................................... 11 2.7 Count Sort.................................... 12 2.8 Bucket Sort................................... 13 2.9 Radix Sort.................................... 14 3 Conclusão 16 Apêndice 17 Referências Bibliográficas 23 3
Capítulo 1 Introdução Métodos ordenação de elementos sempre foi uma área atrativa para os pesquisadores da Ciência da Computação. Em busca de métodos eficientes para resolver tal problema, pesquisadores analisavam algumas situações do dia-a-dia, como a maneira que os jogadores de cartas ordenavam as cartas que continha, para facilitá-lo durante o jogo. Decorrente disto vários algoritmos de ordenação foram apresentados, como o Bubble Sort, Selection Sort, Quick Sort, dentre outros. Este trabalho tem por objetivo apresentar de forma objetiva as principais características de alguns métodos de ordenação, suas vantagens e desvantagens, suas restrições e o tempo computacional de cada um deles. 4
Capítulo 2 Algoritmos de Ordenação Os algoritmos de ordenação são algoritmos que colocam os elementos de uma lista em uma determinada ordem, ou seja dada uma vetor de elementos de entrada n 1, n 2, n 3,..., n k, o algoritmo será aplicado a este vetor de forma que a saída seja da forma n 1 n 2 n 3,..., n k. Existem vários algoritmos destinados a este fim, a maioria deles baseado na idéia de Inserção, Seleção e Permutação. A eficiência dos algoritmos de ordenação é analisada segundo alguns critérios, como a complexidade computacional, quantidade de memória utilizada, quanto ao uso de recursão, estabilidade do algoritmo e outros. Esses critérios por sua vez são analisados pelo número de movimentos e o número de comparação efetuadas durante a execução do algoritmo [Wir86]. Quanto aos critérios apresentados, segue a definição de alguns deles abaixo: Complexidade Computacional Está relacionado com o comportamento do algoritmo no melhor, médio e pior caso, que é definido pelo número de comparações efetuadas pelo algoritmo em relação ao tamanho do vetor de entrada. A complexidade é comumente apresentada como uma função assintótica em relação a n, que é igual ao número de elementos do vetor de entrada. Estabilidade A estabilidade é definida pela não realização de trocas inúteis, como a troca de elementos iguais. Um algoritmo é dito ser estável se o mesmo não efetua trocas inúteis. A seguir são apresentados alguns dos principais algoritmos de ordenação, como uma breve explicação de seu funcionamento e algumas características específicas como a análise computacional deste algoritmo. 5
2.1 Bubble Sort É um dos mais simples dos algoritmos de ordenação, entretanto é o menos eficiente. Considere um vetor disposto na vertical. Este método bolha faz com que um elemento mais leve (menor) situado ao final do vetor ( fundo ) flutue até o início deste. Para isso, são feitas comparações e trocas para cada par de elementos consecutivos. Ou seja, é um método de permutação direta que consiste em comparar um elemento com todos os elementos subseqüentes no vetor, verificando se o elemento é maior que o elemento analisado, se for, faz a troca dos elementos. Este processo é realizado até o último elemento do vetor. Complexidade de tempo: Melhor caso: n2 2 Vetor ordenado ou quase ordenado. Pior caso: 2n 2 Vetor na ordem inversa. Médio caso: 5n2 2 Assintoticamente: Todos os casos com complexidade O(n 2 ) Este método é um dos mais demorados que existe, por ser de ordem quadrática até mesmo em seu melhor caso e também devido à grande quantidade de trocas realizadas, mas ele pode sofrer algumas melhorias: Verificar se o vetor já está ordenado, antes de chegar ao último elemento a ser analisado no vetor (se já estiver ordenado, não há necessidade de mais comparações). Neste caso, o algoritmo pode ter uma melhor performance do que a quadrática, mas sem garantia para a maioria dos casos; Armazenar a posição na qual o vetor não sofreu nenhuma troca, para que na próxima verificação, o algoritmo reinicie o ciclo a partir dessa marcação, não necessitando chegar até essa posição passando pelos antecessores; Alternar a direção dos sucessivos passos de ordenação: Shake Sort, um Bubble Sort que age tanto da esquerda para a direita quanto da direita para a esquerda alternadamente (bidirecional). O Shake Sort também pode utilizar armazenamento de posições para os dois sentidos ou verificação de inalteração de posições. Mesmo com tais melhorias o Bubble Sort é ainda pior do que os outros algoritmos de permutação. O Shake Sort é utilizado quando já se sabe a priori se o vetor está quase em ordem, porém este caso é muito raro na prática. 6
2.2 Insertion Sort Consiste em um algoritmo simples, baseado na ordenação pela comparação durante a inserção. Seu funcionamento obedece a definição a seguir: Definição 2.1 Sejam V e V vetores. Inicialmente o número de elementos de V é n, enquanto V está vazio. A cada iteração, um elemento de V (entrada) será retirado, da esquerda para a direita e inserido em V (saída) na posição correta de acordo com a ordenação desejada, resultando em V o vetor ordenado. Exemplo 2.1 Estado inicial: V = {3,7,4,8,1} V = Estado após a retirada do elemento 3 de V e, inserção do mesmo em V : V = {7,4,8,1} V = {3} Estado após a retirada do elemento 7 de V e, inserção do mesmo em V : V = {4,8,1} V = {3,7} Estado após a retirada do elemento 4 de V e, inserção do mesmo em V : V = {8,1} V = {3,4,7} Estado após a retirada do elemento 8 de V e, inserção do mesmo em V : V = {1} V = {3,4,7,8} Estado após a retirada do elemento 1 de V e, inserção do mesmo em V : V = V = {1,3,4,7,8} Observe que a cada estado, V está sempre ordenado. A seguir, descrevemos as características do Insertion Sort Implementação O algoritmo possui simplicidade de implementação; Eficiência É eficiente para pequenos conjuntos de dados e também para conjuntos onde os dados encontram-se parcialmente ordenados; Melhor caso Um vetor já ordenado, onde é executado em tempo O(n), em que n é o tamanho da entrada; 7
Pior caso Um vetor no qual seus elementos estão em ordem inversa, sendo executado em tempo O(n 2 ), em que n é o tamanho da entrada. Tempo médio O Insertion Sort é, em média, executado em tempo O(n 2 ), com n representando o tamanho da entrada. 2.3 Selection Sort O Selection Sort é baseado no seguinte princípio: 1. Escolha o elemento de menor valor; 2. Troque os elementos do ponteiro corrente, a k, pelo elemento encontrado no passo anterior; 3. Incremente o ponteiro atual, a k+1 e repita as operações anteriores agora com a apenas os n 1 elementos restantes, depois os n 2, até restar somente um elemento, o maior deles. Neste algoritmo o número de comparação das chaves é independente da ordem inicial do vetor, assim o número de comparações é definido por: C = (n2 n) (2.1) 2 Já o número de movimentações é mínimo se o vetor já está ordenado como mostra a equação 2.2, e o número máximo de movimentações é realizado se o vetor está ordenado na ordem inversa, como mostra a equação 2.3 [Wir86]. M min = 3 (n 1) (2.2) M max = n2 + 3 (n 1) (2.3) 4 Com isso podemos concluir que os algoritmo Selection Sort é mais indicado do que o algoritmo Insertion Sort, exceto nos seguintes casos: as chaves estão inicialmente ordenadas; as chaves estão inicialmente quase ordenadas. 8
2.4 Merge Sort O Merge Sort aplica a técnica de divisão e conquista para a ordenação [Goo04]. Baseia-se na junção de duas seqüências ordenadas resultando em uma única, também ordenada. A técnica da divisão e conquista para ordenar uma seqüência S de n elementos é dividida em três etapas, segundo [Goo04]: 1. Divisão: retornar S caso ela possua um ou zero elemento. Caso contrário, remover todos os elementos de S, inserindo a primeira metade ( n/2 ) em S 1 e a segunda metade ( n/2 ) em S 2 ; 2. Recursão: ordenar S 1 e S 2 de modo recursivo; 3. Conquista: devolver os elementos para S, realizando um merge entre S 1 e S 2 de maneira a manter a seqüência S ordenada. Abaixo notamos a figura 2.1 que mostra a execução do algoritmo Merge Sort: Figura 2.1: Exemplo da execução do algotirmo Merge Sort. Detalhando um pouco mais a divisão nota-se que enquanto o número de elementos da seqüência for maior que um, devem ser criadas duas novas seqüências, cada uma com metade dos elementos da seqüência anterior e o algoritmo de divisão deve ser chamado recursivamente para essas duas novas seqüeências. Já o processo de junção deve ser realizado da seguinte maneira: remover, iterativamente, o menor elemento entre S 1 e S 2 e inserí-lo na posição final de S. Quando uma das 9
seqüências estiver vazia, todo o restante da outra deverá ser movido para o final de S. [Goo04] propõe um algoritmo, para esta finalidade, que pode ser encontrado no apêndice, com o nome de junção. Podemos destacar algumas características do Merge Sort: Eficiência É uma boa escolha quando a entrada não cabe totalmente na memória principal, tendo de ser armazenada em dispositivos mais lentos, como um disco rígido, pois aproveita melhor os dados trazidos em bloco do disco; Tempo de execução Não depende do estado inicial da seqüência de entrada. Logo, tanto no melhor quanto no pior e, conseqüentemente no caso médio, o Merge Sort executa em O(log 2 n), com n igual ao número de elementos da entrada. 2.5 Quick Sort O Quick Sort é um método de ordenação por particionamento e é o mais eficiente que se conhece considerando casos gerais, ou seja, não-específicos, mas é não-estável. Ele tem como base os algoritmos de ordenação por permutações, como o BubbleSort. Porém sua estratégia baseia-se na solução de problemas conhecida como dividir para conquistar, na qual consiste em escolher um pivô, um elemento qualquer do vetor para ser o elemento que dividirá o vetor em dois segmentos (sub-vetores), sendo estes os problemas menores da estratégia. Esta divisão é lógica, e não física, como acontece com o MergeSort. Após esta divisão, efetua-se um processo de separação dos demais elementos, tendo como parâmetro de separação o valor do elemento escolhido como pivô. Primeiramente, o vetor é varrido da esquerda para a direita, até que se encontre um elemento a > pivô; e também da direita para a esquerda a fim de encontrar um elemento b < pivô. Assim, achado os dois elementos a e b, faz-se a troca deles. Este processo será realizado mais vezes, a partir de b e a, caso haja necessidade, até que todos os elementos à esquerda do pivô sejam menores ou iguais a ele; e da mesma forma, todos os elementos à sua direita sejam maiores ou iguais. Após esta etapa, o elemento escolhido como pivô estará na sua posição correta em relação à ordenação do vetor. No caso de não se conseguir escolher algum elemento em um dos segmentos, a troca é feita com o próprio pivô. Após esta partição do vetor, realiza-se o processo de recursão para os segmentos. Estes segmentos serão considerados como sendo vetores originais, assim também serão particionados, gerando seus segmentos, de tamanho cada vez menor. Assim, o processo de particionamento se repete até que se obtenha vetores contendo apenas uma posição, e com isso, todos os pivôs serão atingidos e como estes estão em suas devidas posições, se 10
obtém a ordenação global. O Quick Sort apresenta diferentes tempos de ordenação para os casos médio, mínimo e máximo, pois dependem da escolha do pivô. Se o pivô for o elemento médio e mediano (separando a mesma quantidade de elementos maiores e menores do que ele em cada lado) em toda chamada da recursão, o tempo é mínimo, ou seja, O(n log(n)). Caso o pivô seja o elemento maior ou menor em cada recursão, o tempo é máximo, ou seja, O(n 2 ). O tempo médio se obtém quando pega o pivô aleatoriamente ou o elemento médio. Isto é necessário pois, quando o vetor já estiver ordenado, ao tomar o pivô como sendo o primeiro elemento, cairá no pior caso do algoritmo. Então é recomendado escolher o pivô aleatoriamente ou o elemento médio. Caraterísticas: Ordena vetores ordenados e inversamente ordenados com velocidade praticamente igual. Caso médio (pivô aleatório) é inferior ao caso ótimo diferindo apenas por um fator logarítmico de 2 ln(2). Assim, o caso médio possui complexidade de tempo de ordenação assintoticamente igual ao seu melhor caso. Preferência por vetores maiores e bastante desordenados. Se escolhendo no vetor o pivô como o elemento central, o caso ótimo do Quick Sort é o vetor inicialmente ordenado. Considerando caso médio, o Quick Sort é de 2 a 3 vezes mais rápido do que o Heap Sort (não-assintótico). 2.6 Heap Sort O Heap Sort também conhecido como método de ordenação por árvores é baseado no método de seleção direta. Este algoritmo utiliza um estrutura de dados chamada de heap, que consiste em uma árvore cuja raiz é o elemento de menor valor, caso seja um heap de mínimo, ou o de maior valor, caso seja um heap de máximo. O Heap é uma estrutura de dados em forma de uma árvore, cuja raiz é o elemento mínimo ou máximo, dependendo do tipo do heap. Para efetuar a construção do heap a partir do vetor de entrada, o tempo gasto é O(n logn), em uma análise mais complexa este tempo pode ser dado como O(n) [Wir86]. A figura 2.2 apresenta um exemplo de um heap. Com heap construído o HeapSort apenas cria o heap a partir do vetor de entrada, e o algoritmo apenas vai removendo do heap e colocando no vetor de saída. 11
Figura 2.2: Exemplo de um Heap de Máximo Segundo [Wir86] o Heap Sort tem uma complexidade computacional na ordem de O(n logn), mesmo no pior caso, este valor é calculado da seguinte maneira: para a construção do heap é necessário O(n), e mais O(logn) para extrair os elementos do heap. E o número médio de movimentos é de aproximadamente n/2 log(n). O Heap Sort tem vantagem sobre o Merge Sort, pois não necessita de memória auxiliar, mas uma boa implementação do Quick Sort é em geral mais rápido [Miy99]. Algumas características do Heap Sort são descritas abaixo: O Heap Sort não é recomendado para vetores de entrada com poucos elementos, devido a complexidade do heap; Quanto maior o vetor de entrada, mais eficiente este algoritmo é; Mesmo no pior caso, o Heap Sort é O(n logn), este excelente desempenho no pior caso é uma das principais vantagens do Heap Sort; O Heap Sort é mais eficiente com vetores de entrada, cujos elementos estão parcialmente ordenados na ordem inversa, dando uma característica não-natural ao algoritmo; Se o vetor de entrada estiver na ordem inversa, o processo de construção do heap leva zero movimentos para ser realizado. 2.7 Count Sort Também conhecido como Counting Sort, é um método de ordenação linear, isto é, tem complexidade O(n). Ele não se baseia em comparações, e sim em indexação. Para isso, 12
os valores a serem ordenados devem ser inteiros, pois estes serão usados como chaves no vetor auxiliar por ele requisitado (repare que ele precisa de memória extra para trabalho, porém não é recursivo). Assim, este algoritmo não funciona com entradas em ponto flutuante, pois se usasse uma lista encadeada, seu desempenho possivelmente não seria satisfatório, devido ao percurso na lista para cada busca. Ele necessita ainda que se saiba a priori o valor do maior elemento k da entrada para criar o vetor auxiliar de contagem dos elementos da entrada, cujos valores devem variar desde 0 até k. Importante observar que O(k) deve ser linear para que o algoritmo também o seja, o que acontece na prática. O algoritmo é formado por quatro laços de repetição que varrem ou o vetor original ou o de trabalho. Primeiramente ele cria o vetor de trabalho e atribui zero a todas posições, consumindo O(k). Depois varre o vetor original e incrementa as quantidades dos elementos da entrada no vetor de trabalho utilizando a indexação. Assim, o tempo gasto é de apenas O(n) pois a indexação consome tempo O(1). Agora ele percorre o vetor de trabalho a fim de obter a quantidade de elementos menores ou iguais ao valor de cada posição. Para isso, ele faz a soma do valor de uma posição com a anterior, começando pela segunda e indo até a última. Este laço leva O(k). Por último, cursa o vetor de entrada ao contrário, para assim garantir estabilidade. Essa propriedade é importante quando se trabalha com outros dados junto ao elemento que sofrerá ordenação, mas para isso será necessário outro vetor auxiliar, o vetor de saída. Este vetor, logicamente terá o mesmo tamanho da entrada, e será preenchido da seguinte forma: o elemento de entrada será colocado na posição de saída indicada pelo seu próprio valor no vetor de trabalho. Logo em seguida, decrementase o valor da contagem do vetor de trabalho na posição utilizada, para que, quando ocorra repetição de elementos na entrada, estes não ocupem o mesmo lugar no vetor de saída. Esta etapa também é O(n), e no total temos O(k)+O(n)+O(k)+O(n) = O(n), adotando que O(k) é linear. Finalmente, não é recomendado usar este algoritmo quando se tem um valor muito grande de k, pois precisaria de muita memória e em muitos casos sem necessidade. Por outro lado, é bom utilizá-lo quando a comparação dos elementos representar um problema, pois este método usa operações aritméticas e atribuições para fazer a ordenação ao invés de comparações. 2.8 Bucket Sort Algoritmo baseado na idéia do uso de chaves como índices em um arranjo B de Buckets. Tal arranjo, possui entradas no intervalo de 0 a [N 1], onde N representa a quantidade de chaves. Cada posição de B é uma lista de itens. Por exemplo, o elemento f será armazenado em B[f]. 13
O Bucket Sort funciona da seguinte maneira: seja S a seqüência que deseja-se ordenar. Cada elemento de S é inserido em seu Bucket. Em seguida, ordena-se os Buckets e o conteúdo é devolvido em S. Para garantir a estabilidade desse algoritmo, deve-se, segundo [Goo04] remover sempre o primeiro elemento da seqüência S e das seqüências B[i]. O Bucket Sort possui tempo de execução O(n + N) onde n representa a quantidade de elementos na sequüência S. Apresenta bom desempenho quando N é pequeno se comparado com n. No melhor caso, espera-se que cada elemento de S seja armazenado em um Bucket diferente e, em contrapartida, em seu pior caso, todos os elementos da seqüência são armazenados no mesmo Bucket. 2.9 Radix Sort O Radix Sort é um algoritmo baseado no conceito de ordenação por contagem, que consiste em criar uma tabela de N entradas, onde N é o número de elementos diferentes que pode ser inseridos no vetor de entrada. E nesta tabela são inseridos contadores, incrementadoos se os mesmos forem correspondentes a chave de valor i, após realizado este processo, é conhecido a quantidade de posições necessárias para cada valor de chave, assim os elementos são transferidos para as posições corretas no novo vetor ordenado[ric03]. O Radix Sort utiliza o conceito de ordenação por contagem, porém ele realiza a contagem com apenas uma parte da representação do elemento, a qual é denominada raiz. O processo de contagem é realizado com esta parte várias vezes até que a representação total do elemento seja analisada. Duas classificações podem ser realizadas com o radix sort, LSD (Least Significant Bits) e MSD (Most Significant Bits), que define a ordem que serão analisados as partes do elemento, se LSD o processo é iniciado com os bits menos significativos e se MSD com os bits mais significativos. Com esta especificação do radix sort apresentada acima é possível perceber que este algoritmo pode ser aplicado a ordenação de strings como um vetor de entrada [bg, bd, a, fk, fj, e] sendo ordenado como [a, bd, bg, e, fj, fk]. Este algoritmo é muito utilizado para ordenação lexicográfica. Para ordenação de números inteiros o radix sort pode trabalhar uma maneira simples, como a parte do elemento sendo um dígito do elemento, assim a ordenação pode ser feita ordenando os dígitos menos signficativos para os mais significativos. Como é mostrado no exemplo 2.2: Exemplo 2.2 Dado o vetor de entrada [36, 0, 9, 25, 1, 49, 64, 16, 81, 4], realizando a or- 14
denação pelo dígito menos significativo, temos algo como mostrado na figura 2.3 Figura 2.3: Vetor ordenado pelo dígito menos significativo Após isto, realiza-se a ordenação dos próximos dígitos, como neste caso os valores são no máximo de dois dígitos, então na ordenação do próximo dígito o vetor já estará ordenado, como é mostrado na figura 2.4. Figura 2.4: Vetor ordenado Uma desvantagem do Radix Sort é a necessidade de um espaço de memória auxiliar de tamanho N, que é o tamanho do alfabeto dos elementos de entrada. Se a quantidade de memória for limitada este é uma desvantagem significante deste algoritmo. A complexidade computacional é da ordem de (i N), onde i é o tamanho do vetor de entrada, sendo então de complexidade temporal linear O(n). 15
Capítulo 3 Conclusão Apresentamos, neste trabalho, alguns dos principais algoritmos de ordenação. Mostramos as características de cada um, analisando o tempo computacional no melhor, pior e médio casos. Através deste estudo, pode-se concluir que não existe um algoritmo de ordenação que seja eficiente para todos os casos, já que para cada situação existe um algoritmo que melhor a solucione. Um exemplo mostrado foi o caso do Merge Sort que funciona melhor em situações onde a entrada não cabe toda na memória princial necessitando ser armazenada em um meio de acesso mais lento e, em contrapartida, o Quick Sort, se bem implementado, é um dos algotimos mais rápidos quando a entrada está toda em memória. Podem ser realizadas pequenas modificações nos algoritmos originais, a fim de garantir características extras, como no caso do Bucket Sort, onde a estabilidade é garantida através da remoção do primeiro elemento de cada seqüência. 16
Apêndice A Pseudo-Código do Algoritmo de ordenação Bubble Sort Algoritmo segundo [Wir86]: BubbleSort(vetor A,n) 1. para i 0 ate n faca 2. para j n - 1 decrescendo ate j = i faca 3. se A[j] < A[j - 1] entao 4. Troque(A[j], A[j - 1]); 5. fim se 6. fim para 7. fim para Pseudo-Código do Algoritmo de ordenação Insertion Sort Algoritmo segundo [Miy99]: InsertionSort(Vetor V, Inteiro n) 1. j 2 2. enquanto j n faça 3. AUX V [j]; 4. i j 1; 5. enquanto i > 0 e V [i] > AUX faça 6. V [i + 1] V [i]; 7. j j 1; 8. fim enquanto 9. V [i + 1] AUX; 10. j j + 1; 11. fim enquanto 17
Pseudo-Código do Algoritmo de ordenação Selection Sort Algoritmo segundo [Wir86]: SelectionSort(A,tam) 1. para i 1 ate tam faca 2. min i; 3. para j i + 1 ate tam faca 4. se A[min] > A[j] 5. min j; 6. fim se 7. fim para 8. se i min 9. A[min] A[i]; 10 fim se 11. fim para Pseudo-Código do Algoritmo de ordenação Merge Sort Algoritmo segundo [Miy99]: MergeSort(Vetor V, inicio, f im) 1. se inicio < fim então 2. meio inicio+fim 2 3. MergeSort(V, inicio, meio); 4. MergeSort(V, meio + 1, fim); 5. Juncao(V [inicio..meio], V [meio + 1..fim], V ); 6. fim se Método auxiliar do Merge Sort Junção(S 1,S 2, S): % Entrada sequência S 1 e S 2 ordenadas de forma não-decrescente e uma sequência S vazia. % Saida sequência S contendo os elementos de S 1 e S 2 ordenados de forma não-decrescente com as sequências S 1 e S 2 tornando-se vazias 18
1. enquanto S 1 ou S 2 não forem vazias faca 2. se S 1.firstElement() S 2.firstElement() então 3. {remova o primeiro elemento de S 1 para o final de S} 4. S.insertLast(S 1.removeFirst()) 5. senão 6. {remova o primeiro elemento de S 2 para o final de S} 7. S.insertLast(S 2.removeFirst()) 8. {remova os elementos restantes de S 1 para S} 9. enquanto(nãos 1.isEmpty()) faça 10. S.insertLast(S 1.removeFirst()) 11. {remova os elementos restantes de S 2 para S} 12. enquanto(nãos 2.isEmpty()) faça 13. S.insertLast(S 2.removeFirst()) Pseudo-Código do Algoritmo de ordenação Quick Sort Algoritmo segundo [Miy99]: QuickSort(vetor V, inicio, fim) 1. se inicio < fim entao 2. meio Particiona( V, inicio, fim ) 3. QuickSort( V, inicio, meio -1 ); 4. QuickSort( V, meio + 1, fim ); 5. fim se fim algoritmo Método auxiliar do Quick Sort Particiona(vetor V, inicio, fim) 1. Pivo V[inicio]; 2. ESQ inicio + 1; 3. DIR fim; 4. enquanto ESQ < DIR faca 5. enquanto V[ESQ] Pivo e ESQ fim faca ESQ ESQ + 1; 6. enquanto V[DIR] > Pivo e DIR inicio faca DIR DIR - 1; 7. se ESQ < DIR entao Troque(V[ESQ], V[DIR]); 8. fim enquanto 9. MEIO DIR; 19
10. Troque(V[inicio, V[MEIO]]); 11. Retorne MEIO; Pseudo-Código do algoritmo de Ordenação Heap Sort Algoritmo segundo [Miy99]: HeapSort(A,i) 1. ConstroiHeap(A,i); 2. para i n decrescendo ate 1 faca 3. A[i] ExtraiMax(A,i); Métodos auxiliares do Heap Sort ConstroiHeap(A,n) 1. para i [ n ] decrescendo ate 1 faca 2 2. Rebaixa(A,n,i); Rebaixa(A,n,i) 1. pai i; 2. fesq FilhoEsq(i); 3. fdir FilhoDir(i); 4. maior pai; 5. se fesq n e A[fesq] > A[pai] 6. entao maior fesq; 7. se fdir n e A[fdir] > A[maior] 8. entao maior fdir; 9. se maior pai entao 10. Troca(A[maior], A[pai]); 11. Rebaixa(A,n,maior); ExtraiMax(A,n,i) 1. se n < 1 erro; 2. max A[1]; 3. A[1] A[n]; 4. n n - 1; 20
5. Rebaixa(A,n,1); 6. retorne max; Pseudo-Código do Algoritmo de ordenação Count Sort Algoritmo segundo [Miy99]: CountSort(A,B,n,k) 1. para i 1 ate k faca 2. C[i] 1; % inicializa contadores com 0 3. para j 1 ate n faca 4. C[A[i]] C[A[i]] +1; 5. para i 2 ate k faca 6. C[i] C[i] + C[i - 1]; 7. para j n decrescendo ate 1 faca 8. B[C[A[j]]] A[j]; 9. C[A[j]] C[A[j]] - 1; Pseudo-Código do Algoritmo de ordenação Bucket Sort Algoritmo segundo [Miy99]: BucketSort(S) 1. Entrada: seqüência S de itens com chaves inteiras no intervalo [0, N 1] 2. Saida: seqüência S ordenada pelas chaves, de forma não-decrescente 3. seja B um arranjo de N seqüências, cada uma inicalmente vazia 4. para cada item x em S faça 5. seja k a chave de x 6. remove x de S e o insere no fim da seqüência B[k] 7. fim para 8. para i 0 ate N 1 faça 9. para cada item x da seqüência B[i] faça 10. remove x de B[i] e o insere no fim de S 11. fim para 12. fim para 21
Pseudo-Código do Algoritmo de Ordenação Radix Sort Algoritmo segundo [Miy99]: RadixSort(A,n,d) 1. para i 1 ate d faca 2. Use um algoritmo estavel para ordenar o array A no digito i. 22
Referências Bibliográficas [Goo04] Michael T. Goodrich. Projeto de algoritmos: Fundamentos, análise e exemplos da Internet. Editora Bookman, 2004. [Miy99] F. K. Miyazawa. Notas de complexidade de algoritmos. Universidade Estadual de Campinas, 1999. [Ric03] Ivan L. M. Ricarte. Ordenação por contagem. Disponível em: http://www.dca.fee.unicamp.br/cursos/ea876/apostila/html/node24.html. Acessado em: 09 Ago. 2007, 2003. [Wir86] Niklaus. Wirth. Algoritmos e Estruturas de Dados. Editora Prentice Hall, 1986. 23