Você está na página 1de 33

Algoritmos e Estrutura

de Dados Avançado
Algoritmos de Ordenação

Prof. PhD. Antonio C. Zena

E-mail: antonio.zena@anhanguera.com
O que é um algoritmo de ordenação?
Algoritmos de ordenação são um conjunto de instruções que
recebem uma matriz ou lista como entrada e organizam os itens em
uma ordem específica.

As ordenações são mais comumente feitas em ordem numérica ou


alfabética (ou lexicográfica) e podem ser em ordem crescente (A-Z,
0-9) ou decrescente (Z-A, 9-0).
Por que os algoritmos de ordenação são importantes
Os algoritmos de ordenação são muito importantes na ciência da
computação, pois frequentemente podem reduzir a complexidade de um
problema.
Esses algoritmos têm aplicações diretas em algoritmos de busca,
algoritmos de banco de dados, métodos de divisão e conquista, algoritmos
de estrutura de dados e muitos outros.
Compromissos dos Algoritmos de Ordenação
Ao escolher um algoritmo de ordenação, algumas perguntas
precisam ser feitas
Quão grande é a coleção sendo ordenada?
Quanta memória está disponível?
A coleção precisa crescer?
As respostas a estas perguntas podem determinar qual algoritmo vai
funcionar melhor para cada situação.
Alguns algoritmos, como o Merge Sort, podem precisar de muito espaço ou
memória para serem executados, enquanto o Insertion Sort nem sempre é o
mais rápido, mas não requer muitos recursos para ser executado.
Então, você deve determinar quais são os seus requisitos e considerar as
limitações do seu sistema antes de decidir qual algoritmo de ordenação
usar.
Alguns Algoritmos de Ordenação Comuns
Alguns dos algoritmos de ordenação mais comuns são:
Selection sort - Ordenação por seleção
Bubble sort
Insertion sort - Ordenação por inserção
Merge sort - Ordenação por intercalação
Quick sort - Ordenação rápida
Heap sort
Counting sort - Ordenação por contagem
Radix sort
Bucket sort
Mas antes de entrarmos em cada um deles, vamos aprender um
pouco mais sobre o que classifica um algoritmo de ordenação.
Classificação de um Algoritmo de Ordenação
Os algoritmos de ordenação podem ser categorizados com base nos
seguintes parâmetros:

1. Baseado no número de trocas ou inversões necessários: Este é o


número de vezes que o algoritmo troca elementos para ordenar a
entrada.
O selection sort requer o mínimo número de trocas.
2. Baseado no número de comparações:
Este é o número de vezes que o algoritmo compara elementos para
ordenar a entrada.
Usando a notação Big-O, os exemplos de algoritmos de ordenação
listados acima requerem pelo menos O(nlogn) comparações no
melhor caso, e O(n^2) comparações no pior caso para a maioria das
saídas.
3. Baseado no uso ou não de recursão:
Algoritmos de ordenação, como o Quicksort, usam técnicas recursivas
para ordenar a entrada.
Outros algoritmos de ordenação, como o Selection Sort ou o Insertion Sort,
usam técnicas não recursivas.
Por fim, alguns algoritmos de ordenação, como o Merge Sort, utilizam tanto
técnicas recursivas quanto não recursivas para ordenar a entrada.
4. Baseado na estabilidade:
Algoritmos de ordenação estáveis mantêm a ordem relativa dos elementos com
valores ou chaves iguais. Algoritmos de ordenação instáveis não mantêm a
ordem relativa dos elementos com valores / chaves iguais.
Exemplo: imagine que tenha o array de entrada [1, 2, 3, 2, 4].
E para diferenciar entre os dois valores iguais, 2, vamos atualizá-los para 2a e
2b, tornando o array de entrada [1, 2a, 3, 2b, 4].
Algoritmos de ordenação estáveis manterão a ordem de 2a e 2b, o que
significa que o array de saída será [1, 2a, 2b, 3, 4].
Algoritmos de ordenação instáveis não mantêm a ordem de valores iguais,
e o array de saída pode ser [1, 2b, 2a, 3, 4].
Insertion sort, Merge Sort e Bubble Sort, são estáveis, enquanto Heap Sort
e Quick Sort não são.
5. Baseado na necessidade de espaço adicional:
Algoritmos de ordenação podem ordenar uma lista sem criar uma lista
completamente nova. Esses são conhecidos como algoritmos de
ordenação in-loco e requerem um espaço extra constante O(1) para a
ordenação.
Enquanto isso, algoritmos de ordenação in-loco criam uma nova lista
durante a ordenação.
O Insertion sort e Quick sort, são algoritmos de ordenação in loco, pois
os elementos são movidos ao redor de um ponto de pivô e não utilizam
um array separado durante o processo de ordenação.
O Merge sort é um exemplo de algoritmo de ordenação fora do lugar,
pois o tamanho da entrada deve ser alocado antecipadamente para
armazenar a saída durante o processo de ordenação, o que requer
memória extra.
Bucket Sort
É um algoritmo de ordenação por comparação que opera em elementos
dividindo-os em diferentes Buckes (segmentos menores de mesmo tamanho)
e, em seguida, ordenando esses Buckes individualmente.
Cada Bucke é ordenado individualmente usando um algoritmo de ordenação
separado, como o insertion sort, ou aplicando o algoritmo de bucket sort
recursivamente.
O bucket sort é útil quando a entrada está uniformemente distribuída em
um intervalo.
Exemplo: suponhamos que tenhamos um grande array de números
inteiros de ponto flutuante distribuídos uniformemente entre um limite
superior e inferior.
Uma maneira simples de resolver esse problema seria usar outro
algoritmo de ordenação como Merge Sort, Heap Sort ou Quick Sort.
No entanto, esses algoritmos garantem uma complexidade de tempo
de melhor caso de O(NlogN).
Usando o algoritmo o bucket sort, a tarefa acima pode ser concluída
em tempo O(n).
Pseudo código para o Bucket Sort:

void bucketSort(float[ ] a,int n)


{
for(each floating integer 'x' in a)
{
insert x into bucket[n*x];
}
for(each bucket)
{
sort(bucket);
}
}
Counting Sort
Counting Sort é uma técnica de ordenação baseada em chaves
compreendidas em um intervalo específico. Ela funciona contando o
número de objetos com valores de chave distintos (tipo de hash). Em
seguida, fazendo alguma operação para calcular a posição de cada
objeto na sequência de saída.
Exemplo:
Suponha que você tenha uma lista de números inteiros de 0 a 5:
input = [2, 5, 3, 1, 4, 2]
Primeiro, precisa criar uma lista de contagens para cada valor único
na lista input.
Como você sabe que o intervalo do input é de 0 a 5, você pode criar
uma lista com cinco espaços reservados para os valores de 0 a 5,
respectivamente:
count = [0, 0, 0, 0, 0, 0]
# val: 0 1 2 3 4 5
Então, percorrer a lista ‘input’ e itera o índice para cada valor em um.
Exemplo: o primeiro valor na lista ‘input’ é 2, então você adiciona um ao valor
no segundo índice da lista ‘count’, que representa o valor 2:
count = [0, 0, 1, 0, 0, 0]
# val: 0 1 2 3 4 5
O próximo valor na lista ‘input’ é 5, então você adiciona um ao valor no
último índice da lista ‘coun’, que representa o valor 5:
count = [0, 0, 1, 0, 0, 1]
# val: 0 1 2 3 4 5
Continue até ter a contagem total para cada valor na lista de ‘input’.
count = [0, 1, 2, 1, 1, 1]
# val: 0 1 2 3 4 5
Finalmente, uma vez que sabe-se quantas vezes cada valor aparece na lista
‘input’, você pode facilmente criar uma lista ‘output’ ordenada. Percorra a
lista ‘count’ e, para cada contagem, adicione o valor correspondente (0 - 5) à
lista ‘output’ tantas vezes.

Por exemplo, não houve nenhum 0 na lista ‘input’, mas houve uma ocorrência
do valor 1, então você adiciona esse valor à lista ‘output’ uma vez:
output = [1]
Então houve duas ocorrências do valor 2, então você adiciona essas ao lista
‘output’:
output = [1, 2, 2]
E assim por diante até que você tenha a lista ‘output’ final ordenada:
output = [1, 2, 2, 3, 4, 5]
Propriedades
Complexidade espacial: O(K)
Desempenho no melhor caso: O(n+K)
Desempenho médio: O(n+K)
Desempenho no pior caso: O(n+K)
Estável: Sim ( K é o número de elementos distintos no array)
Insertion Sort (ordenação por inserção)
Insertion sort é um algoritmo de ordenação simples para um pequeno
número de elementos.
Exemplo:
Em insertion sort, você compara o elemento chave com os elementos
anteriores. Se os elementos anteriores são maiores do que o elemento
chave , então você move o elemento anterior para a próxima posição.
Comece do índice 1 até o tamanho do array de entrada.
[8 3 5 1 4 2]

Step 1 :
chave = 3 //começando do 1º índice.

Aqui, a ‘chave’ será comparada com os elementos anteriores.

Neste caso, a ‘chave’ é comparada com 8.


Como 8 > 3, mova o elemento 8 para a próxima posição e insira a ‘chave’ na
posição anterior.

Resultado: [ 3 8 5 1 4 2 ]
Step 2 :

chave = 5 //2º índice

8 > 5 //mover 8 para o 2º índice e inserir 5 no 1º índice.

Resultado: [ 3 5 8 1 4 2 ]
Step 3 :

chave = 1 //3º índice

8>1 => [ 3 5 1 8 4 2 ]

5>1 => [ 3 1 5 8 4 2 ]

3>1 => [ 1 3 5 8 4 2 ]

Resultado: [ 1 3 5 8 4 2 ]
Step 4 :

chave = 4 //4º índice

8 > 4 => [ 1 3 5 4 8 2 ]

5 > 4 => [ 1 3 4 5 8 2 ]

3 > 4 ≠> parar

Resultado: [ 1 3 4 5 8 2 ]
Step 5 :

chave = 2 //5º índice

8 > 2 => [ 1 3 4 5 2 8 ]

5 > 2 => [ 1 3 4 2 5 8 ]

4 > 2 => [ 1 3 2 4 5 8 ]

3 > 2 => [ 1 2 3 4 5 8 ]

1 > 2 ≠> parar

Resultado: [1 2 3 4 5 8]
Propriedades:
Complexidade espacial: O(1)
Complexidade temporal: O(n), O(n*n), O(n*n) para o melhor, médio e pior
caso, respectivamente.
Melhor caso: o array já está ordenado.
Caso médio: o array está ordenado aleatoriamente.
Pior caso: o array está ordenado em ordem inversa.
Ordenação in loco: Sim
Estável: Sim
Perguntas?

27/03/2024

Você também pode gostar