Escrito por
Mateus Almeida
em
Análise Heap Sort
Pseudocódigo
Heap Sort
1 |PROCEDIMENTO HeapSort(A[] : Inteiro):
2 |VAR
3 | |i : Inteiro
4 |INICIO
5 | |ConstruirHeapMax(A)
6 | |PARA i <- A.tamanho..2, FAÇA:
7 | | |A[1] <-> A[i]
8 | | |A.tamanhoHeap <- A.tamanhoHeap - 1
9 | | |MaxHeapify(A, 1)
10 | |FIM-PARA
11 |FIM
ConstruirHeapMax
1 |PROCEDIMENTO ConstruirHeapMax(A[] : Inteiro):
2 |VAR
3 | |i : Inteiro
4 |INICIO
5 | |A.tamanhoHeap <- A.tamanho
6 | |PARA i <- floor(A.tamanho/2)..1, FAÇA:
7 | | |MaxHeapify(A, i)
8 | |FIM-PARA
9 |FIM
MaxHeapify
1 |PROCEDIMENTO MaxHeapify(A[], i : Inteiro):
2 |VAR
3 | |l, r, maior : Inteiro
4 |INICIO
5 | |l <- Esquerdo(i)
6 | |r <- Direito(i)
7 | |SE l <= A.tamanhoHeap E A[l] > A[i], ENTÃO:
8 | | |maior <- l
9 | |SENAO
10 | | |maior <- i
11 | |FIM-SE
12 | |SE r <= A.tamanhoHeap E A[r] > A[maior], ENTÃO:
13 | | |maior <- r
14 | |FIM-SE
15 | |SE maior != i, ENTÃO:
16 | | |A[i] <-> A[maior]
17 | | |MaxHeapify(A, maior)
18 | |FIM-SE
19 |FIM
Funções auxiliares
1 |PROCEDIMENTO Direito(i : Inteiro):
2 |INICIO
3 | |Retorna 2*i+1
4 |FIM
1 |PROCEDIMENTO Esquerdo(i : Inteiro):
2 |INICIO
3 | |Retorna 2*i
4 |FIM
Corretude ConstruirHeapMax
Laço invariante
Laço para as linhas 6 a 8.
Invariante
No começo de cada laço invariante, cada nó i+1, i+2, …, n é a raiz de um heap máximo.
Inicialização
Antes da primeira iteração, temos que i=\(\left \lfloor n/2 \right \rfloor\) e cada nó \(\left \lfloor n/2 \right \rfloor\) + 1, \(\left \lfloor n/2 \right \rfloor\) + 2, …, n é uma folha e, portanto, é a raiz de um heap máximo.
Manutenção
Supondo que o invariante é verdadeiro antes da i-ésima iteração do laço invariante, os filhos do elemento A[i] estão em posições maiores que i e, pela propriedade invariante, são raizes de heaps máximos. Sabemos que esta é a condição exigida pelo procedimento MaxHeapify para que a chamada MaxHeapify(A, i) torne a posição i a raiz de um heap máximo. Logo, após a chamada MaxHeapify(A, i), temos que o nó i é a raiz de um heap máximo. Ao decrementar i, temos que a propriedade invariante é verdadeira para a próxima iteração do laço.
Finalização
Quando i=0, pela invariante de laço, 1, 2, 3,…, n são raizes de um heap máximo. Particularmente, o nó 1 é raiz do heap contendo todos elementos de A.
Corretude Heap Sort
Laço invariante
Laço para as linhas 6 a 10.
Invariante
No começo de cada iteração do laço invariante, o subvetor A[1..i] é um heap máximo contendo os i-menores elementos de A[1..n], e o subvetor A[i+1..n] contém os (n-i)-maiores elementos de A[1..n] ordenados.
Inicialização
Antes da i-ésima iteração do laço o subvetor A[i+1..n] é vazio, logo o invariante é trivialmente verdadeiro.
Manutenção
Supondo que o invariante é verdadeiro antes da i-ésima iteração, temos que A[1] é o maior elemento do subvetor A[1..i] do qual é menor que os elementos em A[i+1..n]. Quando A[1] é adicionado na i-ésima posição, então A[i..n] contém os maiores elementos ordenados do subvetor. Ao decrementar o tamanho do heap chamando MaxHeapify transformamos A[1..i-1] em um heap máximo. Desta forma, a sequência resultante é composta pelos valores de A[i..n] ordenados onde, ao executar o laço invariante i-vezes, teremos que A[1..n] estará ordenado.
Finalização
Após a n-ésima iteração do laço inariante, temos que A[2..n] está ordenado e A[1] é o menor elemento do array, ou seja, o array estará ordenado.
Complexidade de tempo
O melhor caso do Heap Sort ocorre quando o vetor já está ordenado. Isso ocorre porque, quando o vetor já está ordenado, o Heap Sort não precisa fazer nenhuma troca de elementos, apenas construir o heap e retirar os elementos em ordem crescente.
O pior caso do Heap Sort ocorre quando o vetor está ordenado de forma inversa, ou seja, em ordem decrescente. Isso ocorre porque, quando o vetor está ordenado de forma inversa, o Heap Sort precisa fazer muitas trocas de elementos para construir o heap e retirar os elementos em ordem crescente.
No caso médio, a complexidade de tempo do Heap Sort é \(\Theta(n log n)\). Isso ocorre porque, em média, o Heap Sort precisa fazer cerca de n/2 trocas de elementos para construir o heap e retirar os elementos em ordem crescente.
Nº de nós com altura h=\(\left \lceil n/2^{h+1} \right \rceil\)
Tempo total
ConstruirHeapMax
Para qualquer sub-árvore da árvore, temos 2n/3 nós sendo o número máximo de elementos.
graph TD;
A(( ))
B(( ))
C(( ))
A --> B;
A --> C;
\[T(n) = T(2n/3) + O(1)
\\=O(logn) \therefore | h=logn
\\=O(h)
\\\sum_{h=0}^{logn}\left \lceil n/2^{h+1} \right \rceil O(h)
\\=\sum_{h=0}^{logn}\left \lceil n/2^{n}\cdot 2 \right \rceil O(ch)
\\=cn/2\sum_{h=0}^{logn}\left \lceil h/2^{h}\cdot 2 \right \rceil
\\=cn/2\cdot 2 = O(n)\]
Heap Sort
T(n) = (Complexidade de tempo do ConstruirHeapMax) + (Complexidade de tempo do MaxHeapify)
\[=O(n) + (n-1) + \left [ O(logn) \right ] = O(nlogn)\]