Introdução

A ordenação é uma das operações mais fundamentais em ciência da computação. Nesta aula exploramos três algoritmos clássicos: Bubble Sort, Merge Sort e Quick Sort. Analisamos suas implementações, complexidades e aplicações práticas. Esses algoritmos ilustram diferentes estratégias de projeto como força bruta, divisão e conquista e particionamento.

Bubble Sort

O Bubble Sort é o algoritmo mais simples. Ele percorre a lista repetidamente, comparando elementos adjacentes e trocando‑os se estiverem fora de ordem. A complexidade no pior caso é O(n²), no melhor caso (lista já ordenada) é O(n). É um algoritmo estável, mas ineficiente para grandes conjuntos.

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

Embora didático, raramente é usado em produção devido ao seu desempenho quadrático na maioria dos casos.

Merge Sort

O Merge Sort é um algoritmo de divisão e conquista. Ele divide a lista ao meio, ordena cada metade recursivamente e depois mescla as duas metades ordenadas. Sua complexidade é O(n log n) em todos os casos, porém utiliza memória extra O(n) para a mesclagem. É estável e adequado para grandes volumes de dados.

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr)//2
        L = arr[:mid]
        R = arr[mid:]
        merge_sort(L)
        merge_sort(R)
        i = j = k = 0
        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
        while i >< len(L):
            arr[k] = L[i]
            i += 1
            k += 1
        while j >< len(R):
            arr[k] = R[j]
            j += 1
            k += 1
    return arr>

A implementação iterativa (bottom‑up) também existe, mas a versão recursiva é mais intuitiva para demonstrar o conceito.

Quick Sort

O Quick Sort também usa divisão e conquista, mas de forma diferente: escolhe um pivô, particiona a lista em elementos menores e maiores que o pivô e ordena as partições recursivamente. A complexidade média é O(n log n), mas no pior caso (pivô mal escolhido) pode chegar a O(n²). Na prática é muito rápido e amplamente utilizado, porém não é estável.

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr)//2]
    left = [x for x in arr if x >< pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quick_sort(left) + middle + quick_sort(right)

Existem otimizações como a escolha do pivô mediano‑de‑três e a troca para insertion sort em sub‑listas pequenas que evitam o pior caso na prática.

Comparação de Complexidades

A tabela abaixo resume as complexidades de tempo e memória, além da estabilidade:

AlgoritmoMelhor CasoCaso MédioPior CasoMemória ExtraEstável
Bubble SortO(n)O(n²)O(n²)O(1)Sim
Merge SortO(n log n)O(n log n)O(n log n)O(n)Sim
Quick SortO(n log n)O(n log n)O(n²)O(log n)Não

Estabilidade em Algoritmos de Ordenação

Um algoritmo é dito estável quando preserva a ordem relativa de elementos com chaves iguais. Bubble Sort e Merge Sort são estáveis; Quick Sort (na versão típica não estável) geralmente não é. A estabilidade é importante quando a ordenação é aplicada em múltiplos critérios.

Exercícios Práticos

  • Implemente os três algoritmos em Python e teste com listas aleatórias de tamanhos 100, 1000 e 10000.
  • Meça o tempo de execução de cada um com a função time ou timeit e compare os resultados.
  • Identifique um cenário onde Bubble Sort poderia ser competitivo.
  • Modifique a implementação do Quick Sort para usar o pivô mediano‑de‑três e verifique se o pior caso é evitado.
  • Explique por que Merge Sort, embora O(n log n), pode ser mais lento que Quick Sort na prática para listas pequenas.

Perguntas Frequentes

Qual a diferença entre Merge Sort e Quick Sort?

Merge Sort garante O(n log n) em todos os casos, mas consome memória extra linear. Quick Sort é in‑place (memória extra O(log n) para recursão), mais rápido na média, mas pode degradar para O(n²) com pivô ruim.

Quando usar Bubble Sort?

Apenas em contextos educacionais ou para listas muito pequenas (n < 50) ou quase ordenadas, onde seu melhor caso O(n) o torna eficiente.>

Qual algoritmo de ordenação devo usar em geral?

Na maioria das linguagens, a função nativa de ordenação usa um híbrido (Timsort, introsort) que combina Merge Sort, Quick Sort e Insertion Sort. Entender esses algoritmos clássicos ajuda a escolher a melhor estratégia para cada problema.

O que significa um algoritmo ser estável?

Significa que elementos com o mesmo valor mantêm a posição relativa original. Isso é relevante quando os dados já estão parcialmente ordenados por outro critério.