Ordenação por Seleção

Hoje vou apresentar mais um algoritmo de ordenação de vetores. É a Ordenação por Seleção (ou Selection Sort). Sem mais papo e antes mesmo da explicação, vamos ao seu pseudocódigo:

para i \leftarrow{} 1 até tamanho-1, faça
minimo \leftarrow{} i
para j \leftarrow{} i+1 até tamanho, faça
  se vetor[j] < vetor[minimo], então
   minimo \leftarrow{} j
  fim-se
fim-para
temp \leftarrow{} vetor[i]
vetor[i] \leftarrow{} vetor[minimo]
vetor[minimo] \leftarrow{} temp
fim-para

tamanho = comprimento do vetor

Funcionamento

A idéia é sempre procurar o menor elemento do vetor e inseri-lo no início do vetor. Procuramos o menor valor do vetor e colocamos ele em vetor[1]. Procuramos o menor valor do vetor excluindo o já colocado e colocamos ele em vetor[2]. E assim vamos indo até termos todo o vetor ordenado.

Partindo sempre a partir do último elemento reordenado (a partir do i), o programa procura o menor elemento no vetor e o substitue pelo elemento i atual.

Exemplo de Funcionamento

O programa recebe o seguinte vetor.

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

Aí ele começa com i=1i=1. Vou sempre marcar ii com a cor preta e minmin com a cor cinza.

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

Ele marca o próprio índice i como a variável minimo, que é sempre o menor elemento do vetor. Então, ele faz um para de j=2j=2 até o comprimento do vetor, com o objetivo de descobrir qual o menor elemento.

j=2j=2v[j]=3<v[minimo]=v[1]=5v[j] = 3 < v[minimo] = v[1] = 5, portanto minimo=j=2minimo = j = 2.

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

j=3j=3v[j]=3<v[minimo]=v[1]=5v[j] = 3 < v[minimo] = v[1] = 5, portanto minimo=j=2minimo = j = 2.

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

j=3j=3v[j]=7>v[minimo]=v[2]=3v[j] = 7 > v[minimo] = v[2] = 3, portanto não mexemos em nada.

j=4j=4v[j]=8>v[minimo]=v[2]=3v[j] = 8 > v[minimo] = v[2] = 3, portanto não mexemos em nada.

j=5j=5v[j]=2<v[minimo]=v[2]=3v[j] = 2 < v[minimo] = v[2] = 3, portanto minimo=j=5minimo = j = 5.

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

j=6j=6v[j]=2<v[minimo]=v[2]=3v[j] = 2 < v[minimo] = v[2] = 3, portanto minimo=j=5minimo = j = 5.

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

j=6j=6v[j]=5>v[minimo]=v[5]=2v[j] = 5 > v[minimo] = v[5] = 2, portanto não mexemos em nada.

Agora substituímos o v[minimo] pelo v[i], formando com isto o novo vetor:

v[1] v[2] v[3] v[4] v[5] v[6]
2 3 7 8 5 5

E assim vamos fazendo com os outros elementos até que todo o vetor esteja ordenado.

Custo

Este algoritmo não tem um melhor/pior caso, porque todos os elementos são varridos, sempre. Medir seu custo é simples. Custo de linha por linha…

n = tamanho do vetor

  1. nn
  2. nn
  3. _j=1nn\sum\_{j=1}^{n} n
  4. _j=1n1\sum\_{j=1}^{n} 1
  5. _j=1n1\sum\_{j=1}^{n} 1
  6. 00
  7. 00
  8. n1n-1
  9. n1n-1
  10. n1n-1
  11. 00

Você pode estar se perguntando porque eu coloquei este custo para a linha 5. Afinal, a linha 5 diria que este programa tem um melhor/pior caso, porque ela não seria executada se o se retornar falso. Mas o caso é que ela é desprezível. Uma soma como estas para o custo geral do nosso algoritmo não vai influenciar em nada. Quer ver? Vamos somar os custos com esta linha valendo 00 (como se nenhum se entrasse) e depois com ela valendo _j=1n\sum\_{j=1}^{n}.

Primeiro cálculo

T(n)=n+(n1)+(_j=1nn)+(j=1n1)×2+0×3+(n1)×3+0T(n) = n + (n-1) + (\sum\_{j=1}^n n) + \\ (\sum\\_{j=1}^n 1) \times 2 + 0 \times 3 + (n-1) \times 3 + 0 T(n)=n2+6n3=Θ(n2)T(n) = n^{2} + 6n - 3 = \Theta{}(n^2)

Segundo cálculo

T(n)=n+(n1)+(_j=1nn)+(j=1n1)×3+0×2+(n1)×3+0T(n) = n + (n-1) + (\sum\_{j=1}^n n) + \\ (\sum\\_{j=1}^n 1) \times 3 + 0 \times 2 + (n-1) \times 3 + 0 T(n)=1,5n2+6,5n3=Θ(n2)T(n) = 1,5 n^{2} + 6,5 n - 3 = \Theta{}(n^2)

Conclusão

Como vocês puderam ver, não faz diferença alguma o n2+n2\frac{n^{2} + n}{2} que aquela somatória nos proporciona. Já que todo o cálculo de algoritmos é baseado apenas no maior expoente de n e desprezamos todas as constantes (inclusive as que multiplicam o n de maior expoente, muitos passos são desprezíveis.

Ordenação por Inserção

Também conhecida como Insertion Sort, a Ordenação por Inserção consiste em inserir um elemento nn num vetor já ordenado de n1n-1 elementos. Neste artigo, apresento-lhes este simples algoritmo para ordenação de vetores.

O pseudocódigo da ordenação por inserção é o seguinte:

para j \leftarrow{} 2 até comprimento do vetor, faça
elemento \leftarrow{} vetor[j]
i \leftarrow{} j - 1
enquanto i > 0 e vetor[i] > elemento, faça
  vetor[i + 1] \leftarrow{} vetor[i]
  i \leftarrow{} i - 1
fim-enquanto
vetor[i + 1] \leftarrow{} elemento
fim-para

Para explicar eu vou fazer uma coisa que sempre faço para corrigir meus algoritmos, fingir que sou o programa, executando cada passo manualmente (claro que geralmente faço mais rápido, porque não preciso escrever tudo que faço). Vamos iniciar com o seguinte vetor v.

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

Aí o código me manda começar com j=2j=2 e iterar até o comprimento do vetor (6). A primeira ordem que ele me dá é para armazenar o elemento a[j]a[j] (a[2]a[2]) na variável elemento. Para facilitar toda a explicação eu vou sempre pintar de cinza o v[j]v[j] onde eu estou (no caso, o segundo elemento do vetor, 3) e de preto o vetor ainda não ordenado (elementos j\geq{}j).

v[1] v[2] v[3] v[4] v[5] v[6]
5 3 7 8 2 5

Então ele me diz que ij1i \leftarrow{} j-1. Portanto, i=1i=1. E agora ele me faz um enquanto (que poderia ser substituído por para) onde meu i deverá ir diminuindo. Vamos entrar no loop…

Bom, meu i=1i = 1 é maior que 0. v[1]=5v[1]=5 é maior que o elemento=3elemento=3? Sim, então vamos entrar no corpo do enquanto… Aqui ele me manda fazer um vetor[i+1]=vetor[i]vetor[i+1] = vetor[i], que nesse caso é fazer um v[2]=v[1]=5v[2]=v[1]=5.

v[1] v[2] v[3] v[4] v[5] v[6]
5 5 7 8 2 5

E agora subtrai de i um valor. Portanto, i=0i=0. Ele retorna ao enquanto, mas agora não satisfazemos a condição i>0i>0, por isso saímos do enquanto. Então ele pede para vetor[i+1]=elementovetor[i+1]=elemento (v[1]=elementov[1]=elemento). Portanto, o vetor fica assim:

v[1] v[2] v[3] v[4] v[5] v[6]
3 5 7 8 2 5

E incrementamos o j, agora j=3j=3.

v[1] v[2] v[3] v[4] v[5] v[6]
3 5 7 8 2 5

elemento=7elemento = 7

i=31=2i = 3-1 = 2

i>0i > 0… E 5>75 > 7? Não! Portanto, não entramos no enquanto.

v[3]=elementov[3] = elemento (nenhuma mudança)

E lá vamos para j=4j=4 e continuando até que vamos ter o vetor ordenado:

v[1] v[2] v[3] v[4] v[5] v[6]
2 3 5 5 7 8

Qual a lógica?

Como eu já disse na introdução, mas lá sem grandes explicações, a Ordenação por Inserção divide o vetor em dois. O primeiro (de elementos <j< j) contém todos seus valores ordenados e o segundo (de elementos j\geq{} j) contém os valores que devem ser inseridos no primeiro vetor já ordenado (por isso ele se chama Algoritmo de Inserção). A chave do algoritmo é o enquanto que acontece para ir deslocando todos os elementos para seu índice +1+1) contém todos seus valores ordenados e o segundo (de elementos j\geq{}j) contém os valores que devem ser inseridos no primeiro vetor já ordenado (por isso ele se chama Algoritmo de Inserção).

A variável elemento só serve para não perdermos o valor de v[j]v[j] (porque depois fazemos v[i+1]=v[i]v[i+1]=v[i] quando i=j1i=j-1)

Acredito que não tenham restado dúvidas. Dê mais uma olhada no algoritmo e tente implementar. Se tiver dificulade, coloque mensagens de debug estratégicas para entender o algoritmo. (ex.: no início do para coloque para imprimir o valor de j e no início de cada enquanto coloque para imprimir os valores elemento, i e v[i])

Custo

Você deve ter percebido que este algoritmo não tem um custo fixo. Se todo o vetor estiver ordenado, ele nunca precisará iterar o ii e portanto será executado bem mais rápido do que se o vetor estiver inteiro em ordem decrescente (quando ele sempre precisará iterar ii até o fim - 0). Então, neste artigo, gostaria-lhes de apresentar a análise de algoritmos baseada em casos. Para este programa, dizemos que:

  • Melhor caso é quando todos os elementos já estão ordenados. Custo: Θ(n)\Theta{}(n)
  • Pior caso é quando os elementos estão em ordem decrescente. Custo: Θ(n2)\Theta{}(n^{2})

Em alguns programas o caso médio é importante também, mas não é o caso da ordenação por inserção. Vemos que há uma diferença bem grande entre o custo dos dois casos. Por isso, precisamos conhecer onde que nosso algoritmo será implementado e quais as chances de ele ser o melhor ou pior caso. Em geral, o pior caso é o mais comum… Por isso, diremos que o custo deste algoritmo é Θ(n2)\Theta{}(n^{2}).

Introdução à Ordenação de Vetores

O que é um vetor?

Vetor é uma estrutura de dados que serve para substituir várias variáveis. Para um problema pequeno onde desejo armazenar dois inteiros e tirar o MMC deles eu posso usar duas variáveis: n1 e n2. Mas existem casos em que seria um número muito grande de variáveis (e em alguns deles nem sabemos ao certo, porque faremos uma alocação a partir de um número que o usuário pedir), por isso vetores são extremamente úteis.

No que consiste a ordenação?

Os algoritmos de ordenação tem como objetivo permutar uma seqüência n_1,n_2,n_3,n\_{1}, n\_{2}, n\_{3}, \ldots{} de forma que n_1n_2n_3n\_{1} \leq{} n\_{2} \leq{} n\_{3} \leq{} \ldots{}. A ordenação não precisa ser exatamente de um vetor, mas vetor é geralmente a estrutura que usamos para guardar uma lista de números para podermos ordená-los.

Por que ordenar?

Citando o Cormen:

  • Às vezes, a necessidade de ordenar informações é inerente a uma aplicação. Por exemplo, para preparar os extratos de clientes, os bancos precisam ordenar os cheques pelo número do cheque.
  • Os algoritmos freqüentemente usam a ordenação como uma sub-rotina chave. Por exemplo, um programa que apresenta objetos gráficos dispostos em camadas uns sobre os outros talvez tenha de ordenar os objetos de acordo com uma relação “acima”, de forma a poder desenhar esses objetos de baixo para cima.
  • Existe uma ampla variedade de algoritmos de ordenação, e eles empregam um rico conjunto de técnicas. De fato, muitas técnicas importantes usadas ao longo do projeto de algoritmos são representadas no corpo de algoritmos de ordenação que foram desenvolvidos ao longo dos anos. Desse modo, a ordenação também é um problema de interesse histórico.
© 2005–2020 Tiago Madeira