Ordenação é um dos problemas mais estudados da ciência da computação — e por boa razão. Dados ordenados permitem busca binária, facilitam visualização, e são pré-requisito para dezenas de algoritmos. Conhecer as diferenças entre algoritmos de ordenação é tanto um conhecimento fundamental quanto um tema quase garantido em entrevistas técnicas. Este artigo implementa e explica os três algoritmos de ordenação mais estudados, com comparação de performance para você saber quando usar cada um.
Bubble Sort: o mais simples, o mais lento
Bubble Sort compara elementos adjacentes e os troca se estiverem na ordem errada, repetindo o processo até que nenhuma troca seja necessária. O maior elemento “borbulha” para o final em cada passagem.
def bubble_sort(arr):
n = len(arr)
for i in range(n):
trocou = False
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
trocou = True
if not trocou: # Otimização: para cedo se já ordenado
break
return arr
print(bubble_sort([64, 34, 25, 12, 22, 11, 90]))
# Saída: [11, 12, 22, 25, 34, 64, 90]
Complexidade: O(n²) no pior caso, O(n) no melhor caso (lista já ordenada com a otimização). Uso na prática: quase nunca — é educacional para aprender o conceito de ordenação por comparação, mas nunca usado em produção por ser lento.
Merge Sort: dividir para conquistar
Merge Sort divide a lista ao meio recursivamente até chegar em sublistas de 1 elemento (que são sempre ordenadas), e então as mescla dois a dois de forma ordenada. É o clássico exemplo de algoritmo “dividir para conquistar”.
def merge_sort(arr):
if len(arr) <= 1:
return arr
meio = len(arr) // 2
esquerda = merge_sort(arr[:meio])
direita = merge_sort(arr[meio:])
return merge(esquerda, direita)
def merge(esq, dir):
resultado = []
i = j = 0
while i < len(esq) and j < len(dir):
if esq[i] <= dir[j]:
resultado.append(esq[i])
i += 1
else:
resultado.append(dir[j])
j += 1
resultado.extend(esq[i:])
resultado.extend(dir[j:])
return resultado
print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
# Saída: [3, 9, 10, 27, 38, 43, 82]
Complexidade: O(n log n) em todos os casos (melhor, médio e pior). Uso na prática: quando estabilidade é necessária (manter a ordem relativa de elementos iguais) e quando se ordena listas ligadas (sem acesso aleatório, o Merge Sort é superior ao Quick Sort).
Quick Sort: o mais rápido na prática
Quick Sort escolhe um “pivô”, reorganiza o array de forma que elementos menores que o pivô fiquem à esquerda e maiores à direita, e recursivamente aplica o mesmo processo nas duas metades.
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivo = arr[len(arr) // 2] # Escolhe o elemento do meio como pivô
menores = [x for x in arr if x pivo]
return quick_sort(menores) + iguais + quick_sort(maiores)
print(quick_sort([3, 6, 8, 10, 1, 2, 1]))
# Saída: [1, 1, 2, 3, 6, 8, 10]
Complexidade: O(n log n) em média, O(n²) no pior caso (pivô sempre o menor ou maior elemento). Uso na prática: é o algoritmo de ordenação mais usado em implementações de bibliotecas padrão (a função sort() do Python usa Timsort, que combina Merge Sort e Insertion Sort; o sort do C++ usa Introsort, que combina Quick Sort, Heap Sort e Insertion Sort). Quick Sort é geralmente mais rápido na prática por ter constante menor e melhor comportamento de cache.
Qual usar? Comparação prática
Na quase totalidade dos casos práticos, use a função sort() nativa da sua linguagem — ela é implementada por especialistas e é ótima para o caso geral. Entender Merge Sort e Quick Sort importa para: entrevistas técnicas, situações onde você precisa de propriedades específicas (estabilidade, comportamento de pior caso garantido), e para entender como as funções nativas funcionam por baixo dos panos. Bubble Sort existe apenas para aprendizado — nunca use em código de produção.
Tem um projeto em mente?
Somos especialistas em transformar ideias em produtos digitais. Apps, sites, automações e IA — vamos construir juntos.