Ciência da computação

Guia completo de heaps e filas de prioridade: como criar árvores com arrays

O heap de estrutura de dados só tem o mesmo nome que o heap de memória. Reunimos as regras para extrair o menor valor rapidamente sem ordenar tudo, o cálculo de índices para representar árvores com arrays e o papel prático das filas de prioridade.

5 min de leitura
Imagem de capa de Guia completo de heaps e filas de prioridade: como criar árvores com arrays

Este é o segundo artigo da série de estruturas de dados anunciada no fim do texto sobre pilhas e filas. O protagonista é o heap, mas antes precisamos esclarecer um mal-entendido.

No artigo relacionado Guia completo de pilhas e filas: por que o Swift não tem um tipo Stack (incluindo a armadilha do removeFirst), você pode conferir os conceitos básicos e os casos de aplicação relacionados.

**O heap de estrutura de dados só compartilha o nome com o heap de memória; não há relação entre eles.**A área de heap da memória é um espaço de alocação dinâmica; o heap deste artigo é «uma árvore para extrair o menor (ou maior) valor em O(1)».

A diferença entre as áreas de alocação de memória é explicada separadamente em Pilha vs heap. Aqui, o foco é apenas Heap e Priority Queue como estruturas de dados.

Se você esperava o heap do artigo sobre pilha vs heap, este é um mundo completamente diferente.

O problema que o heap resolve é claro.

«Quero processar primeiro o que é mais urgente, mas ordenar tudo antes seria desperdício.»


Ordenar é exagero

Imagine uma fila que remove primeiro a tarefa de maior prioridade. Ordenar o array toda vez custa O(n log n) a cada inserção.

Mesmo mantendo a ordem e procurando a posição de inserção, mover elementos custa O(n).

Pensando bem, não precisamos que tudo esteja ordenado. Basta saber imediatamente qual é o item mais urgente.

Podemos descobrir quem está em segundo lugar depois que o primeiro sair, sem problema.

É exatamente isso que o heap promete. Por isso, tanto a inserção quanto a remoção terminam em O(log n).

«Quanto menos você promete, mais rápido fica» é um trade-off clássico no projeto de estruturas de dados.


Regra do heap: o pai é menor que os filhos; só isso

Um heap mínimo é uma árvore binária completa com uma única regra.

Todo pai é menor ou igual aos próprios filhos.

Não importa qual irmão é maior. A subárvore esquerda também não precisa ser menor que a direita.

Basta respeitar a relação entre pai e filho. Graças a essa flexibilidade, a raiz sempre contém o menor valor global, mas nada além disso é garantido.

Inserção (sift up): adiciona no fim da árvore e, se for menor que o pai, troca e sobe. Como percorre apenas a altura da árvore, custa O(log n).

Extração do menor valor (sift down): remove a raiz, leva o último elemento para ela e desce trocando-o com o menor dos dois filhos. Também custa O(log n).

Diagrama da correspondência entre os índices da árvore de heap mínimo e sua representação em array
É uma árvore, mas não tem nenhum ponteiro

A mágica de criar uma árvore com um array

É aqui que a implementação do heap fica elegante. Como a árvore binária completa é preenchida da esquerda para a direita, podemos colocá-la em ordem no array sem ponteiros.(NIST Heap).

Índice:  0   1   2   3   4   5
Valor:     [1,  3,  2,  7,  4,  5]

Os relacionamentos familiares aparecem apenas com o cálculo dos índices.

  • Pai: (i - 1) / 2
  • Filho esquerdo: 2i + 1, filho direito: 2i + 2

Não há objetos de nó, ponteiros para filhos nem alocação de memória no heap. Aproveitamos a localidade de cache discutida em array vs lista encadeada e usamos apenas a estrutura lógica da árvore.

Em Swift, o esqueleto fica assim.

struct MinHeap<Element: Comparable> {
    private var elements: [Element] = []

    var min: Element? { elements.first }   // O(1)

    mutating func insert(_ value: Element) {  // O(log n)
        elements.append(value)
        siftUp(from: elements.count - 1)
    }

    mutating func removeMin() -> Element? {   // O(log n)
        guard !elements.isEmpty else { return nil }
        elements.swapAt(0, elements.count - 1)
        let min = elements.removeLast()
        siftDown(from: 0)
        return min
    }
}

siftUp/siftDown são as trocas repetidas explicadas acima.

Por sinal, transformar um array existente inteiro em heap com build-heap não é O(n log n) por inserir cada elemento. Fazendo sift down a partir da metade inferior, termina em O(n) — um detalhe comum em entrevistas.


O lugar do heap na prática

**Uma fila de prioridade é um heap.**Usando a expressão do artigo sobre pilhas e filas, a fila remove por ordem de chegada, a fila de prioridade por urgência, e o heap é sua implementação padrão (NIST Priority Queue).

  • Escalonador do SO: atribui a CPU primeiro aos processos de maior prioridade
  • Menor caminho de Dijkstra: algoritmo que remove repetidamente «o caminho mais curto encontrado até agora»; sem heap, o desempenho desaba
  • Gerenciamento de timers: estrutura interna que só precisa saber qual timer, entre muitos, tocará primeiro
  • Heapsort: inserir e remover tudo produz uma ordenação O(n log n). A vantagem é ordenar in-place sem memória extra
  • Problema Top-K: «manter os K maiores de um fluxo» — resolvido em O(n log K) com um heap mínimo de tamanho K. É um padrão frequente em testes de programação

A biblioteca padrão do Swift não tem heap. Assim como o Deque do artigo sobre pilhas e filas, o pacote da Apple swift-collections fornece Heap (swift-collections Heap).

Esta implementação de min-max heap oferece min e max em O(log n). Em ambientes sem pacotes externos, como testes de programação, é prático dominar o esqueleto MinHeap acima.

Ilustração de uma fila de prioridade que atende pacientes urgentes primeiro, como na triagem de um pronto-socorro
A chamada segue a urgência, não a ordem da fila

Resumo

  • O heap de estrutura de dados só tem o mesmo nome que o heap de memória — é «uma árvore que extrai o menor valor instantaneamente»
  • A única regra é pai ≤ filho — abrir mão da ordenação total reduz inserção e remoção a O(log n)
  • Por ser uma árvore binária completa, pode sercolocada em um array sem ponteiros — pai (i-1)/2, filhos 2i+1, 2i+2
  • O heap é a implementação padrão da fila de prioridade; Dijkstra, escalonadores e Top-K são usos representativos
  • No Swift, swift-collections oferece Heap; em testes de programação, implementar diretamente é o padrão

A próxima parte será sobre tabelas hash: como o Swift Dictionary obtém O(1) e o verdadeiro significado do que Hashable exige.

Fontes e verificação

  • swift-collections HeapApple · Documentação oficial · Consultado 17 de agosto de 2026Evidência: API Heap do Swift e comportamento de heaps mínimo e máximo
  • NIST Dictionary of Algorithms: Priority QueueNIST · Dados oficiais · Consultado 17 de agosto de 2026Evidência: Definição do tipo abstrato de dados de uma fila de prioridade
  • NIST Dictionary of Algorithms: HeapNIST · Dados oficiais · Consultado 17 de agosto de 2026Evidência: Estrutura e características operacionais de um heap baseado em arrays