Implementação e Análise do Merge Sort para Ordenação e Contagem de Inversões

Ordenação por Mesclagem (Merge Sort)

O algoritmo de ordenação por mesclagem é uma aplicação clássica da estratégia divide-and-conquer. Sua estrutura recursiva particiona repetidamente o conjunto de dados até alcançar escalares individuais, para então recombiná-los progressivamante em estruturas ordenadas. Esse fluxo garante consistência temporal previsível, independentemente da configuração inicial dos elementos.

Mecanismo de Fusão

Durante a junção de dois subintervalos previamente ordenados, posicionam-se indicadores no início de cada segmento. O menor valor é transferido para um buffer temporário e seu respectivo ponteiro avança. O ciclo se repete até que um dos lados seja consumido totalmente, momento em que os remanescentes são copiados integralmente. Ao retornar das chamadas recursivas, o arranjo original é sobrescrito pelos dados consolidados.

Implementação em C++

#include <vector>

void merge(std::vector<int>& auxiliary, std::vector<int>& target, int lower, int upper) {
    int pivot = lower + (upper - lower) / 2;
    int leftPtr = lower, rightPtr = pivot + 1, copyIdx = lower;

    while (leftPtr <= pivot && rightPtr <= upper) {
        if (target[leftPtr] <= target[rightPtr]) {
            auxiliary[copyIdx++] = target[leftPtr++];
        } else {
            auxiliary[copyIdx++] = target[rightPtr++];
        }
    }

    while (leftPtr <= pivot) auxiliary[copyIdx++] = target[leftPtr++];
    while (rightPtr <= upper) auxiliary[copyIdx++] = target[rightPtr++];

    for (int i = lower; i <= upper; ++i) {
        target[i] = auxiliary[i];
    }
}

void executeMergeSort(std::vector<int>& auxiliary, std::vector<int>& target, int lower, int upper) {
    if (lower >= upper) return;

    int pivot = lower + (upper - lower) / 2;
    executeMergeSort(auxiliary, target, lower, pivot);
    executeMergeSort(auxiliary, target, pivot + 1, upper);
    merge(auxiliary, target, lower, upper);
}

Detecção de Pares Invertidos

Um par invertido surge quando um elemento posicionado antes de outro apresenta valor superior (i < j entretanto arr[i] > arr[j]). Utilizar verificações duplas resulta em complexidade quadrática, tornando a abordagem inviável para conjuntos extensos. O Merge Sort pode ser estendido para rastrear essas ocorrências naturalmente durante a etapa de fusão.

Quando o indicador do lado direito avança por conter um valor inferior ao corrente do lado esquerdo, conclui-se que esse valor direito é menor que todos os elementos ainda não processados na metade esquerda. A contagem aumenta exatamente em pivot - leftPtr + 1, agregando múltiplas inversões em uma única operação constante.

Código Aprimorado para Acumulação de Inversões

#include <vector>

void countInversions(std::vector<int>& auxiliary, std::vector<int>& data, int lower, int upper, long long& inversionCounter) {
    if (lower >= upper) return;

    int pivot = lower + (upper - lower) / 2;
    countInversions(auxiliary, data, lower, pivot, inversionCounter);
    countInversions(auxiliary, data, pivot + 1, upper, inversionCounter);

    int leftPtr = lower, rightPtr = pivot + 1, currentPos = lower;

    while (leftPtr <= pivot && rightPtr <= upper) {
        if (data[leftPtr] <= data[rightPtr]) {
            auxiliary[currentPos++] = data[leftPtr++];
        } else {
            // Valor da direita menor que todos os restantes da esquerda
            inversionCounter += (pivot - leftPtr + 1);
            auxiliary[currentPos++] = data[rightPtr++];
        }
    }

    while (leftPtr <= pivot) auxiliary[currentPos++] = data[leftPtr++];
    while (rightPtr <= upper) auxiliary[currentPos++] = data[rightPtr++];

    for (int idx = lower; idx <= upper; ++idx) {
        data[idx] = auxiliary[idx];
    }
}

Comparativo Técnico entre Métodos de Ordenação

Estabilidade

A estabilidade preserva a ordem relativa de chaves equivalentes. Algoritmos estáveis: Insertion, Bubble, Merge e Radix/Bucket. Algoritmos não estáveis: Selection, Shell, Quick e Heap. Aplicações que demandam ordenação multidimensional frequentemente exigem estabilidade para garantir regras secundárias.

Complexidade Temporal e de Memória Auxiliar

  • O(n²): Insertion, Bubble, Selection.
  • O(n log n): Quick, Heap, Merge.
  • O(n): Bucket/Radix (limitado a domínios conhecidos).
  • Espaço Extra: Merge e Bucket necessitam O(n). Quick ocupa O(log n) em média (pior caso O(n)). Ensertion e Heap operam in-place com O(1).

Fatores de Escolha

  • Sequências parcial ou totalmente organizadas beneficiam diretamente Insertion e Bubble, enquanto Quick pode apresentar lentidão.
  • Para pequenos volumes sem restrição de estabilidade, Selection ou Insertion oferecem implementação direta e baixo overhead.
  • Amplitude finita e memória abundante justificam o uso de Bucket sort para ganho linear.
  • Grandes datasets distribuídos aleatoriamente, sem exigência de estabilidade, têm no Quick Sort a opção preferencial em benchmarks práticos.
  • Restrições rigorosas de memória combinadas com garantia O(n log n) no pior cenário apontam para Heap Sort.

Problema de Fixação

Enunciado

Solicite três números inteiros e apresente-os dispostos em ordem crescente, separados por espaços. A resolução deve empregar manualmente as estratégias discutidas, abstendo-se de funções prontas de bibliotecas padrão.

Especificação de Entrada

Três valores inteiros consecutivos na mesma linha.

Especificação de Saída

Sequência ordenada seguida de uma nova linha.

Referência Operacional

Entrada:
7 -4 12
Saída:
-4 7 12

Tags: C++ merge-sort complexidade-temporal algoritmos-de-ordenação pares-invertidos

Publicado em 10-22 23:11