O Merge Sort é um dos algoritmos de ordenação mais eficientes. Ele funciona com base no princípio de Dividir para Conquistar, baseado na ideia de dividir uma lista em várias sub-listas até que cada sub-lista consista em um único elemento e, então, mesclar essas sub-listas de maneira que resulte em uma lista ordenada.
Regra de Funcionamento do Merge Sort
O conceito de Dividir para Conquistar envolve três passos:
- Dividir o problema em múltiplos subproblemas.
- Resolver os Subproblemas. A ideia é quebrar o problema em subproblemas atômicos, onde eles são de fato resolvidos.
- Combinar as soluções dos subproblemas para encontrar a solução do problema atual.
Portanto, a regra de funcionamento do Merge Sort envolve os seguintes passos:
- Dividir o array não ordenado em subarrays, cada um contendo um único elemento.
- Pegar pares adjacentes de arrays de um único elemento e mesclá-los para formar um array de 2 elementos.
- Repetir o processo até que um único array ordenado seja obtido.
Um array de Tamanho ‘N’ é dividido em duas partes, ‘N/2’ tamanho de cada. Então, esses arrays são divididos ainda mais até chegarmos a um único elemento. O caso base aqui é alcançar um único elemento. Quando o caso base é atingido, começamos a mesclar a parte esquerda e a parte direita e obtemos um array ordenado no final. O Merge Sort quebra repetidamente um array em vários subarrays até que cada subarray consista em um único elemento e mesclando esses subarrays de maneira que resulte em um array ordenado.
Implementações do Merge Sort
Vamos implementar o algoritmo de ordenação Merge Sort em Java, C e Python.
Implementação em Java
public class MergeSort {
void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int L[] = new int[n1];
int R[] = new int[n2];
for (int i = 0; i < n1; ++i)
L[i] = arr[l + i];
for (int j = 0; j < n2; ++j)
R[j] = arr[m + 1 + j];
int i = 0, j = 0;
int k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void sort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
sort(arr, l, m);
sort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
public static void main(String args[]) {
int arr[] = { 12, 11, 13, 5, 6, 7 };
MergeSort ob = new MergeSort();
ob.sort(arr, 0, arr.length - 1);
System.out.println("Sorted array");
for (int i = 0; i < arr.length; ++i)
System.out.print(arr[i] + " ");
}
}
Output:
Sorted array
5 6 7 11 12 13
Este código define uma classe MergeSort
em Java que implementa o algoritmo de ordenação por mesclagem (Merge Sort). O Merge Sort é um algoritmo de ordenação eficiente baseado no princípio de dividir para conquistar. Aqui está o que cada parte do código faz:
- Método
merge
: Este método é responsável por mesclar dois subarrays do array principalarr[]
. Os parâmetrosl
,m
, er
representam, respectivamente, o índice inicial, o ponto médio e o índice final do subarray que precisa ser mesclado. O método cria dois arrays temporáriosL[]
eR[]
para armazenar os elementos dos subarrays esquerdo e direito, copia os dados para esses arrays temporários e, em seguida, mescla os arrays temporários de volta no array originalarr[]
de maneira ordenada. - Método
sort
: Este método é a implementação recursiva do Merge Sort, que divide o array em subarrays (esquerda e direita) até que cada subarray contenha apenas um elemento (divisão) e, em seguida, começa a mesclar esses subarrays de volta de forma ordenada (conquista). Ele faz isso encontrando o ponto médiom
do subarray definido pelos índicesl
(esquerda) er
(direita), e chamando a si mesmo recursivamente para ordenar as duas metades do array antes de chamar o métodomerge
para mesclar as metades ordenadas. - Método
main
: Este método é o ponto de entrada do programa. Ele cria um array de inteirosarr[]
com alguns valores não ordenados, cria uma instância da classeMergeSort
, chama o métodosort
para ordenar o array inteiro (do índice 0 ao último índice) e, finalmente, imprime o array ordenado na saída padrão.
Em resumo, este código ordena um array de inteiros usando o algoritmo Merge Sort e imprime o array ordenado na saída padrão. É um exemplo clássico de aplicação do princípio de dividir para conquistar para ordenação de dados.
Implementação em C
#include <stdio.h>
#include <stdlib.h>
void merge(int arr[], int l, int m, int r) {
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int L[n1], R[n2];
for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1+ j];
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
}
else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l+(r-l)/2;
mergeSort(arr, l, m);
mergeSort(arr, m+1, r);
merge(arr, l, m, r);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int arr_size = sizeof(arr)/sizeof(arr[0]);
mergeSort(arr, 0, arr_size - 1);
printf("Sorted array is \n");
for (int i=0; i < arr_size; i++)
printf("%d ", arr[i]);
return 0;
}
Output:
Sorted array is
5 6 7 11 12 13
Este código é uma implementação do algoritmo de ordenação Merge Sort em C. Ele ordena um array de inteiros em ordem crescente. O código é dividido em três partes principais: a função merge
, a função mergeSort
e a função main
. Aqui está o que cada parte faz:
Função merge
- Mescla dois subarrays de
arr[]
. - O primeiro subarray é
arr[l..m]
e o segundo subarray éarr[m+1..r]
. l
,m
, er
são índices do array, ondel
é o índice do início do subarray,m
é o meio, er
é o fim.- Cria dois arrays temporários
L[]
eR[]
para manter cópias dos subarrays que serão mesclados. - Copia os dados para os arrays temporários e depois mescla os arrays temporários de volta no array
arr[l..r]
de forma ordenada.
Função mergeSort
- Ordena um subarray
arr[l..r]
usando o algoritmo Merge Sort. - Se
l < r
(o subarray tem mais de um elemento), encontra o ponto médiom
. - Chama-se recursivamente para ordenar as duas metades:
arr[l..m]
earr[m+1..r]
. - Após ordenar as metades, utiliza a função
merge
para mesclar as duas metades ordenadas.
Função main
- Define um array
arr
com valores não ordenados. - Calcula o tamanho do array
arr_size
. - Chama a função
mergeSort
para ordenar o array inteiroarr[0..arr_size-1]
. - Imprime o array ordenado.
Em resumo, este código demonstra como aplicar o algoritmo Merge Sort para ordenar um array de inteiros em C. O algoritmo Divide e Conquista é exemplificado aqui pela divisão do problema de ordenação em problemas menores (ordenando subarrays menores) e pela combinação de suas soluções (mesclando subarrays ordenados) para resolver o problema maior (ordenar o array inteiro).
Implementação em Python
def mergeSort(arr):
if len(arr) > 1:
mid = len(arr)//2 # Encontra o ponto médio do array
L = arr[:mid] # Dividindo a parte esquerda
R = arr[mid:] # Dividindo a parte direita
mergeSort(L) # Ordenando a primeira metade
mergeSort(R) # Ordenando a segunda metade
i = j = k = 0
# Mescla os arrays temporários de volta para arr[]
while i < len(L) and j < len(R):
if L[i] < R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
# Verifica se algum elemento foi deixado
while i < len(L):
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
# Teste da função
arr = [12, 11, 13, 5, 6, 7]
mergeSort(arr)
print("Sorted array is:", arr)
Este código define uma função mergeSort
que ordena um array (arr
) em ordem crescente usando o algoritmo Merge Sort. O algoritmo divide o array em dois subarrays, ordena esses subarrays (recursivamente) e, em seguida, mescla os subarrays ordenados.
- Divisão do Array: Divide o array dado pela metade até que subarrays de um único elemento sejam alcançados.
- Ordenação e Mesclagem: Ordena e mescla esses subarrays para formar um array ordenado, comparando os elementos dos subarrays e os colocando na posição correta no array original.
Ao final, quando você executa a função mergeSort
com o array de exemplo e imprime o array ordenado, o output será:
Sorted array is: [5, 6, 7, 11, 12, 13]
Complexidade de Tempo e Espaço do Merge Sort
- Complexidade de Espaço
Espaço Auxiliar: O(n) Ordenação In Loco: Não Algoritmo: Dividir para Conquistar - Complexidade de Tempo
O Merge Sort é um algoritmo recursivo e sua complexidade de tempo pode ser expressa pela seguinte relação de recorrência: T(n) = 2T(n/2) + O(n). A solução da recorrência acima é O(nLogn). A lista de tamanho N é dividida em no máximo Logn partes, e a mesclagem de todas as sub-listas em uma única lista leva O(N) de tempo, portanto, o tempo de execução no pior caso deste algoritmo é O(nLogn).
- Complexidade de Tempo no Melhor Caso: O(n*log n)
- Complexidade de Tempo no Pior Caso: O(n*log n)
- Complexidade de Tempo Médio: O(n*log n)
A complexidade de tempo do MergeSort é O(n*Log n) nos 3 casos (pior, médio e melhor), pois o mergesort sempre divide o array em duas metades e leva um tempo linear para mesclar duas metades.
Resumo
Até agora, discutimos vários aspectos do algoritmo de ordenação Merge Sort, incluindo implementações em diferentes linguagens de programação, sua complexidade de tempo e espaço, e oferecemos uma análise sobre a eficácia do algoritmo. Aqui está um resumo dos pontos principais:
Implementações do Merge Sort
- Java, C, e Python: Fornecemos implementações do algoritmo Merge Sort nessas três linguagens de programação, destacando a abordagem de dividir para conquistar que caracteriza o algoritmo. Cada implementação segue a estrutura básica de dividir o array em subarrays até que cada um contenha um único elemento, e então mesclar esses subarrays de volta de forma ordenada.
Complexidade de Tempo e Espaço
- Espaço Auxiliar O(n): O Merge Sort requer espaço adicional proporcional ao tamanho do array para armazenar elementos temporariamente durante a mesclagem, o que resulta em uma complexidade de espaço O(n).
- Não é uma Ordenação In Loco: O algoritmo não ordena os elementos diretamente no array original sem usar espaço extra significativo.
- Complexidade de Tempo O(nLogn): Independentemente do caso (melhor, médio, ou pior), a complexidade de tempo do Merge Sort é O(nLogn), o que é derivado da sua natureza recursiva de dividir o array pela metade em cada etapa e levar um tempo linear para mesclar as duas metades.
Análise
- A análise sobre o Merge Sort confirma sua eficiência e confiabilidade, especialmente para conjuntos de dados grandes, devido à sua consistente complexidade de tempo O(nLogn) em todos os casos. Sua abordagem de dividir para conquistar permite que ele lide eficientemente com grandes volumes de dados, tornando-o uma escolha popular para ordenação.
Conclusão
- O Merge Sort é um algoritmo fundamental em ciência da computação, conhecido por sua eficiência, confiabilidade e complexidade de tempo previsível. Apesar de requerer espaço extra, suas vantagens em termos de performance o tornam uma escolha valiosa para a ordenação de arrays em muitas aplicações práticas e teóricas.