Quick Sort

5
Quicksort O problema da ordenação consiste em rearranjar um vetor v[0..n-1] em ordem crescente, ou seja, permutar os elementos do vetor de modo que tenhamos v[0] v[1] ≤ . . . ≤ v[n-1]. O algoritmo Quicksort, inventado por C.A.R. Hoare em 1962, resolve o problema. Em algumas raras instâncias , o Quicksort pode ser tão lento quanto os algoritmos elementares ; mas em geral é muito rápido. Mais precisamente, o algoritmo consome tempo proporcional a n log n em média e proporcional a n 2 no pior caso. Usaremos duas abreviaturas ao discutir o algoritmo. A expressão v[i..k] x será usada como abreviatura de v[j] x para todo j no conjunto de índices i..k. Analogamente, a expressão v[i..k] v[p..r] será interpretada como v[j] v[q] para todo j no conjunto i..k e todo q no conjunto p..r. O problema da separação O núcleo do algoritmo Quicksort é o seguinte problema da separação (= partition subproblem), que formularemos de maneira propositalmente vaga: rearranjar um vetor v[p..r] de modo que todos os elementos pequenos fiquem na parte esquerda do vetor e todos os elementos grandes fiquem na parte direita. O ponto de partida para a solução deste problema é a escolha de um pivô, digamos c. Os elementos do vetor que forem maiores que c serão considerados grandes e os demais serão considerados pequenos. É importante escolher c de tal modo que as duas partes do vetor rearranjado sejam estritamente menores que o vetor todo. A dificuldade está em resolver o problema da separação de maneira rápida sem usar muito espaço de trabalho. O problema da separação admite muitas formulações concretas. Eis uma primeira: rearranjar v[p..r] de modo que tenhamos v[p..j] v[j+1..r] para algum j em p..r-1. (Nesta formulação, o pivô não é explícito.) Eis outra formulação: rearranjar v[p..r] de modo que v[p..j-1] v[j] < v[j+1..r] para algum j em p..r. Esta página usa a segunda formulação; outras formulações são mencionadas nos exercícios. Exercícios 1

description

O problema da ordenação consiste em rearranjar um vetor v[0..n-1] em ordem crescente, ou seja, permutar os elementos do vetor de modo que tenhamos v[0] ≤ v[1] ≤ . . . ≤ v[n-1]. O algoritmo Quicksort, inventado por C.A.R. Hoare em 1962, resolve o problema. Em algumas raras instâncias, o Quicksort pode ser tão lento quanto os algoritmos elementares; mas em geral é muito rápido. Mais precisamente, o algoritmo consome tempo proporcional a n log n em média e proporcional a n2 no pior caso.

Transcript of Quick Sort

  • Quicksort

    O problema da ordenao consiste em rearranjar um vetor v[0..n-1] em ordem

    crescente, ou seja, permutar os elementos do vetor de modo que tenhamos v[0] v[1]

    . . . v[n-1].

    O algoritmo Quicksort, inventado por C.A.R. Hoare em 1962, resolve o problema. Em

    algumas raras instncias, o Quicksort pode ser to lento quanto os algoritmos

    elementares; mas em geral muito rpido. Mais precisamente, o algoritmo consome

    tempo proporcional a n log n em mdia e proporcional a n2 no pior caso.

    Usaremos duas abreviaturas ao discutir o algoritmo. A expresso v[i..k] x ser

    usada como abreviatura de v[j] x para todo j no conjunto de ndices i..k.

    Analogamente, a expresso v[i..k] v[p..r] ser interpretada como v[j] v[q]

    para todo j no conjunto i..k e todo q no conjunto p..r.

    O problema da separao

    O ncleo do algoritmo Quicksort o seguinte problema da separao (= partition

    subproblem), que formularemos de maneira propositalmente vaga:

    rearranjar um vetor v[p..r] de modo que todos os elementos pequenos fiquem na

    parte esquerda do vetor e todos os elementos grandes fiquem na parte direita.

    O ponto de partida para a soluo deste problema a escolha de um piv, digamos c. Os

    elementos do vetor que forem maiores que c sero considerados grandes e os demais

    sero considerados pequenos. importante escolher c de tal modo que as duas partes

    do vetor rearranjado sejam estritamente menores que o vetor todo. A dificuldade est

    em resolver o problema da separao de maneira rpida sem usar muito espao de

    trabalho.

    O problema da separao admite muitas formulaes concretas. Eis uma primeira:

    rearranjar v[p..r] de modo que tenhamos

    v[p..j] v[j+1..r]

    para algum j em p..r-1. (Nesta formulao, o piv no explcito.) Eis outra

    formulao: rearranjar v[p..r] de modo que

    v[p..j-1] v[j] < v[j+1..r]

    para algum j em p..r. Esta pgina usa a segunda formulao; outras formulaes so

    mencionadas nos exerccios.

    Exerccios 1

  • 1. Positivos e negativos. Escreva uma funo que rearranje um vetor v[p..r] de inteiros de modo que tenhamos v[p..j-1] 0 e v[j..r] > 0 para algum j em p..r+1. Faz sentido exigir que j esteja em p..r? Procure fazer uma funo rpida que no use vetor auxiliar. Repita o exerccio depois de trocar v[j..r] > 0 por v[j..r] 0. Faz sentido exigir que v[j] seja 0?

    2. Um vetor v[p..r] est arrumado se existe j em p..r tal que v[p..j-1] v[j] < v[j+1..r] . Escreva um algoritmo que decida se v[p..r] est arrumado. Em caso afirmativo, o seu algoritmo deve devolver o valor de j.

    3. Critique a seguinte verso do algoritmo da separao: 4. int sep( int v[], int p, int r) { 5. int w[1000], i = p, j = r, c = v[p], k; 6. for (k = p+1; k

  • }

    A funo rearranja o vetor v[p..r] e devolve um elemento j do conjunto p..r tal que

    v[p..j-1] v[j] < v[j+1..r] .

    Gostaramos que j ficasse a meio caminho entre p e r, mas devemos estar preparados

    para aceitar os casos extremos j == p e j == r .

    p j r

    c c c c c > c > c > c > c > c

    Algumas perguntas que chamam a ateno para detalhes importantes: Que acontece se

    executarmos a funo com p igual a r? Que acontece se eliminarmos i = p na linha 4? Que acontece se escrevermos i > j na

    linha 5? Que acontece se trocarmos j por i nas linhas 9 e 10?

    Anlise do algoritmo da separao

    Eu prefiro escrever a funo separa de maneira um pouco diferente para facilitar a

    anlise de sua correo:

    // Recebe vetor v[p..r] com p < r. Rearranja os

    // elementos do vetor e devolve j em p..r tal que

    // v[p..j-1]

  • Na penltima passagem peo ponto A, teremos

    p i=j r

    c c c c c ? > c > c > c > c

    Na ltima passagem pelo ponto A valem as seguintes relaes:

    i == j+1 p j r v[p+1..j] c e v[j+1..r] > c.

    Portanto, v[p..j-1] v[j] < v[j+1..r] na fim da execuo da funo, como

    prometido.

    Desempenho do algoritmo da separao

    Qual o desempenho da funo separa? Quanto tempo a funo consome? O consumo

    de tempo proporcional ao nmero de comparaes entre elementos do vetor. No

    difcil perceber que esse nmero proporcional ao nmero r - p + 1 de elementos

    do vetor.

    Exerccios 2

    1. Qual o resultado da funo separa quando os elementos de v[p..r] so todos iguais? E quando v[p..r] crescente? E quando v[p..r] decrescente? E quando cada elemento de v[p..r] tem um de dois valores possveis?

    2. A funo separa produz um rearranjo estvel do vetor, ou seja, preserva a ordem relativa de elementos de mesmo valor?

    3. Analise e discuta a seguinte verso da funo separa. Ela equivalente do livro de Cormen, Leiserson, Rivest e Stein.

    4. int separa_CLRS( int v[], int p, int r) { 5. int c = v[r], i = p, j, t; 6. for (j = p; j < r; ++j) { 7. if (v[j]

  • 25. v[p] = v[j], v[j] = c; 26. return j; 27. }

    28. Critique a seguinte verso do algoritmo da separao: 29. int separa( int v[], int p, int r) { 30. int c = v[p], i = p+1, j = r, t; 31. while (i