02 - Quick sort
Neste atividade vamos explorar o algoritmo Quick sort, que é definido pela seguinte ideia em alto nível:
- escolha um elemento do vetor (pode ser o primeiro). Vamos chamar ele de pivô
- coloque o pivô no lugar certo em termos de ordenação.
- índice
ital que todo $A[j] \leq A[i], j < i$ - índice
ital que todo $A[j] > A[i], j > i$
- índice
- ordene a parte à esquerda de
i - ordene a parte à direita de
i
Recursão e o algoritmo QUICK_SORT
Vamos começar lendo essa descrição e notando uma coisa: parece que temos um algoritmo Recursivo! Os passos 3 e 4 são, essencialmente, aplicar ordenação em subarrays à esquerda e à direita do elemento pivô.
Exercício 1
Resposta
Aqui podemos ter duas respostas igualmente válidas:
- base ser só o array de um elemento. Como ele só tem um elemento sempre está ordenado.
- base ser composta de dois casos
- um elemento sozinho, como na anterior
- dois elementos: aqui podemos simplesmente trocar se o primeiro for maior que o segundo
Exercício 2
Resposta
Se você usou 1 como base, sua resposta será 5. Se foi 2 seria 4. Não importa muito o valor aqui, mas sim que é algo parecido com $\log_2 N$.
Dica 1
Você deve estar vendo que não estamos sendo muito precisos em nossas contas. Isso ocorre pois comparamos eficiência de algoritmos para N muito grande. Nesses casos, se for $\log_2 N$ ou $\log_2 N - 1$ ou até $5 \times \log_2 N$ não faz muita diferença. O valor de $N$ (para $N$ grande) domina todas essas pequenas flutuações. Nas próximas disciplinas de algoritmos iremos escrever isso formalmente.
Exercício 3
Resposta
Como não há nenhuma divisão propriamente dita, precisamos selecionar algo próximo de N pivôs até chegar no array de tamanho 1 ou 2.
Nosso algoritmo se chamará QUICK_SORT(A, INICIO, FIM). Ele ordena o trecho que vai de INICIO até FIM (exclusivo, ou seja, o último elemento do intervalo é A[FIM-1]). Os passos 3 e 4 consistem em, basicamente, rechamar QUICK_SORT em subarrays definidos pelo resultado dos passos 1 e 2. A base do algoritmo também na foi definida: se INICIO = FIM -1 (subarray de tamanho 1) então o algoritmo só retorna e não faz nada.
O pivô nos passos 1 e 2
Os passos 1 e 2 parecem concentrar grande parte da lógica do algoritmo. Eles consistem em particionar o array em torno de um elemento pivô. Supondo que pivô tenha sido movido para a posição I, teremos duas partes:
- uma parte esquerda no intervalo
INICIOatéI - uma parte direita no intervalo
I+1atéFIM
Note que, como o pivô já está na posição correta, ele não entra em nenhum das duas partes. Resta então pensar em como fazer essa partição de modo que, se o lugar certo do pivô é a posição I,
A[I] >= A[J]seI > JA[I] < A[J]seI < J
Chamaremos os passos 1 e 2 de PARTICIONA(A, INICIO, FIM). Vamos então tentar simular a seguinte ideia para estes passos:
- escolha o pivô como o elemento
A[INICIO] - vamos usar a variável
LUGAR_PIVO = INICIOpara marcar lugar atual do pivô no subarrayINICIO-FIM - começamos então a percorrer o subarray, usando
J = INICIO+1atéFIMpara marcar o elemento atual- se o valor do elemento atual for maior que o do pivô, não faça nada
- se o valor do elemento atual for menor que o do pivô, aumente um em
LUGAR_PIVOe troque o elemento atual comLUGAR_PIVO
- troque o pivô com o elemento em
LUGAR_PIVO
Como anteriormente, vamos simular esse algoritmo para entender melhor seu funcionamento.
Exercício 4
Resposta
LUGAR_PIVO = 0, INICIO = 0, A[INICIO] = 6J = 1-> menor,LUGAR_PIVO = 1, A = [ 6 3 1 2 7 5 4 8 ]J = 2-> menor,LUGAR_PIVO = 2, A = [ 6 3 1 2 7 5 4 8 ]J = 3-> menor,LUGAR_PIVO = 3, A = [ 6 3 1 2 7 5 4 8 ]J = 4-> maior,LUGAR_PIVO = 3, A = [ 6 3 1 2 7 5 4 8 ]J = 5-> menor,LUGAR_PIVO = 4, A = [ 6 3 1 2 5 7 4 8 ]J = 6-> menor,LUGAR_PIVO = 5, A = [ 6 3 1 2 5 4 7 8 ]J = 7-> maior,LUGAR_PIVO = 5, A = [ 6 3 1 2 5 4 7 8 ]- Final:
LUGAR_PIVO = 5, A = [ 4 3 1 2 5 6 7 8 ]
Exercício 5
Resposta
LUGAR_PIVO = 0, INICIO = 0, A[INICIO] = -2J = 1-> maior,LUGAR_PIVO = 0, A = [ -2 1 2 3 466 2 ]J = 2-> maior,LUGAR_PIVO = 0, A = [ -2 1 2 3 466 2 ]J = 3-> maior,LUGAR_PIVO = 0, A = [ -2 1 2 3 466 2 ]J = 4-> maior,LUGAR_PIVO = 0, A = [ -2 1 2 3 466 2 ]J = 5-> maior,LUGAR_PIVO = 0, A = [ -2 1 2 3 466 2 ]- Final:
LUGAR_PIVO = 0, A = [ -2 1 2 3 466 2 ]
Exercício 6
Resposta
LUGAR_PIVO = 0, INICIO = 0, A[INICIO] = 500J = 1-> menor,LUGAR_PIVO = 1, A = [ 500 1 2 3 466 2 ]J = 2-> menor,LUGAR_PIVO = 2, A = [ 500 1 2 3 466 2 ]J = 3-> menor,LUGAR_PIVO = 3, A = [ 500 1 2 3 466 2 ]J = 4-> menor,LUGAR_PIVO = 4, A = [ 500 1 2 3 466 2 ]J = 5-> menor,LUGAR_PIVO = 5, A = [ 500 1 2 3 466 2 ]- Final:
LUGAR_PIVO = 5, A = [ 2 1 2 3 466 500 ]
Exercício 7
Resposta
Discutido em sala. Use a opção "Editar" para modificar suas anotações após a discussão.
Juntando tudo
Vamos agora juntar tudo o que fizemos.
Exercício 8
Resposta
Discutido em sala. Use a opção "Editar" para modificar suas anotações após a discussão.
Dica 2
Teste seu algoritmo recursivo com as seguintes entradas:
A = [ 1 ]A = [ 2 1 ]A = [ 1 2 3 ]A = [ 500 1 2 3 466 2 ]A = [ 6 3 1 2 7 5 4 8 ]