Algoritmos de Ordenação: Selection Sort

Henrique Braga
7 min readMar 5, 2018

--

Seguindo com a série, iremos falar de outro algoritmo de ordenação conhecido como selection sort (ordenação por seleção).

Vamos ao Selection Sort

Resumo do resumo: sua ideia consiste em ordenar a lista “selecionando” a cada iteração o menores itens possíveis e os colocam da esquerda para a direita.

Assim como o Bubble Sort, é necessário para cada item da lista percorrê-la toda (logo, serão necessários dois loops: um para cada elemento da lista e outro para cada um desses elementos percorrer toda a lista).

Suas principais vantagens estão na fácil implementação do algoritmo, além de ocupar pouca memória se comparado a algoritmos como quick e merge sort que utilizam a estratégia de “dividir e conquistar” (que serão explicados em outros posts futuramente).

A desvantagem é que um dos algoritmos de maior tempo de execução em todos os casos (O(n²) — será explicado o porquê deste tempo de execução neste artigo), o que o torna uma opção ruim para listas com um grande número de itens.

Para entender melhor a ideia que algoritmo aplica — ao invés de uma lista, pense que temos duas sublistas: uma sublista que está ordenada e outra que está desordenada.

Como não foi iniciado o processo de “seleção” (que será explicado a seguir), faz sentido considerar que a lista toda não está ordenada. Logo, não há nenhum elemento na “lista ordenada” ainda e a lista desordenada contém todos os elementos.

Temos então na lista ordenada: nenhum elemento. E na lista desordenada? Todos os elementos da lista principal: 5, 3, 1, 2, 4

Agora, o que este “processo de seleção” citado acima realmente significa?

O processo de seleção obtém o menor elemento da lista desordenada. Por enquanto, ignore os detalhes da implementação e imagine que percorremos a lista desordenada item a item para saber qual o menor número.

Qual é o menor número desta lista desordenada? É o número 1, obviamente!

Este então, foi o número selecionado pelo processo. Colocamos ele então na lista ordenada e retiramos da lista desordenada.

Para não nos perdermos, eis aqui o que feito:

  • A lista principal contém os elementos sequencialmente: 5, 3, 1, 2, 4;
  • A lista principal foi subdividida logicamente em duas sublistas: a lista ordenada (que inicialmente está vazia) e a lista desordenada (que inicialmente contém todos os elementos da lista principal);
  • O processo de seleção rodou uma vez. Com isso, se obteve o menor item da lista desordenada (que tinha os números 5, 3, 1, 2, 4: logo, o menor número dentro deste conjunto é o número 1).
  • Colocamos o número 1 (menor item da lista) na lista ordenada ( que na verdade é o índice da primeira posição da lista principal) e remove este número da lista desordenada (não se preocupe, pois maiores detalhes serão explicados sobre essa parte. Mas o que ocorre no algoritmo é que os itens são trocados dentro da lista principal: assim o certo a se dizer é que o elemento que estava no índice da lista que está se iterando no primeiro loop pela posição do elemento que possui menor valor);

Este processo de seleção é realizado até que a lista desordenada esteja vazia (ou mais precisamente, quando o primeiro loop terminar). Com isso, há a certeza de que a lista ficará ordenada.

No próximo processo de seleção, será ignorado o número 1 (ou o que está à esquerda).

Mas por que? Ora, ele já foi “selecionado” e agora faz parte da sublista ordenada e sabe-se também que não será encontrado ninguém menor dentro da sublista desordenada.

O menor item encontrado é o número 2. Coloque na “sublista ordenada”
O menor item encontrado é o número 3. Coloque na “sublista ordenada”
O menor item encontrado é o número 4. Coloque na “sublista ordenada”

Pronto! Os elementos encontram-se ordenados, pois não há mais nada na lista desordenada.

Vamos ignorar esta divisão lógica das sublistas ordenadas e desordenadas para entender o que ocorre de fato no processo do algoritmo. Existe apenas uma lista principal e os itens obtidos por meio do processo de seleção são colocados da posição mais à esquerda da lista principal. Em outras palavras: os itens são posicionados da esquerda para a direita, conforme o processo de seleção obtém os menores itens.

Na próxima iteração do primeiro loop, são ignorados os itens que estão à esquerda da posição do mesmo, somente iterando e comparando os itens que estão à direita. Esse comportamento ocorre porque sabe-se que tudo que está à esquerda já está na ordem correta e o que está à direita, pode ou não estar ordenado (o algoritmo considera que o que está à direita está desordenado). Isto significa que se a lista possui os itens 1, 2, 5, 4, 3 e o primeiro loop está no terceiro item (5) — todos os itens à esquerda do terceiro item (1 e 2) não serão considerados para a realização do processo de seleção.

Ao ter certeza de qual é o menor item (essa informação é obtida ao fim do segundo loop) e sua posição na lista, pode-se então trocar os valores de posição. Com isso, o item de menor valor obtido ficará na posição que está sendo iterado no primeiro loop e o valor que estava neste entra na lista dos “desordenados”. (Se não ficou claro, dê uma olhada nas imagens acima, pois é exatamente aquilo que ocorre a cada processo de seleção)

Aplicando o processo de seleção citado acima, a lista modifica-se da seguinte maneira:

Estado Inicial: [5, 3, 1, 2, 4]

Após a primeira seleção : [1, 3, 5, 2, 4]

Após a segunda seleção: [1, 2, 5, 3, 4]

Após a terceira seleção: [1, 2, 3, 5, 4]

Após a quarta seleção: [1, 2, 3, 4, 5]

Desempenho

Agora, vamos entender o desempenho do selection sort. Sabe-se que o processo de seleção precisa varrer sempre a “lista desordenada” para comparar os itens. Essa lista, como pôde ser observado na explicação acima, vai decrementando em um conforme as iterações.

Utilizando o exemplo acima como base:

Dado uma lista de cinco posições [5,4,3,2,1]:

Serão inicialmente quatro comparações (ou 5–1) para o processo de seleção obter o menor item (o item que está sendo iterado (no caso índice 0) não precisa ser comparado. Logo, conta-se do segundo ao quinto item, ou seja dos índices 1 a 4: 1, 2, 3, 4).

No segundo processo de seleção, serão três comparações (ou 5–2) nos índices 3, 4 e 5

No terceiro processo de seleção serão duas comparações (ou 5–3) nos índices 4 e 5.

No quarto processo de seleção será apenas uma comparação (ou 5–4) no índice 5

Pode-se expressar isso então, da seguinte forma:

(5–1) + (5- 2) + (5–3) + (5–4) = 4 + 3 + 2 + 1 = 10

Se os pares somados forem sempre agrupados do maior para menor par, teremos como resultado o mesmo valor do tamanho da lista (no exemplo acima, 5):

[(5 - 1) + (5 - 4)] + [(5 - 3) + (( 5- 2))][4 + 1] + [2 + 3] = 5 + 5 = 10

Em caso de agrupar em pares e algum acabe ficando sem par (isso ocorre quando a quantidade de itens na lista é par), basta somá-lo isoladamente da mesma forma (o que sobrar valerá sempre a metade do tamanho da lista).

Por exemplo, imagine que a lista tenha seis elementos, então, seguindo o mesmo raciocínio acima:

[(6–1) + (6–5)] + [(6–2) + (( 6 - 4)] + [(6–3)][5 + 1] + [4 + 2] + [3] = 6 + 6 + 3 = 15

Se consideramos n como o número de elementos na lista, pode-se descobrir o número de pares da seguinte forma:

número de pares = (n — 1) / 2

Para uma lista com cinco elementos:

(5–1) / 2 = 2 pares

Para uma lista com seis elementos:

(6 – 1) / 2 = 5 / 2 = 2,5 pares

Mas ainda não sabemos o que realmente queremos: o número de operações executadas. Sabemos como descobrir o número de “pares” dado o tamanho da lista. Porém precisamos ainda multiplicar pelo tamanho da lista, pois conforme explicado acima, a soma de cada agrupamento é igual ao tamanho da lista.

Para uma lista com cinco elementos:

5 * 2 = 10 operações6 * 2,5 = 15 operações

A fórmula para calcular o número de operações no selection sort é:

n * (n — 1) / 2

Vamos aplicar a fórmula e validar se o número de operações está sendo calculado corretamente:

4 * [(4–1) / 2] = 4* (3 / 2) = 6 operações

Para uma lista com cinco elementos:

5 * [(5 - 1) / 2] = 5 * (4 / 2) = 10 operações

Para uma lista com seis elementos:

6 * ([(6 — 1) / 2]) = 6 * (5 / 2) = 15 operações

Logo, abstraindo a fórmula acima:

n * (n — 1) / 2 = (n² — n) / 2

Para análise assintótica, podemos ignorar os valores constantes. O que importa mesmo n² / 2 , pois conforme a lista crescer, este valor irá aumentar quadraticamente. Por isso, o algoritmo selection sort possui tempo de execução O().

Por fim, segue uma implementação em Python para o algoritmo selection sort

Exemplo código selection sort usando Python

Ufa, terminamos por aqui! Até a próxima!

--

--

Henrique Braga
Henrique Braga

Written by Henrique Braga

“If you’re not making someone else’s life better, then you’re wasting your time. Your life will become better by making other lives better.”

Responses (1)