INE 5408 Estruturas de Dados Ordenao Conceitos bsicos
INE 5408 Estruturas de Dados Ordenação -Conceitos básicos -Algoritmos
Introdução • A noção de um conjunto de dados ordenados é de considerável importância na nossa vida cotidiana e por conseguinte também em computação. • Exemplos: – Listas telefônicas – Listas de clientes de uma empresa – Listas de peças em um catálogo • Algoritmos eficientes para ordenar grandes quantidades de dados são de extrema importância.
Conceitos básicos • Arquivo: – um arquivo r de tamanho n é uma seqüência de n itens r[0], r[1], …, r[n-1]; – cada item em um arquivo é chamado registro. • Um arquivo é classificado por chave: – se para cada registro r[i] existe uma parte de r[i], k[i] chamada chave de classificação; – se i<j implicar que k[i] precede k[j] de acordo com algum critério qualquer predefinido.
Conceitos básicos • Exemplo: – em um catálogo telefônico o arquivo é o próprio catálogo; – um registro é uma entrada com nome, número e endereço; – o nome é a chave.
Localização de execução • Ordenação Interna é aquela realizada na memória principal do computador; • Ordenação Externa é aquela onde os registros podem estar em uma memória auxiliar (arquivo em disco).
Estabilidade • Uma técnica de ordenação é chamada estável se, para todos os registros i e j, dado que k[i] é igual a k[j]: – se r[i] precede r[j] no arquivo original, então r[i] precederá r[j] no arquivo classificado.
Classificações • Uma ordenação pode ocorrer sobre os próprios registros ou sobre uma tabela auxiliar de ponteiros: – classificação direta é uma ordenação onde um arquivo é ordenado diretamente, sendo os registros movidos de uma posição física para outra; – classificação por endereços é uma ordenação onde somente uma tabela de ponteiros é ordenada; os registros em si não são tocados.
Classificação direta
Classificação por endereços
Critérios para seleção de ordenação • Eficiência no tempo: – se um arquivo é pequeno (ex. : 20 registros) técnicas sofisticadas podem ser piores: • algoritmos complexos, módulos grandes; – número de vezes que a ordenação será executada: • se vamos classificar um conjunto de dados só uma vez, pode ser mais interessante implementar um algoritmo simples; – um programa que executa ordenações repetidamente deve ser eficiente; – esforço de programação não deve ser uma desculpa para a utilização de um algoritmo inadequado. Um sistema ineficiente não vai vender.
Critérios para seleção de ordenação • Aspectos da eficiência de tempo: – o critério básico para a determinação da eficiência de tempo de um algoritmo não é somente sua complexidade assintótica; – operações críticas: • operação crítica é uma operação básica no processo de ordenação. Ex. : a comparação de duas chaves ou o deslocamento de um registro de uma posição de memória para outra; • um algoritmo que executa todas as operações críticas na memória será geralmente mais rápido do que outro que executa algumas em memória secundária (disco).
Critérios para seleção de ordenação • Eficiência de espaço: – a técnica que escolhi é compatível com as características de hardware do ambiente para o qual a estou destinando?
Quicksort • A ordenação por troca de partição ou quicksort é provavelmente o algoritmo de ordenação mais utilizado; • Divide and Conquer; • Complexidade média O(n log n).
Quicksort • Vantagens: – simples de implementar; – muito bem compreendido; – extensas análises matemáticas de seu comportamento já foram feitas. • Desvantagens: – complexidade pode ser O(n 2) no pior caso, em que o arquivo está ordenado ou ordenado em ordem inversa. • Existem versões melhoradas.
Algoritmo básico • Quicksort trabalha particionando um arquivo em duas partes e então as ordenando separadamente; • pode ser definido recursivamente. quicksort(t. Info: a[], inteiro: lim. Inf, lim. Sup) inteiro i; início SE(lim. Sup > lim. Inf) ENTÃO i <- particione(a, lim. Inf, lim. Sup); quicksort(a, lim. Inf, i-1); quicksort(a, i+1, lim. Sup); FIM SE fim
Algoritmo básico • Os parâmetros dir e esq delimitam os subarquivos dentro do arquivo original, dentro dos quais a ordenação ocorre; • a chamada inicial pode ser feita com quicksort(a, 1, N); • o ponto crucial é o algoritmo de partição.
Particionamento em Quicksort • O procedimento de particionamento deve rearranjar o arquivo de maneira que as seguintes condições valham: – o elemento a[i] está em seu lugar final no arquivo para um i dado; – nenhum dos elementos em a[esq], . . . , a[i-1] são maiores do que a[i]; – nenhum dos elementos em a[i+1], . . . , a[dir] são menores do que a[i].
Exemplo Ordenação do vetor inicial 25 57 48 37 12 92 86 33 • Se o primeiro elemento (25) for colocado na sua posição correta, teremos: 12 25 57 48 37 92 86 33; Neste ponto: – todos os elementos abaixo de 25 serão menores e – todos os elementos acima de 25 serão maiores.
Exemplo • Como 25 está na sua posição final, o problema foi decomposto na ordenação dos subvetores: (12) e (57 48 37 92 86 33); • o subvetor (12) já está classificado; • agora o vetor pode ser visualizado: 12 25 (57 48 37 92 86 33); • repetir o processo para a[2]. . . a[7] resulta em: 12 25 (48 37 33) 57 (92 86)
Exemplo • Se continuarmos particionando 12 25 (48 37 33) 57 (92 86), teremos: • 12 25 (37 33) 48 57 (92 86) • 12 25 (33) 37 48 57 (92 86) • 12 25 33 37 48 57 (86) 92 • 12 25 33 37 48 57 86 92
25 57 48 37 12 92 86 33 12 25 57 48 37 92 86 33 12 57 48 37 92 86 33 48 37 33 57 92 86 86 92 48 37 33 33 37 33 Visão da Recursividade do Quicksort como Árvore: 86
Método para o particionamento • Considere pivô = a[lim. Inf] como o elemento cuja posição final é a procurada: – usar sempre o primeiro é só um artifício para facilitar a implementação. • Dois ponteiros alto e baixo são inicializados como os limites máximo e mínimo do subvetor que vamos analisar; • em qualquer ponto da execução, todo elemento acima de alto é maior do que x e todo elemento abaixo de baixo é menor do que x.
Método para o particionamento • Os dois ponteiros alto e baixo são movidos um em direção ao outro da seguinte forma: – 1. incremente baixo em uma posição até que a[baixo] >= pivô; – 2. decremente alto em uma posição até que a[alto] < pivô; – 3. se alto > baixo, troque a[baixo] por a[alto]. • O processo é repetido até que a condição descrita em 3 falhe (quando alto <= baixo). Neste ponto a[alto] será trocado por a[lim. Inf], cuja posição final era procurada, e alto é retornado em i.
Simulação de Quicksort lim. Inf a 23 baixo lim. Sup 66 53 75 56 21 11 33 6 11 79 43 alto Ordenamos um vetor de 12 posições de lim. Inf até lim. Sup. Escolhemos para pivô a[lim. Inf]. Para ordená-lo, vamos dividí-lo em duas partes: a parte da esquerda possuirá os elementos menores que o pivô, a parte direita os maiores.
Simulação de Quicksort lim. Inf a 23 lim. Sup 66 baixo 53 75 56 21 11 33 6 11 79 43 alto Incrementamos baixo até encontrar um elemento maior que o pivô (23). Nesse caso achamos 66.
Simulação de Quicksort lim. Inf a 23 lim. Sup 66 baixo 53 75 56 21 11 33 6 11 79 43 alto Decrementamos alto até encontrar um elemento menor que o pivô (23). Nesse caso achamos 11.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 53 75 56 baixo Trocamos a[alto] e a[baixo]. 21 11 33 6 66 alto 79 43
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 53 baixo 75 56 21 11 33 6 66 79 43 alto Continuamos a incrementar baixo até encontrar um elemento maior que o pivô (23). Nesse caso achamos 53.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 53 baixo 75 56 21 11 33 6 66 79 43 alto Continuamos a decrementar alto até encontrar um elemento menor que o pivô (23). Nesse caso achamos 6.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 75 56 baixo Trocamos a[alto] e a[baixo]. 21 11 33 53 alto 66 79 43
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 75 baixo 56 21 11 33 53 66 79 43 alto Continuamos a incrementar baixo até encontrar um elemento maior ou igual ao pivô (23). Nesse caso achamos 75.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 75 baixo 56 21 11 33 53 66 79 43 alto Continuamos a decrementar alto até encontrar um elemento menor que o pivô (23). Nesse caso achamos 11.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 11 56 baixo Trocamos a[alto] e a[baixo]. 21 75 alto 33 53 66 79 43
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 11 56 baixo 21 75 33 53 66 79 43 alto Continuamos a incrementar baixo até encontrar um elemento maior ou igual ao pivô (23). Nesse caso achamos 56.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 11 56 21 75 33 53 66 79 43 baixo alto Continuamos a decrementar alto até encontrar um elemento menor que o pivô (23). Nesse caso achamos 21.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 11 21 56 baixo alto Trocamos a[alto] e a[baixo]. 75 33 53 66 79 43
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 11 21 56 75 33 53 66 79 43 alto baixo Continuamos a incrementar baixo até encontrar um elemento maior ou igual ao pivô (23). Nesse caso achamos novamente o 56.
Simulação de Quicksort lim. Inf a 23 lim. Sup 11 6 11 21 56 75 33 53 66 79 43 alto baixo Continuamos a decrementar alto até encontrar um elemento menor que o pivô (23). Nesse caso achamos novamente o 21, mas alto é menor que baixo, então atingimos a condição de parada!
Simulação de Quicksort lim. Inf a 21 lim. Sup 11 6 11 23 56 75 33 53 66 79 43 alto baixo Agora trocamos o pivô a[lim. Inf] com a[alto] e a divisão do vetor está completa!
Simulação de Quicksort lim. Inf a 21 baixo lim. Sup 11 6 11 23 56 75 33 53 66 79 alto Agora chamamos a partição com lim. Sup = alto – 1 = 4 e particionamos o subvetor esquerdo. 1 - 12 1 -4 6 - 12 43
Simulação de Quicksort lim. Inf a 21 lim. Sup 11 6 11 23 56 75 33 53 66 79 43 alto baixo Incrementamos baixo até encontrar um elemento maior ou igual ao pivô (21) ou baixo deixar de ser menor que alto. Nesse caso encontramos alto. Decrementamos alto até encontrar um elemento menor que o pivô (21) ou alto deixar de ser maior que baixo. Nesse caso já nos encontramos em baixo e não fazemos nada.
Simulação de Quicksort lim. Inf a 11 lim. Sup 11 6 21 23 56 75 33 53 66 79 43 alto baixo Agora trocamos o pivô a[lim. Inf] com a[alto] e a divisão do vetor está completa!
Simulação de Quicksort lim. Inf a 11 baixo lim. Sup 11 6 21 23 56 75 33 53 66 79 43 alto Agora chamamos a partição com lim. Sup = alto - 1 = 3 e particionamos o subvetor esquerdo do subvetor esquerdo 1 - 12 1 -4 1 -3 6 - 12 --
Simulação de Quicksort lim. Inf a 11 lim. Sup 11 6 21 23 56 75 33 53 66 79 43 baixo alto Incrementamos baixo até encontrar um elemento maior ou igual que o pivô (11) ou baixo deixar de ser menor que alto. Nesse caso encontramos 11. Decrementamos alto até encontrar um elemento menor que o pivô (11) ou alto deixar de ser maior que baixo. Nesse caso já nos encontramos em 6 e não fazemos nada.
Simulação de Quicksort lim. Inf a 11 lim. Sup 6 11 21 23 baixo alto Trocamos a[alto] e a[baixo]. 56 75 33 53 66 79 43
Simulação de Quicksort lim. Inf a 11 lim. Sup 6 11 21 23 56 75 33 53 66 79 alto baixo Continuamos a incrementar baixo até encontrar um elemento maior ou igual ao pivô (11). Nesse caso achamos 11. 43
Simulação de Quicksort lim. Inf a 11 lim. Sup 6 11 21 23 56 75 33 53 66 79 43 alto baixo Continuamos a decrementar alto até encontrar um elemento menor que o pivô (11). Nesse caso achamos 6 e alto é menor que baixo, então atingimos a condição de parada!
Simulação de Quicksort lim. Inf a 6 lim. Sup 11 11 21 23 56 75 33 53 66 79 alto baixo Agora trocamos o pivô a[lim. Inf] com a[alto] e a divisão do vetor está completa! 43
Simulação de Quicksort lim. Sup lim. Inf a 6 11 11 21 23 56 75 Agora chamamos a partição com lim. Sup = alto - 1 = 1 e particionamos o subvetor esquerdo do subvetor esquerdo. Como lim. Sup > lim. Inf é falso, paramos. 33 53 66 79 43 1 - 12 1 -4 1 -3 1 -1 6 - 12 -- 3 -3
Algoritmo de particionamento inteiro partição(t. Info: a[], inteiro: lim. Inf, lim. Sup) variáveis t. Info: pivo, temp; inteiro: baixo, alto; início pivô <- a[lim. Inf]; alto <- lim. Sup; baixo <- lim. Inf + 1; enquanto (baixo < alto) faça enquanto (a[baixo] < pivô E baixo < lim. Sup) faça incremente baixo; // Sobe no arquivo. enquanto (a[alto] => pivô) faça decremente alto; // Desce no arquivo. se (baixo < alto) então // Troca. temp <- a[baixo]; a[baixo] <- a[alto]; a[alto] <- temp; fim se fim enquanto a[lim. Inf] <- a[alto]; a[alto] <- pivô; retorne alto; fim
Comentários • Para evitar o pior caso e casos ruins onde elementos estão em grupos ordenados pode-se utilizar uma estratégia probabilística: – Selecione para pivô, ao invés do primeiro, um elemento aleatório. Critérios: • seleção totalmente randômica: selecione qualquer elemento do subvetor usando um gerador de números aleatórios; – desvantagem: tempo de processamento extra para o gerador. • seleção pseudorandômica: selecione um elemento do subvetor com base em um cálculo qualquer baseado em valores que você tem à mão (alto, baixo, chave do primeiro); • seleção média: pegue ao invés do elemento inicial, sempre o do meio. • Todos estes métodos melhoram a performance média.
Heap. Sort ou Classificação por Monte. . .
Heapsort • O Heapsort, também chamado de Classificação por Monte ou Ordenação com Árvore-Heap é um método de ordenação que utiliza uma árvore-heap como estrutura intermediária para efetuar a ordenação. • Heapsort sempre é O(n log n) – Quicksort geralmente é O(n log n) mas no pior caso fica O(n 2) – Quicksort geralmente é um pouco melhor, mas Heapsort é mais confiável em situações críticas
Heapsort • Desvantagens: – construir a árvore-heap pode consumir muita memória. • Em suma: – para dados bem bagunçados o Quicksort é mais vantajoso por ser mais econômico; – para dados imprevisíveis, pode ser mais vantajoso por ser previsível em termos de tempo de execução.
O que é “heap”? • Definições de heap: • • • (conhecida) Uma grande quantiodade de memória de onde o programador poded alocar ou liberar blocos para uso dentro de um programa. (nova) Uma árvore binária balanceada, justiifcada à esquerda, onde nenhum nodo possui um valor maior que seu pai. Não possuem nada em comum: – Heapsort usa a segunda
A Árvore-Heap • Uma árvore-heap é uma árvore binária justificada à esquerda, organizada por níveis, onde o nodo pai de uma subárvore sempre é maior que todos os seus descendentes.
Método em Heapsort É um método em três estágios: • primeiramente criamos uma visão sobre o arquivo como árvore binária por níveis; • Iterativamente: 1. reestruturamos o arquivo para que se transforme em um monte; Geramos a seqüência de saída, repetidamente: 2. retirando a raiz do monte e 3. reestruturando o monte para que retorne a satisfazer a condição-heap.
Origem da Metáfora: • Efeito Nóz-do-Pará (Brazil Nut Effect) – Fenômeno da flutuação reversa em material granular • Veja alguns vídeos. . .
Visão do arquivo x 1, . . . , xn por níveis
Exemplo (26 5 77 1 61 11 59 15 48 19) Arquivo original por níveis Árvore-Heap resultante
Árvores binárias balanceadas • Lembre: – A profundidade de um nodo é sua distância da raiz – A profundidade de uma árvore é a profundidade de seu nodo mais profundo • Uma árvore binária de profundidade n está balanceada se todos os nodos da profundidade 0 até n-2 possuírem dois filhos n-2 n-1 n Balanceada Não balanceada 61
Árvores binárias justificadas à esquerda • Uma árvore binária balanceada de profundidade n é justificada à esquerda se: – possui 2 n nodos de profundidade n (cheia), ou – possui 2 k nodos de profundidade k, para todo k < n, e todas as folhas no nível n estão mais à esquerda possível Justificada à esquerda Não-justificada à esquerda
Passos para Dominar Heap. Sort 1. Aprender a transformar uma árvore binária em um heap 2. Aprender a transformar uma árvore binária de volta em um heap após ela ter sofrido modificações 3. Aprender como estas idéias servem para ordenar um vetor 63
A propriedade-heap • Um nodo possui a propriedade-heap se nenhum de seus descendentes é maior que ele. 12 8 12 3 Raiz possui propriedade-heap 8 12 12 Raiz possui propriedade-heap 8 14 Raiz não possui propriedade-heap • Todas as folhas possuem propriedade-heap • Uma árvore binária é um heap se todos os seus nodos possuem a propriedade-heap
Peneirar para cima (sift. Up) • Dado um nodo não-heap, ele pode adquirir a propriedade-heap trocando seu valor com seu maior filho. 15 11 8 15 Nodo azul não possui propriedade-heap 8 11 Nodo azul possui propriedade-heap • Isto é chamado sifting up • Observe que o filho pode ter perdido a propriedadeheap
Construindo um monte I • Uma árvore unitária é um heap • Construímos um heap adicionando nodos um a um: – Adicione o nodo imediatamente à direita do nodo mais à direita no nível mais profundo. – Se o nível mais profundo estiver completo, inicie um novo nível • Exemplos: Adicione um novo nodo aqui
Construindo um monte II • Toda vez que adicionamos um nodo, poderemos estar destruindo a propriedade-heap de seu pai. – Para arrumar isto, peneiramos para cima. • Como o valor do nodo pai se altera, isto pode alterar a propriedade-heap em seu pai. • Repetimos o sifting up, subindo na árvore até que ou – Atinjamos nodos cujos valores não tenham de ser trocados • o pai ainda é maior que o filho ou – Atinjamos a raiz
Construindo um monte III 8 8 10 10 8 1 12 8 5 2 10 8 10 3 10 5 12 8 12 5 10 8 5 4
Outros filhos não são afetados 12 10 8 12 5 14 14 8 14 5 10 12 8 5 10 ü O nodo contendo 8 não é afetado porque seu pai fica maior, não menor ü O nodo contendo 5 não é afetado porque seu pai fica maior, não menor ü O nodo contendo 8 continua não sendo afetado porque seu pai, embora tenha diminuído, continua sendo mairo do que era antes
Um heap-exemplo • Aqui uma árvore binária-exemplo depois que ela foi amontoada (heapificada…) 25 22 19 18 17 22 14 21 14 3 9 15 11 • Observe que amontoar não significa ordenar • Amontoar também não altera a forma da árvore. Se ela começou justificada à esquerda, continua assim.
Removendo a raiz • Observe que o maior valor agora está na raiz • Suponha que descartemos a raiz: 11 22 19 18 17 22 14 21 14 3 9 15 11 • Como consertamos a árvore para voltar a estar balanceada e justificada à esquerda? • Solução: Retire o elemento mais à direita do nível mais inferior e use como raiz. 71
O método reamontoar I • A árvore está balanceada e justificada à esquerda, mas não é mais um heap • Porém, somente a raiz não possui a propriedade-heap 11 22 19 18 17 22 14 21 14 3 15 9 • Peneiramos para cima a raiz (sift up) • Feito isto, somente um filho perdeu a propriedade-heap
O método reamontoar II • Agora o filho à esquerda da raiz perdeu a propriedade-heap (também um 22) 22 11 19 18 17 22 14 21 14 3 15 9 • Este nodo podemos também peneirar para cima • Novamente somente um filho perdeu a condição-heap
O método reamontoar III • Agora o filho à direita do filho à esquerda da raiz (11) é não-heap: 22 22 19 18 17 11 14 21 14 3 15 9 • Podemos peneirar para cima este nodo • Depois, somente um de seus filhos poderia ter perdido a propriedade-heap, mas todos são folha!
O método reamontoar IV • A árvore torna a ser um monte porque todos os nodos possuem a propriedade-heap 22 22 19 18 17 21 14 11 14 3 9 • O maior valor voltou a estar na raiz • Podemos repetir isto até a árvore estar vazia • Assim retiramos do maior ao menor 15
Ordenação usando um Monte • A árvore é balanceada e justificada à esquerda, pode ser representada em um vetor – Somente funciona com árvores balanceadas justificadas è esquerda – Podemos representar tudo como operações sobre arrays – Para ordenar: Passo 1: amontoe o vetor; ENQUANTO houver elementos { Passo 2: remova e substitua a raiz; Passo 3: reamontoe o novo nodo; } 76
Mapeando em um vetor 25 22 17 19 18 0 22 14 1 2 14 21 3 4 3 5 6 9 7 8 15 11 9 10 25 22 17 19 22 14 15 18 14 21 3 11 12 9 11 • Observe: – Filho da esquerda de i está em 2*i+1 – Filho da direita de i está em 2*i+2 – Exemplo: filhos de 3 (19) são 7 (18) e 8 (14) 77
Removendo e substituindo a raiz • A “raiz” é o primeiro elemento no vetor • O “nodo mais à direita no nível mais profundo” é o último elemento • Troque-os 0 1 2 3 4 5 6 7 8 9 10 25 22 17 19 22 14 15 18 14 21 3 0 1 2 3 4 5 6 7 8 9 10 11 22 17 19 22 14 15 18 14 21 3 11 12 9 11 11 12 9 25 • . . . e faça de conta que o ultimo elemento do vetor não existe mais, sendo o último índice 11 (contendo 9)
Reamontoe e repita • Reamontoe a raiz (índice 0, contendo 11). . . 0 1 2 3 4 5 6 7 8 9 10 11 22 17 19 22 14 15 18 14 21 3 0 1 2 3 4 5 6 7 8 9 10 22 22 17 19 21 14 15 18 14 11 3 0 1 2 3 4 5 6 7 8 9 10 11 12 9 25 11 Área de descarte vira vetor ordenado 12 9 22 17 19 22 14 15 18 14 21 3 22 25 • Lembre-se que o “ultimo” mudou. • Repita até o “ultimo” ser o primeiro e está ordenado!
Análise de Complexidade I • Inicialização: Passo 1: Amontoe (heapifique) o vetor • Para amontoar adicionamos os n nodos – Cada nodo tem de ser peneirado para cima • Como a árvore binária está perfeitamente balanceada, peneirar um nodo toma O(log n) – Como fazemos isto n vezes, amontoar toma n*O(log n), que é O(n log n) tempo
Análise de Complexidade II • O laço principal: ENQUANTO houver elementos { Passo 2: remova e substitua a raiz; Passo 3: reamontoe o novo nodo; } • Repetimos o laço n – 1 vezes, uma para cada remoção de um elemento • Remover e substituir a raiz toma O(1) tempo • O tempo total é n vezes o tempo de reamontoar
Análise de Complexidade III • Para reamontoar a raiz, temos de, no pior caso, seguir apenas um caminho da raiz até um nodo folha • Eventualmente parando antes • Este caminho tem comprimento máximo O(log n) pois a árvore é perfeitamente balanceada • Logo, reamontoar um nodo tem complexidade O(log n) • Como reamontoamos num laço repetido n vezes, a complexidade do laço é n*O(log n), ou O(n log n)
Análise de Complexidade IV • Olhe o algoritmo novamente: Passo 1: amontoe o vetor; ENQUANTO houver elementos { Passo 2: remova e substitua a raiz; Passo 3: reamontoe o novo nodo; } • • Amontoar tem complexidade O(n log n) O laço tem complexidade O(n log n) A complexidade total é O(n log n) + O(n log n) O que é o mesmo que O(n log n) 83
Algoritmo Heapsort heapsort(t. Info: a[], inteiro: n) variáveis Só ajustamos a raiz inteiro: i; de cada subárvore t. Info: temp; início para i de (n div 2) até 1 passo -1 faça ajuste(a, i, n); // Amontoe. fim para i de (n-1) até 1 passo -1 faça temp <- a[i+1]; a[i+1] <- a[1]; // Posição inicial = 1. a[1] <- temp; ajuste(a, 1, i); // Reamontoe. fim para fim
Algoritmo do ajuste(t. Info: a[], inteiro: i, n) variáveis inteiro: j; t. Info: a. Aux; início a. Aux <- a[i]; // Supõe posição inicial = 1. j <- 2*i; enquanto j <= n faça se j < n E a[j] < a[j+1] então incremente j; fim se se a. Aux >= a[j] então retorne fim se a[j div 2] <- a[j]; a[j] <- a. Aux; // Falta no algoritmo dado em // Horowitz & Sahni. j <- 2*j; fim enquanto a[j div 2] <- a. Aux; fim
Atribuição-Uso Não-Comercial-Compartilhamento pela Licença 2. 5 Brasil Você pode: - copiar, distribuir, exibir e executar a obra - criar obras derivadas Sob as seguintes condições: Atribuição — Você deve dar crédito ao autor original, da forma especificada pelo autor ou licenciante. Uso Não-Comercial — Você não pode utilizar esta obra com finalidades comerciais. Compartilhamento pela mesma Licença — Se você alterar, transformar, ou criar outra obra com base nesta, você somente poderá distribuir a obra resultante sob uma licença idêntica a esta. Para ver uma cópia desta licença, visite http: //creativecommons. org/licenses/by-nc-sa/2. 5/br/ ou mande uma carta para Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
- Slides: 86