Introdução à ordenação
Nesta seção examinaremos os algoritmos mais básicos para ordenação de uma lista de dados (vetores). Os algoritmos examinados aqui não são eficientes e por isso para aplicações reais usualmente são implementados outros (como Ordenação Rápida/Quick Sort, Ordenação por Fusão/Merge Sort ou Ordenação por Monte/Heap Sort). O último a ser examinado (Inserção), é mais eficiente, principalmente se os dados estiverem "quase-ordenados".
O problema é, dada uma sequência de valores (em lista ou vetor), ordenar crescente (ou decrescente) essa sequência. Por exemplo, na figura abaixo, inicialmente os dados não estão ordenados {4, 2, 3, 0, 9, 3, 5, 7}. Um algoritmo de ordenação que recebesse esse vetor com n=8 valores, deveria trocar os valores de posição de modo a conseguir a seguinte situação final para o vetor: {0, 2, 3, 3, 4, 5, 7, 9}.
Para simplificar as explicações fixaremos um nome para o vetor e usaremos a notação V[i] para indicar o valor que está na posição i do vetor. Também convencionaremos que a primeira posição do vetor seja V[0], logo um vetor com N elementos terá o último deles na posição V[N-1].
Os métodos que examinaremos são os conhecidos: Método da Bolha (Buble Sort), Seleção Direta e Inserção Direta.
Método da Bolha (ou "Pedregulho")
A ideia do método da Bolha é inicia comparando os dois últimos elementos, o menor fica à esquerda, então comparar os dois anteriores e fazer a mesma coisa, desse modo o menor vai movendo-se para cima (como as bolhas). Mas também podemos fazer o "inverso", seguir empurrando o maior para baixo e assim, poderíamos apelidar essa variante de método do Pedregulho.
Vamos detalhar a primeira fase do método do Pedregulho, nessa fase empurramos o maior do N elementos para a última posição. O primeiro passo é comparar V[0] com V[1], se o valor em V[0] for maior que o valor em V[1], troque seus valores (diremos apenas Se (V[0] > V[1], troque-os). O passo 2 é: Se (V[1] > V[2], troque-os (logo V[2] terá o maior dos 3 primeiros). O passo 3 é: Se (V[2] > V[3], troque-os (logo V[3] terá o maior dos 4 primeiros). Segue comparando V[i] com V[i+1], até o passo N-1: Se (V[N-2] > V[N-1], troque-os (logo V[N-1] terá o maior dos 4 primeiros).
A figura abaixo ilustra os 7 primeiros passos, na etapa 1, do algoritmo para levar o maior elemento (considerando N=8) para a posição 8.
Note que na primeira imagem (acima, esquerda), existem os 8 na ordem inicial {4, 2, 3, 0, 9, 3, 5, 7}. Cada passo de comparação dos pares de vizinhos está indicado por duas setas sobre os valores a serem comparados. Na representação seguinte, o vetor é novamente apresentado, eventualmente tendo sido trocados os (se V[i]>V[i+1]). Na passo 7, usamos apenas uma seta para indicar que o maior dentre todos os elementos foi para a última posição.
Mas, como o objetivo é ordenar, não apenas descobrir o maior, podemos usar a mesma ideia (de modo indutivo): aplica-se o mesmo algoritmo (empurrar o maior para a última posição), mas considerando agora apenas os 7 primeiro elementos, pois o maior já está na posição 8. Novamente, isso resultará no segundo maior na posição 7
Mais uma vez (em geral) o vetor não está ordenado, mas podemos aplicar a mesma ideia mas 5 vezes: para V[0]..V[6] (maior deles para V[6]); para V[0]..V[5] (maior deles para V[6]); para V[0]..V[4] (maior deles para V[6]); para V[0]..V[3] (maior deles para V[6]); para V[0]..V[2] (maior deles para V[6]); para V[0]..V[1] (maior deles para V[1]).
Desse modo obtém-se o vetor em ordem crescente! Abaixo apresentamos o algoritmo do Pedregulho (Bolha) em C e em Python.
Método de ordenação Bolha/Pedregulo em C e em Python | ||
---|---|---|
Exemplo em C | Exemplo em Python | |
|
# Ordena bolha/pedregulho from __future__ import print_function; # imprime sem mudar linha Python 2 def imprimir (X, n) : print("(", end=""); for i in range(n) : print("%3d " % X[i], end=""); print(")"); def ordPedregulho (V, n) : for i in range(n) : for j in range(n-i-1) : if (V[j]>V[j+1]) : # troque-os aux = V[j]; # preservar V[j] V[j] = V[j+1]; V[j+1] = aux; def main () : V = [4, 2, 3, 0, 9, 3, 5, 7]; # definir vetor dados imprimir(V,8); ordPedregulho(V, 8); imprimir(V,8); main(); |
Método da Seleção Direta
A ideia o método da Seleção Direta é novamente deixar na última posição o maior elemento (ou o menor na primeira), mas diferentemente do método da Bolha, nesse mantém-se fixado o último elemento, comparando-o com o primeiro, com o segundo e assim por diante. Considerando novamente um vetor com 8 elementos, após 7 comparações (e eventuais trocas), o maior deles estará na posição 8.
Esse método está ilustrado na figura abaixo. Na primeira coluna estão os 7 passos para obter o maior elemento em V[7]. Na coluna 2 estão os 6 passos para obter o segundo maior para V[6]. Na coluna 3 estão os 5 passos para obter o terceiro maior para V[5] e na coluna 4 estão os 4 passos para obter o quarto maior para V[6]. Para finalizar o algoritmo deve-se fazer mais duas sequências de passos (ou laços), fixando-se o elemento 3 para obter o 5-ésimo (5=8-3) maior e fixando-se o elemento 2 para obter o 6-ésimo (6=8-2) maior.
Abaixo está uma implementação do método Seleção Direta em C e em Python.
Método de ordenação Bolha/Pedregulo em C e em Python | ||
---|---|---|
Exemplo em C | Exemplo em Python | |
|
# Ordena Selecao Direta from __future__ import print_function; # imprime sem mudar linha Python 2 def imprimir (X, n) : print("(", end=""); for i in range(n) : print("%3d " % X[i], end=""); printf(")"); def ordSelecao (V, n) : for i in range(n-1, 0, -1) : for j in range(i) : if (V[j]>V[i]) : # troque-os aux = V[j]; # preservar V[j] V[j] = V[i]; V[i] = aux; def main () : V = [4, 2, 3, 0, 9, 3, 5, 7]; # definir vetor dados imprimir(V,8); ordSelecao(V, 8); imprimir(V,8); main(); |
Note que os comandos para trocar os conteúdos entre V[j] e V[i] (subordinado ao if (V[j]>V[i])) poderia ser evitado, bastaria mais duas variáveis auxiliares, imax e max, respectivamente, para armazenar o índice do maior valor e o maior valor (max = V[imax]). Assim, ao final laço for j faríamos apenas uma troca (entre V[i] e V[imax]). Nessa variante do algoritmo, o número de passos e de comparações é o mesmo, entretanto o número de trocas (geralmente) seria bem menor!
Método da Inserção Direta
A ideia o método da Inserção Direta é um pouco diferentes do Bolha e Seleção, naqueles após cada laço interno um elemento maior ficava em sua posição "definitiva", no Inserção não. A ideia do Inserção é (na etapa i de ordenação): suponha que V[0] até V[i-1] esteja ordenado, então podemos estender a ordem localizando o primeiro elemento, da direita para a esqueda, que não seja maior que V[i], digamos que seja o V[j], então move-se todos eles uma posição para a sua direita, abrindo a posição V[j] para inserir o V[i]. Esse princípio é ilustrado abaixo com j=3:
1. V[0]<V[1]<V[2] odernados; 2. V[3] libera seu espaço (cópia em aux); 3. todos os elementos com valor estritamente maior que aux "dão um passo para a direita"; 4. a posição i=0 é liberada para inserir ali aux (estendendo a ordenação para 4 elementos). |
Fig. 4. Ilustração dos "deslocamentos" para inserir a chave (inicialmente) na posição V[3] para a posição correta (V[0]<V[1]<V[2]<V[3]. |
Esse método está ilustrado na figura abaixo. Na primeira coluna estão as 3 instruções para obter uma ordenação com os 2 primeiros elementos. Na coluna 2 estão as 4 instruções para obter ordenação dos 4 primeiros elementos e na coluna 3 estão as 5 instruções para obter ordenação dos 5 primeiros elementos.
É importante notar que se os dados estiverem já ordenados, serão realizadas apenas n=7 operações!
De modo geral, se a etapa de ordenação i resultar em ordenação completa dos dados, deste ponto em diante,
serão feitas apenas n-i-1 comparações e nenhuma troca (!).
Pode-se usar uma
bandeira
para identificar tal fato e finalizar o algoritmo.
Por exemplo, na última coluna da figura 5, se o valor em V[4] fosse 5 (ou qualquer outro valor maior),
haveria apenas três instruções (independe do valor 5):
copiar V[3] para aux,
comparar aux com V[2] e, como aux é maior (ou igual), finalizaria,
copiando aux novamente para V[3].
Abaixo está uma implementação do método Inserção Direta em C e em Python.
Método de ordenação Inserção Direta em C e em Python | ||
---|---|---|
Exemplo em C | Exemplo em Python | |
|
# Ordena Insercao Direta from __future__ import print_function; # imprime sem mudar linha Python 2 def imprimir (X, n) : print("(", end=""); for i in range(n) : print("%3d " % X[i], end=""); printf(")"); def ordInsercao (V, n) : for i in range(1, n) : aux = V[i]; # "abrir espaco" copiando V[i] j = i-1; while (j > -1 and V[j] > aux) : # "mova" para direita os maiores que aux V[j+1] = V[j]; # mover para direita V[j] j -= 1; V[j+1] = aux; # coloque aux na posicao correta def main () : V = [4, 2, 3, 0, 9, 3, 5, 7]; # definir vetor dados imprimir(V,8); ordInsercao(V, 8); imprimir(V,8); main(); |
Bem, em resumo é isso. Experimente copiar esses códigos, tente algumas modificações, teste até estar seguro de ter compreendido.
Leônidas de Oliveira Brandão
http://line.ime.usp.br
Alterações:
2020/08/20: acerto no formato
2020/08/15: novo formato, pequenas revisões
2020/08/12: novo formato, pequenas revisões
2020/06/14: inserção de rótulos para as figuras
2019/06/16: versão inicial