Algoritmos de ordenação

Algoritmo Quicksort


Ideia do Quicksort

Estratégia "divide and conquer":

  1. Escolhemos um valor (designado pivot)
  2. Partimos a sequência em duas partes:
  3. Recursivamente ordenamos as duas sub-sequências

Caso base: se a sequência tem 0 ou 1 valores não há nada a fazer (já está ordenada).

Exemplo

Começamos com a sequência: \[ \begin{array}{|c|c|c|c|c|c|c|c|} \hline 55 & 41 & 59 & 26 & 53 & 58 & 97 & 93 \\ \hline \end{array} \]

Escolhemos o primeiro valor como pivot e re-organizamos os valores: \[ \underbrace{\begin{array}{|c|c|c|} \hline 41 & 26 & 53 \\ \hline \end{array}}_{<55} \begin{array}{|c|} \hline \textbf{55} \\ \hline \end{array} \underbrace{\begin{array}{|c|c|c|c|} \hline 59 & 58 & 97 & 93 \\ \hline \end{array}}_{\geq55} \]

Exemplo (cont.)

Recursivamente ordenamos as duas sub-sequências repetindo este método: \[ \begin{array}{ccc} \begin{array}{|c|c|c|} \hline 41 & 26 & 53 \\ \hline \end{array} & \begin{array}{|c|} \hline 55 \\ \hline \end{array} & \begin{array}{|c|c|c|c|} \hline 59 & 58 & 97 & 93 \\ \hline \end{array} \\ \overset{\vdots}{\downarrow} & & \overset{\vdots}{\downarrow} \\ \begin{array}{|c|c|c|} \hline 26 & 41 & 53 \\ \hline \end{array} & & \begin{array}{|c|c|c|c|} \hline 58 & 59 & 93 & 97 \\ \hline \end{array} \end{array} \]

Sequência final ordenada: \[ \begin{array}{|c|c|c|c|c|c|c|c|} \hline 26 & 41 & 53 & 55 & 58 & 93 & 97 \\ \hline \end{array} \]

Algoritmo recursivo

\[ \begin{array}{l} \text{Dados:}\quad v[0], v[1], \ldots, v[n-1] \\[1ex] \textsf{qsort}(l, u): \quad \color{red}{\text{// ordenar}~v[l\ldots u]} \\ \qquad \text{se}~ l\geq u~\text{então termina imediatamente;} \\ \qquad \color{red}{\text{// caso contrário:}} \\ \qquad m \gets \textsf{partition}(l, u)~ \color{red}{\text{// partir usando pivot}}\\ \qquad \textsf{qsort}(l, m-1) ~ \color{red}{\text{// ordenar}~v[l\ldots m-1]}\\ \qquad \textsf{qsort}(m+1, u) ~ \color{red}{\text{// ordenar}~v[m+1\ldots u]} \end{array} \]

Algoritmo Quicksort

Partir usando pivot

\[ \begin{array}{|c|ccc|ccc|ccc|} \hline \textit{pivot} & & <\textit{pivot} & & & \geq \textit{pivot} & & & ? & \\ \hline \overset{\uparrow}{l} & \overset{\uparrow}{l+1} & & \overset{\uparrow}{m} & & & & \overset{\uparrow}{i} & & \overset{\uparrow}{u} \end{array} \]

Partir usando pivot (cont.)

\[ \begin{array}{|ccc|c|ccc|} \hline & <\textit{pivot} & & \textit{pivot} & & \geq \textit{pivot} & \\ \hline \overset{\uparrow}{l} & & \overset{\uparrow}{m-1} & \overset{\uparrow}{m} & \overset{\uparrow}{m+1} & & \overset{\uparrow}{u} \end{array} \]

Partir usando pivot: algoritmo

\[ \begin{array}{l} \textsf{partition}(l,u):\\ \qquad m \gets l\\ \qquad \textsf{para} ~i~\text{de} ~l+1 ~\text{até}~u: \\ \qquad\quad \text{se}~ v[i] < v[l]: \\ \qquad\quad\quad\quad m \gets m+1\\ \qquad\quad\quad\quad \text{trocar}~ v[i], v[m]\\ \qquad \text{trocar}~ v[l], v[m]\\ \qquad \text{retornar}~m \end{array} \]

Implementação (função principal)

void quicksort_rec(int vec[], int l, int u) {
  int m;
  if(l >= u)
    return;   // caso base; termina logo
  // senão: caso recursivo 
  m = partition(vec, l, u);
     // partir usando pivot
  quicksort_rec(vec, l, m-1);
     // ordenar vec[l..m-1]
  quicksort_rec(vec, m+1, u);
     // ordenar vec[m+1..u]
}

Implementação (partição)

int partition(int vec[], int l, int u) {
  int i, m, temp;
  m = l;   // m: indice do ponto médio
  for(i = l+1; i<=u; i++) {
     if(vec[i] < vec[l]) { // fora de ordem
      m ++;
      temp = vec[i];  // trocar vec[i], vec[m]
      vec[i] = vec[m];
      vec[m] = temp;
     }
  }
  temp = vec[l]; // trocar vec[l], vec[m]
  vec[l] = vec[m];
  vec[m] = temp;
  return m;
}

Terminação

\[ \begin{array}{|ccc|c|ccc|} \hline & <\textit{pivot} & & \textit{pivot} & & \geq \textit{pivot} & \\ \hline \overset{\uparrow}{l} & & \overset{\uparrow}{m-1} & \overset{\uparrow}{m} & \overset{\uparrow}{m+1} & & \overset{\uparrow}{u} \end{array} \]

Complexidade

Exemplo: sequência ascendente

\[ \begin{array}{r} \begin{array}{|c|c|c|c|c|c|c|c|} \hline 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\ \hline \end{array} \\ \hfill \downarrow\hfill \\ \begin{array}{|c|} \hline \textbf{1} \\ \hline \end{array} \begin{array}{|c|c|c|c|c|c|c|} \hline 2 & 3 & 4 & 5 & 6 & 7 & 8 \\ \hline \end{array} \\ \hfill \downarrow\hfill \\ \begin{array}{|c|} \hline \textbf{2} \\ \hline \end{array} \begin{array}{|c|c|c|c|c|c|} \hline 3 & 4 & 5 & 6 & 7 & 8 \\ \hline \end{array} \\ \hfill \overset{\downarrow }{\vdots}\hfill \end{array} \]

Resultados experimentais

Tempos para ordenação de \(n\) valores aleatórios (segundos):

\(n\) seleção inserção quicksort
10000 0.142152 0.082719 0.001343
20000 0.578847 0.336311 0.002831
40000 2.279674 1.349157 0.005698

Resultados experimentais