5.10. O Shell Sort

O shell sort, às vezes chamaado de “ordenação por incrementos diminutos”, melhora a ordenação por inserção ao quebrar a lista original em um número menor de sublistas, as quais são ordenadas usando a ordenação por inserção. A forma única como essas sublistas são escolhidas é a chave para o shell sort. Em vez de quebrar a lista em sublistas de itens contíguos, o shell sort usa um incremento i, às vezes chamado de gap, para criar uma sublista escolhendo todos os itens que estão afastados i itens uns dos outros.

Isso pode ser visto na Figura 6. Essa lista tem nove itens. Se usarmos um incremento de três, serão três sublistas, cada uma sendo submetida à ordenação por inserção. Depois de realizado esse processo, ficamos com a lista mostrada em Figura 7. Embora essa lista não esteja completamente ordenada, algo muito interessante aconteceu. Ao ordenarmos as sublistas, os itens ficaram mais próximos de onde eles pertencem de fato.

../_images/shellsortA.png

Figura 6: Shell Sort com Incremento de Três

../_images/shellsortB.png

Figura 7: Shell Sort depois de Ordenar Cada Sublista

A Figura 8 mostra uma ordenação por inserção final usando um incremento de um; em outras palavras, uma ordenação por inserção convencional. Observe que ao realizar as ordenações de sublistas anteriores, reduzimos agora o número total de operações de deslocalmento necessárias para colocar a lista na sua ordem final. Nesse caso, precisamos de apenas mais quatro deslocamentos para completar o processo.

../_images/shellsortC.png

Figura 8: ShellSort: Uma Inserção Final Com Incremento de 1

../_images/shellsortD.png

Figura 9: Sublistas Iniciais para um Shell Sort

Dissemos antes que a forma como os incrementos são escolhidos é uma característica única do shell sort. A função mostrada em ActiveCode 1 usa um conjunto de diferentes incrementos. Nesse caso, começamos com \(\frac {n}{2}\) sublistas. No passo seguinte, \(\frac {n}{4}\) sublistas são ordenadas. Eventualmente, uma única lista é ordenada com a ordenação por inserção básica. A Figure 9 mostra as primeiras sublistas para o nosso exemplo usando esse incremento.

A seguinte chamada da função shellSort mostra as listas parcialmente ordenadas depois de cada incremento, com a ordenação final sendo uma ordenação por inserção com incremento de um.



À primeira vista, você pode pensar que o shell sort não tem como ser melhor que a ordenação por inserção, já que ele a utiliza na lista toda como um último passo. Acontece que essa ordenação por inserção final não precisa realizar muitas comparações (ou deslocamentos), já que a lista foi pré-ordenada por ordenações por inserção incrementais anteriores, como descrito acima. Em outras palavras, cada passagem produz uma lista que está “mais ordenada” que a anterior. Isso faz com que a passagem final seja muito eficiente.

Embora uma análise geral do shell sort esteja além do escopo deste texto, podemos dizer que ele tende a ficar entre \(O(n)\) e \(O(n^{2})\), baseado no comportamento descrito acima. Para os incrementos mostrados em Listing 5, o desempenho é \(O(n^{2})\). Alterando o incremento, por exemplo, usando \(2^{k}-1\) (1, 3, 7, 15, 31, e assim por diante), o shell sort pode executar a \(O(n^{\frac {3}{2}})\).

Autoavaliação

    Q-2: Dada a seguinte lista de números: [5, 16, 20, 12, 3, 8, 9, 17, 19, 7] Que resposta exibe o conteúdo correto da lista depois que todas as trocas são feitas para um gap de tamanho 3?

  • [5, 3, 8, 7, 16, 19, 9, 17, 20, 12]
  • Cada grupo de números representados pelas posições de índice de separação 3 são ordenados corretamente.
  • [3, 7, 5, 8, 9, 12, 19, 16, 20, 17]
  • Essa solução é para um gap de tamanho dois.
  • [3, 5, 7, 8, 9, 12, 16, 17, 19, 20]
  • Essa é uma lista completamente ordenada, você foi longe demais.
  • [5, 16, 20, 3, 8, 12, 9, 17, 20, 7]
  • O tamanho de gap três indica que cada grupo representado por cada terceiro número (e.g., 0, 3, 6, 9; 1, 4, 7; e 2, 5, 8) está ordenado, e não grupos de 3.
Next Section - 5.11. O Merge Sort