Você está na página 1de 5

Quicksort bsico

Agora que resolvemos o problema da separao, podemos cuidar do Quicksort


propriamente dito. O algoritmo usa a estratgia da diviso e conquista e tem a aparncia
de um Mergesort ao contrrio:
// A funo recebe um vetor v[p..r], com p <= r+1,
// e rearranja o vetor em ordem crescente.
void
quicksort( int v[], int p, int r)
{
int j;
// 1
if (p < r) {
// 2
j = separa( v, p, r);
// 3
quicksort( v, p, j-1);
// 4
quicksort( v, j+1, r);
// 5
}
}

(Note que a funo est correta mesmo quando p > r, ou seja, quando o vetor est
vazio.)

Exerccios 3
1. Que acontece se trocarmos p < r por p != r na linha 2 do quicksort?
2. Que acontece se trocarmos j-1 por j na linha 4 do quicksort? Que acontece se
trocarmos j+1 por j na linha 5 do cdigo?
3. Submeta o vetor 77 55 33 99 indexado por 1..4 funo quicksort. Teremos
a seguinte sequncia de invocaes da funo (observe a indentao):
4. quicksort( v,1,4)
5.
quicksort( v,1,2)
6.
quicksort( v,1,0)
7.
quicksort( v,2,2)
8.
quicksort( v,4,4)

Repita o exerccio com o vetor 55 44 22 11 66 33 indexado por 1..6.


9. Tail recursion. Verifique que a segunda invocao da funo quicksort (linha 5 do
cdigo) pode ser eliminada se trocarmos o if por um while:
10. void quicksrt( int v[], int p, int r) {
11.
int j;
12.
while (p < r) {
13.
j = separa( v, p, r);
14.
quicksrt( v, p, j-1);
15.
p = j + 1;
16.
}
17. }
18. A funo quicksort produz uma ordenao estvel do vetor?

19. Discuta a seguinte implementao do algoritmo Quicksort. A funo s se aplica a


vetores v[p..r] com p < r.
20. void qcksrt( int v[], int p, int r) {
21.
int j;
22.
j = separa( v, p, r);
23.
if (p < j-1) qcksrt( v, p, j-1);

24.
25. }

if (j+1 < r) qcksrt( v, j+1, r);

26. Formulao alternativa da separao. Suponha dada uma implementao da funo


separa que funciona assim: rearranja v[p..r] e devolve um ndice j tal que
v[p..j] v[j+1..r]. Escreva uma verso do algoritmo Quicksort que use essa
implementao do separa. Que restries devem ser impostas sobre j?
27. Verso Iterativa. Escreva uma verso no recursiva do algoritmo Quicksort. [Soluo]
28. Quicksort Aleatorizado. Escreva uma verso aleatorizada (= randomized) do algoritmo
Quicksort.

Animaes do Quicksort

Animao do Quicksort produzida por Mike Bostock (veja tambm uma visualizao
esttica, uma variante, e uma outra forma de visualizao).
Sorting Algorithms Animation by David R. Martin.
Demonstrao animada do Quicksort, copiada da pgina da disciplina COS226 de
R. Sedgewick (Universidade de Princeton).
Animao de algoritmos de ordenao de Nicholas Andr Pinho de Oliveira.
Quick-sort with Hungarian (Kkllmenti legnyes) folk dance, created at Sapientia
University (Romania).

Desempenho do Quicksort bsico


O consumo de tempo da funo quicksort proporcional ao nmero de comparaes
entre elementos do vetor. Se o ndice j devolvido por separa estiver sempre mais ou
menos a meio caminho entre p e r, o nmero de comparaes ser aproximadamente n
log n, sendo n o nmero de elementos do vetor. Se o vetor j estiver ordenado ou
quase ordenado, o nmero de comparaes estar na ordem de
n2.

Portanto, o pior caso do Quicksort no melhor que o dos algoritmos elementares.


Felizmente, o pior caso muito raro: em mdia, o consumo de tempo do quicksort
proporcional a
n

log n .

(Veja detalhes na minha pgina Anlise do Quicksort.)

Altura da pilha de execuo do Quicksort


Na verso bsica do Quicksort, o cdigo cuida imediatamente do subvetor v[p..j-1] e
trata do subvetor v[j+1..r] somente depois que v[p..j-1] estiver ordenado.
Dependendo do valor de j nas sucessivas invocaes da funo, a pilha de execuo
pode crescer muito, atingindo altura igual ao nmero de elementos do vetor. (Isso
acontece, por exemplo, se o vetor estiver em ordem decrescente.) O fenmeno no
afeta o consumo de tempo do algoritmo, mas pode esgotar o espao de memria. Para
controlar o crescimento da pilha de execuo preciso tomar duas providncias:

1. cuidar primeiro do menor dos subvetores v[p..j-1] e v[j+1..r] e


2. eliminar a segunda invocao recursiva da funo (veja exerccio acima).

Se adotarmos estas providncias, o tamanho do subvetor que est no topo da pilha de


execuo ser menor que a metade do tamanho do subvetor que est logo abaixo na
pilha. De modo mais geral, o subvetor que est em qualquer das posies da pilha de
execuo ser menor que metade do subvetor que est imediatamente abaixo. Assim, se
a funo for aplicada a um vetor com n elementos, a altura da pilha no passar de log
n.
// A funo rearranja o vetor v[p..r], com p <= r+1,
// de modo que ele fique em ordem crescente.
void
quickSort( int v[], int p, int r)
{
int j;
while (p < r) {
j = separa( v, p, r);
if (j - p < r - j) {
quickSort( v, p, j-1);
p = j + 1;
} else {
quickSort( v, j+1, r);
r = j - 1;
}
}
}

APNDICE: outras formas do Quicksort


A algoritmo Quicksort pode ser escrito de vrias outras maneiras, diferentes da
discutida acima. A diferena entre as vrias verses est no modo de resolver o
subproblema da separao. (Em todas as implementaes abaixo, o mecanismo de
controle da altura da pilha de execuo foi omitido para tornar o cdigo mais legvel.)
1. A verso abaixo comea por rearranjar v[p..r] de modo que v[p..i-1] v[i]
v[i+1..r] e p i r (compare com o resultado da separao feita acima).
2. // A funo rearranja o vetor v[p..r], com p <= r+1,
3. // de modo que ele fique em ordem crescente.
4.
5. void quick_FlipFlop( int v[], int p, int r)
6. {
7.
int i, j, t, ff;
8.
if (p < r) {
9.
i = p, j = r;
10.
ff = -1;
11.
while (i < j) {
12.
if (v[i] > v[j] {
13.
t = v[i], v[i] = v[j], v[j] = t;
14.
ff = -ff;
15.
}
16.
if (ff == -1) --j;
17.
else ++i;

18.
19.
20.
21.
22. }

}
quick_FlipFlop( v, p, i-1);
quick_FlipFlop( v, i+1, r);
}

23. A verso abaixo (veja livro de Cormen, Leiserson e Rivest) comea por rearranjar
v[p..r] de modo que v[p..j] v[j+1..r] e p j < r (compare com o
resultado da separao feita acima). Note que os vetores v[p..j] e v[j+1..r] so
ambos estritamente menores que o vetor original.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.

// A funo rearranja o vetor v[p..r], com p <= r+1,


// de modo que ele fique em ordem crescente.
void quick_CLR( int v[], int p, int r)
{
int c, i, j, t;
if (p < r) {
c = v[p];
i = p-1, j = r+1;
while (1) {
do --j; while (v[j] > c);
do ++i; while (v[i] < c);
if (i >= j) break;
t = v[i], v[i] = v[j], v[j] = t;
}
quick_CLR( v, p, j);
quick_CLR( v, j+1, r);
}
}

43. Eis outra implementao. (Se no me engano, ela est no livro de Sedgewick.)
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.

// A funo rearranja o vetor v[p..r], com p <= r+1,


// de modo que ele fique em ordem crescente.
void quick_S( int v[], int p, int r) {
int c, i, j, t;
if (p < r) {
c = v[(p+r)/2];
i = p, j = r;
while (i <= j) {
while (v[i] < c) ++i;
while (c < v[j]) --j;
if (i <= j) {
t = v[i], v[i] = v[j], v[j] = t;
++i, --j;
}
}
quick_S( v, p, j);
quick_S( v, i, r);
}
}

Exerccio: formule com preciso o subproblema da separao que quick_S


resolve.
64. A seguinte implementao do Quicksort sugerida no livro de Sedgewick:
65. #define troca( A, B) { int t = A; A = B; B = t; }
66. void quick_S2( int v[], int p, int r) {
67.
if (p < r) {
68.
int i = p-1, j = r, c = v[r];
69.
while (1) {

70.
71.
72.
73.
74.
75.
76.
77.

while (v[++i] < c) ;


while (c < v[--j]) if (j == p) break;
if (i > j) break;
troca( v[i], v[j]);
}
troca( v[i], v[r]);
quick_S2( v, p, j);
quick_S2( v, i+1, r);
}
}

Você também pode gostar