Você está na página 1de 18

TÉCNICAS DE PROGRAMAÇÃO

Paulo Lacerda
2 ALGORITMOS DE ORDENAÇÃO

Imagine que você precisa procurar por um nome em uma agenda de telefones sem
qualquer tipo de ordenação. Esta certamente seria uma tarefa complexa e demorada.
Esse mesmo problema ocorre com as informações em computação. Por esta razão,
sistemas que estruturam dados mais complexos com arrays e listas precisam
armazenar seu conteúdo em ordem crescente ou decrescente para facilitar as
pesquisas de informações dentro dessas estruturas.

Tente imaginar uma tabela de um banco de dados de uma instituição financeira


renomada, quantos milhares de registros com diversas informações importantes estão
armazenados ali? Suponha que essas tabelas não fossem ordenadas, quanto tempo
um usuário levaria para fazer uma simples consulta de dados?

Os algoritmos de ordenação são métodos que organizam estruturas complexas e seus


conjuntos de dados em uma ordem crescente ou decrescente, desta forma,
melhorando o desempenho de consultas (NASCIMENTO; MOZZAQUATRO;
ANTONIAZZI, 2016).

Os métodos de ordenação podem ser:

• Internos: Onde os registros cabem na memória principal (in-place) e podem ser


acessados imediatamente;
• Externos: Onde os registros não cabem na memória principal (not in-place) e só
podem ser consultados por meio de grandes blocos ou por acesso sequencial.
(NASCIMENTO; MOZZAQUATRO; ANTONIAZZI, 2016).

Existem dois tipos de métodos ordenação:

1) Simples: Usado para estruturas menores, mais simples e de fácil entendimento.

2) Complexo: Usado em estrutura de dados mais sofisticadas.

20
Em termos de complexidade, as informações fundamentais são:

• O número de comparações entre chaves C(n);


• O número de movimentações M(n) dos registros nas estruturas.

Sendo que n representa o número de registros.

2.1 Ordenação por bolha (bubble sort)

O algoritmo de ordenação por bolha opera de maneira simples. Ele percorre toda a
estrutura de dados (array) ou objeto a ser ordenado e faz uma comparação entre os
elementos adjacentes ou suas chaves verificando se estão na ordem correta. Essa
comparação pode ser realizada em ordem crescente ou decrescente (NASCIMENTO;
MOZZAQUATRO; ANTONIAZZI, 2016).

A cada iteração, o método de ordenação por bolha fica mais eficiente. Essas iterações
ocorrem por meio de um conjunto de comparações n-j, onde n é o número de
elementos e j é a contagem da iteração atual.

A respeito de complexidade, esse método tem seu pior caso em O(n²) e seu melhor
caso quando o algoritmo executa n operações relevantes, sendo n o número de
ordenações.

O método de ordenação por bolha é de complexidade quadrática, portanto não é


recomendado para arrays com um volume extenso de dados ou para situações que
exijam uma ordenação rápida.

2.1.1 Análise do algoritmo

A figura 2.1 mostra um script em linguagem Java implementado com o algoritmo


bolha.

21
Fonte: O autor, 2019.

Figura 2.1 – Implementação do algoritmo de ordenação por bolha

O script apresentado na figura 2.1 executa a ordenação de um array de 10 elementos


inteiros desordenados que são apresentados na linha 28. Na linha 29 é definido o
método de ordenação arrayRetornoOrdenado que é capaz de retornar um array
ordenado para alimentar a variável do tipo array. Na linha 30, o objeto é mostrado no
console ordenado.

É importante lembrar que existe a necessidade de converter o array para string senão
haverá, somente, a visualização de um número do objeto criado em memória, algo
parecido com ‘[I@1e643faf’. Isso pode ser feito por meio do método Arrays.toString().

O método bubbleSort recebe como parâmetro um array desordenado (linha sete). Na


linha nove é criada uma variável de troca para a verificação do final das comparações.
Um comando que garante a repetição da operação até que todos os elementos
estejam ordenados é iniciado na linha 13.

Observa-se que, entre as linhas 17 e 20, os valores e chaves são comparados e, caso
necessário, alterados de posição de forma a criar uma ordenação crescente dos
elementos. Na linha 24, o array é retornado em ordem.

22
Esse algoritmo tem a vantagem de ser fácil de compreensão e conhecido. Além disso, a
troca dos elementos de lugar é feita sem a necessidade de armazenamento
temporário, fazendo com que seu uso de memória seja mínimo. Apresenta,
entretanto, como principal desvantagem a baixa eficiência em organizar listas de
dados muito extensas.

2.1.2 Complexidade

Lembre-se que a complexidade desse tipo de algoritmo é do tipo n² etapas de


processamento para cada número n de elementos que serão ordenados. Desta
forma, se um array possui cinco elementos, o número de passos do processo será 25.

Quando lidamos com listas muito longas, a quantidade de etapas faz com que esse
método se torne impraticável no mundo real. Por esta razão, esse algoritmo só é
usado no mundo acadêmico para gerar conhecimento de lógica de ordenação.

2.2 Ordenação por inserção

Esse algoritmo de ordenação também é simples, pois examina um objeto em uma


estrutura de dados (array ou lista) e faz a comparação das suas chaves construindo
uma matriz final com a inserção de um elemento por vez (DEITEL; DEITEL, 2010). Esse
algoritmo também possui uma relação quadrática, portanto só deve ser usado em
estruturas pequenas de dados. Apesar disso, dentre os algoritmos de ordenação
simples, esse é o que apresenta maior eficiência.

É possível compreender o algoritmo de inserção por meio de uma comparação com


um jogo de cartas. Geralmente, quando um indivíduo está jogando, ele segura um
conjunto ordenado de cartas em sua mão. Ao retirar uma nova do monte, vai
compará-la com as outras que já está segurando e a posicionará em sua mão de uma
maneira que conserve a organização preestabelecida.

23
Essa mesma ideia pode ser traduzida para a lógica das máquinas por meio do
algoritmo de ordenação por inserção. O algoritmo percorre todo array, começando
pelo índice um, fazendo as comparações necessárias até posicionar o elemento no
local correto, ou seja, para cada iteração x do algoritmo, o elemento A[x] troca de
posição com cada elemento maior que ele a sua esquerda.

A grande vantagem do algoritmo de ordenação por inserção é que, sempre que um


novo elemento for adicionado ao array, ele já estará ordenado ou quase ordenado,
devido à natureza linear do processo. A principal desvantagem desse sistema é o custo
da movimentação dos elementos dentro do vetor (figura 2.2).

Fonte: O autor, 2019.

Figura 2.2 – Algoritmo por inserção

2.2.1 Complexidade

No melhor dos casos, a complexidade desse algoritmo é dada pelo termo O(n) quando
o array está completamente ordenado. Na pior das situações, tem-se o termo O(n²),
ou seja, um array com 20 elementos cria uma complexidade de 20², portanto 400
iterações.

24
2.2.2 Análise do algoritmo

A figura 2.3 mostra um script em linguagem Java implementado com o algoritmo de


inserção.

Fonte: O autor, 2019.

Figura 2.3 – Implementação do algoritmo e ordenação por inserção

O script executa a ordenação de um array de dez elementos inteiros desordenados


que foram declarados na linha 26. Na linha 27 é determinado o método de retorno do
array ordenado. Na linha 28, é feita a impressão do array em forma de string, usando
o método Arrays.toString().

O método de ordenação tem início na linha nove com a declaração de duas variáveis:

1) Variável J: recebe os valores das alterações da chave.

2) Variável value: recebe o elemento temporariamente para ser trocado de posição.

Logo em seguida, ocorre o cascateamento de dois comandos de repetição. O primeiro


determina a leitura de todo o array, elemento por elemento. O segundo faz a troca
quando a chave for maior que zero e menor que a variável value. Por fim, o array
ordenado ao final do loop é retornado a linha 27 e impresso na linha 28.

25
Uma vantagem do algoritmo de ordenação por inserção é que nenhum movimento e
realizado sem necessidade, pois a ordenação só ocorre quando algo está fora do lugar.

2.3 Ordenação por seleção (selection sort)

O algoritmo por seleção é a ferramenta mais natural para classificação de elementos


dentro de uma estrutura de dados. Esse algoritmo é usado para ordenar um vetor por
meio do K-ésimo menor ou maior elemento em uma estrutura de dados complexa, por
exemplo, um array ou lista (DEITEL; DEITEL, 2010).

O algoritmo percorre a matriz várias vezes (figura 2.4), selecionando o menor próximo
item e posicionando-o no local correto, ou seja, funciona seguindo os seguintes
passos:

• Encontre o menor elemento dentro da estrutura de dados;

• Compare com o elemento a seguir;

• Se for menor, troque esse elemento com o primeiro da matriz.

Fonte: O autor, 2019.

Figura 2.4 – Mecânica do selection sort

Inicialmente, o primeiro elemento da matriz é considerado o menor elemento (2.5).

Fonte: O autor, 2019.

Figura 2.5 – Menor elemento

26
Em seguida, é feita uma busca para encontrar o menor elemento da matriz, neste
caso, é o número um. Se o valor do menor elemento for inferior ao do elemento
primeiro, ocorre uma troca. Caso contrário, as posições se mantém.

O próximo laço considera o segundo elemento da tabela e executa o algoritmo


novamente, desta vez buscando o segundo menor valor.

Fonte: O autor,2019.

Figura 2.6 – Comparação e troca

2.3.1 Complexidade

Esse algoritmo também possui uma complexidade quadrática, ou seja, usa o termo
O(n2). Como, em cada iteração, ele compara um elemento a todos os outros, não
existe melhor ou pior caso, afinal, mesmo que o array esteja perfeitamente ordenado,
o algoritmo será executado do começo ao fim.

27
Fonte: O autor, 2019.

Figura 2.7 – Complexidade

Esse algoritmo apresenta as seguintes vantagens:

• Simplicidade de implementação;
• Não utiliza vetores auxiliares de armazenamento temporário, por isso não
ocupa muito espaço memoria;
• Possui velocidade para algoritmo de ordenação para estruturas pequenas.

Apesar disso, possui algumas desvantagens como:

• Operação vagarosa em estruturas de dados muito grandes;


• Sempre executa (n²-n)/2 comparações, independentemente do quão ordenada
está o array/lista.

2.3.2 Análise do algoritmo

A figura 2.8 mostra um script em linguagem Java implementado com o algoritmo por
seleção em uma estrutura de dados tipo array composta por 10 elementos não
ordenados do tipo inteiro.

28
Fonte: O autor, 2019.

Figura 2.8 – Algoritmo de ordenação por seleção

Nota-se que os valores do array e a variável que o recebe estão declarados,


respectivamente, nas linhas 28 e 29.

Na linha 30, o comando de exibição do array é organizado pelo método de ordenação


por seleção. Método este que tem início na linha sete com recebimento de
parâmetros, um vetor e um tamanho de vetor.

Repare que o método é composto de dois comandos de repetição, um mais externo e


outro mais interno. O loop mais externo (linha nove) é responsável por percorrer todo
o array e é executado n-1 vezes. Para cada iteração (i) entre 0 e n-2, o loop interno
interage j= (n-1) - i vezes. Como as comparações de chaves são realizadas no laço
interno, ocorrem O(n2) comparações, sendo n o número de elementos da matriz. Esse
número é fixo tanto no melhor quanto no pior dos casos.

29
Podem ocorrer variações no script como posicionar o número maior no final da matriz
ao invés do menor no início, entretanto elas não alteram a maneira básica como o
elemento mínimo (ou máximo) é encontrado e nem melhoram a eficiência do
algoritmo.

2.4 Ordenação por pente (comb sort)

O algoritmo de ordenação por pente (combo sort) opera na base de troca de posições
exatamente como o algoritmo por bolha (bubble sort), entretanto realiza a operação
de maneira melhorada, pois processa os dados previamente. Sua principal função é
organizar estruturas de dados como listas e arrays (ELKAHLOUT; MAGHARI, 2017).

Uma comparação dos elementos é realizada por passos de posições distanciadas umas
das outras. A esses espaços é dado o nome de GAP (figura 2.9). Em cada iteração, esse
passo se torna menor até se igualar a um. O princípio do algoritmo é que os elementos
maiores sejam movidos na direção do final da matriz para evitar as chamadas
tartarugas (valores pequenos no final do array) que prejudicam o desempenho do
algoritmo de bolha.

Fonte: O autor, 2019.

Figura 2.9 – GAP de 3 posições

O algoritmo de pente faz uso de um fator de encolhimento de valor


1.247330950103979 que determina o GAP a ser usado na ordenação da lista, sempre
arredondando para o valor mais próximo.

Desta forma, pode-se dizer que, enquanto o GAP do algoritmo de bolha é sempre 1, o
de pente apresenta uma variação que tem início em X e termina em 1.

30
GAP = 3/1,3 = 2,3 = 2.

Fonte: O autor, 2019.

Figura 2.10 – GAP reduzido

A cada iteração, os valores das extremidades do GAP são comparados. Se o valor inicial
for menor que o final, eles são trocados. Caso contrário são mantidos. O GAP se
desloca, casa por casa, para a direita até o final da estrutura (figura 2.11).

Fonte: O autor, 2019.

Figura 2.11 – Troca dos valores

31
2.4.1 Complexidade

No melhor dos cenários, quando a matriz já está perfeitamente ordenada e não são
necessárias alterações, a complexidade da ordenação por pente é de O(n), por outro
lado, no pior dos casos ela é de O(n²).

Uma das principais vantagens desse algoritmo é que, durante as etapas de


classificação, não é necessário manter controle das operações de troca para saber qual
é o momento de parar. Além disso, apresenta maior eficiência nas primeiras iterações
e menor nas ultimas, pois passa a se comportar de maneira parecida com o método de
ordenação por bolha.

Ensaios experimentais indicam que o desempenho da ordenação por pente pode ser
comparado ao Quick Sort que é considerado o algoritmo de ordenação mais rápido
disponível.

2.4.2 Análise do algoritmo

Na figura 2.12, podemos ver um script em linguagem Java que implementa o algoritmo
de ordenação por pente para organizar uma estrutura de dados tipo array. Por meio
de sua análise, seremos mais capazes de compreender a aplicação do algoritmo.

Fonte: O autor, 2019.

Figura 2.12 – Algoritmo de ordenação por pente


32
O algoritmo da figura 2.12 ordena uma estrutura de dados do tipo array composto por
nomes que são declarados na linha 33 do código. Uma varável tipo array de string que
recebe o vetor ordenado do método combsort() que possui o parâmetro (nomes) é
determinado na linha 34.

Além disso, são declarados:

• O método de ordenação combSort (Linha nove);

• A variável de GAP (Linha dez);

• Uma varável do tipo primitiva Boleana (linha 11).

Ocorre um cascateamento de dois comandos de repetição, um externo que é


responsável pela análise do GAP e da variável de troca; e um interno cuja função é
trocar a posição dos elementos dentro da estrutura, além de fazer as comparações.

Nota-se que o fator de encolhimento foi usado para determinar o tamanho do GAP no
laço mais externo. Já no laço mais interno, foi empregado para fazer uma comparação
de elementos por meio de comando ‘compareTo’. Na linha 25 é incrementada a
iteração e na linha 28 é determinado que, após a ordenação, o array retorna para ser
exibido na linha 35 do código.

Pode-se concluir que, no algoritmo por pente, se o tamanho do GAP aumenta, o


número de trocas diminui. Isso, na maioria das vezes, torna o algoritmo mais veloz e
eficiente.

2.5 Coquetel (CockTail)

O algoritmo do tipo coquetel recebeu esse nome porque percorre a matriz de um lado
para o outro como se a estivessem chacoalhando. É similar ao sistema de ordenação
por bolha, afinal, ambos pertencem à família de algoritmos baseados em comparação.

A principal diferença entre esses dois sistemas é a maneira como eles percorrem a
estrutura dos dados. Enquanto o algoritmo por bolha se movimenta da esquerda para
a direita, o coquetel percorre ambas as direções (ELKAHLOUT; MAGHARI, 2017) o que
faz com que a sua implementação seja muito mais difícil (DEITEL; DEITEL, 2010) (figura
2.13)

33
O algoritmo de coquetel, em sua primeira iteração, envia o maior elemento para o
final da lista. Na segunda, transporta o menor valor para o início da matriz e continua
intercalando até o array estar completamente ordenado.

Fonte: O autor,2019.

Figura 2.13 – Percorre em ambas as direções

A ideia é posicionar os elementos maiores no final da fila e os menores no início.


Sempre começando pela esquerda na primeira corrida.

Fonte: O autor,2019.

Figura 2.14 – Posicionamento dos elementos

2.5.1 Complexidade

Esse algoritmo de ordenação pode ser aplicado em estruturas de dados como arrays e
listas. O seu nível de complexidade é de O(n2) no pior dos casos e O(n) no melhor dos
cenários.

Sua maior vantagem é resolver as tartarugas (elementos menores no final da matriz) e


os coelhos (elementos maiores no meio da estrutura). Isso faz com que seu
desempenho seja superior à ordenação por bolha.

34
2.5.2 Análise do código

A figura 2.15 mostra um script em linguagem Java implementado com o algoritmo


coquetel de uma estrutura de dados do tipo array. Por meio de sua análise, é possível
ter uma compreensão melhor do algoritmo.

Fonte: O autor, 2019.

Figura 2.15 – Algoritmo de ordenação coquetel

O script da figura 2.15 contém um array de dez elementos desordenados de valores do


tipo inteiro entre zero e nove que foi estabelecido na linha 31. Uma variável do tipo
array recebe, na linha 32, o conjunto ordenado que retornou do método cocktailSort().

O método cocktailSort tem início na linha sete com o recebimento do parâmetro que,
neste caso é o array de inteiros não ordenado. Nas linhas nove e dez, são declaradas as
variáveis de controles. Logo em seguida, um primeiro comando de repetição é usado
para fazer a análise da variável de troca e se a variável de início é menor que a do fim.
Dentro desse laço, existem dois comandos de repetição.

35
No primeiro, é feita uma comparação entre os elementos para entender qual é o maior
valor entre os pares e para posicioná-lo no final da matriz. No segundo, é feito um
contraste entre os pares para analisar qual é o elemento de menor valor para, então,
movê-lo para o início do conjunto. Esse processo se repete até que a condição inicial
do primeiro laço não seja satisfeita. Na linha 27, o array já ordenado retorna impresso
na linha 33.

Embora o algoritmo coquetel seja ligeiramente mais eficiente que o algoritmo por
bolha, é quase sempre mais lento que o de inserção. Desta forma não é muito
adequado para o uso em produção.

Conclusão

Nesse bloco foram apresentados os algoritmos de ordenação, também conhecidos


como métodos de classificação. Eles estão presentes no dia a dia da programação, pois
tem a finalidade de colocar os dados em ordem crescente ou decrescente para facilitar
o trabalho dos algoritmos de busca.

Os métodos de ordenação são mais simples de serem implementados e mais eficientes


em estruturas menores. Foram apresentadas, também, as características e as
complexidades dos tipos mais comuns que incluem o bubble sort, o selection sort, o
insertion sort e o comb sort.

REFERÊNCIAS

DEITEL, P.; DEITEL, H. Java: como programar. 8. ed. São Paulo: Pearson, 2010. Cap. 9

ELKAHLOUT, A. H.; MAGHARI, A. Y. A. A comparative study of sorting algorithms comb,


cocktail and counting sorting. International Research Journal of Engineering and
Technology (IRJET), v. 4, n. 1, 2017.

NASCIMENTO, J. S.; MOZZAQUATRO, P. M.; ANTONIAZZI, R. L. Análise e comparação de


algoritmos implementados em Java. Revista Eletrônica Unicruz. Rio Grande do Sul, v.
1, n. 1, 2015. Disponível em: <https://bit.ly/35I8Fxd>. Acesso em: 6 nov. 2019.

36

Você também pode gostar