Heap Binário em Zig — Implementação Completa
O heap binário é uma árvore binária completa armazenada em array, onde cada pai tem valor menor (min-heap) ou maior (max-heap) que seus filhos. É a base para filas de prioridade e heap sort.
Conceito
Min-Heap (raiz = menor):
1
/ \
3 5
/ \ / \
7 9 8 10
Armazenado como array:
Índice: 0 1 2 3 4 5 6
Valor: [1, 3, 5, 7, 9, 8, 10]
Relações (índice baseado em 0):
Pai de i: (i - 1) / 2
Filho esq de i: 2*i + 1
Filho dir de i: 2*i + 2
Inserir 2:
Adiciona no final: [1, 3, 5, 7, 9, 8, 10, 2]
Sobe (bubble up): 2 < 7, troca → [1, 3, 5, 2, 9, 8, 10, 7]
2 < 3, troca → [1, 2, 5, 3, 9, 8, 10, 7]
2 > 1, para → [1, 2, 5, 3, 9, 8, 10, 7]
Remover mínimo (1):
Troca raiz com último: [7, 2, 5, 3, 9, 8, 10]
Desce (bubble down): 7 > 2, troca → [2, 7, 5, 3, 9, 8, 10]
7 > 3, troca → [2, 3, 5, 7, 9, 8, 10]
Implementação em Zig
const std = @import("std");
const Allocator = std.mem.Allocator;
/// Heap binário genérico (min-heap ou max-heap via comparador).
pub fn HeapBinario(comptime T: type) type {
return struct {
const Self = @This();
items: std.ArrayList(T),
comparador: *const fn (T, T) bool,
/// Cria um min-heap ou max-heap.
pub fn init(allocator: Allocator, comparador: *const fn (T, T) bool) Self {
return .{
.items = std.ArrayList(T).init(allocator),
.comparador = comparador,
};
}
pub fn deinit(self: *Self) void {
self.items.deinit();
}
/// Insere um elemento — O(log n).
pub fn inserir(self: *Self, valor: T) !void {
try self.items.append(valor);
self.subirHeap(self.items.items.len - 1);
}
/// Remove e retorna o topo (mínimo ou máximo) — O(log n).
pub fn removerTopo(self: *Self) ?T {
if (self.items.items.len == 0) return null;
const topo = self.items.items[0];
const ultimo = self.items.items.len - 1;
self.items.items[0] = self.items.items[ultimo];
_ = self.items.pop();
if (self.items.items.len > 0) self.descerHeap(0);
return topo;
}
/// Consulta o topo sem remover — O(1).
pub fn topo(self: *const Self) ?T {
if (self.items.items.len == 0) return null;
return self.items.items[0];
}
pub fn tamanho(self: *const Self) usize {
return self.items.items.len;
}
pub fn vazio(self: *const Self) bool {
return self.items.items.len == 0;
}
/// Constrói heap a partir de array existente — O(n).
pub fn heapify(self: *Self, dados: []const T) !void {
self.items.clearRetainingCapacity();
try self.items.appendSlice(dados);
// Bottom-up heapify
var i: usize = self.items.items.len / 2;
while (i > 0) {
i -= 1;
self.descerHeap(i);
}
}
fn subirHeap(self: *Self, idx: usize) void {
var i = idx;
while (i > 0) {
const pai = (i - 1) / 2;
if (self.comparador(self.items.items[i], self.items.items[pai])) {
std.mem.swap(T, &self.items.items[i], &self.items.items[pai]);
i = pai;
} else break;
}
}
fn descerHeap(self: *Self, idx: usize) void {
var i = idx;
const n = self.items.items.len;
while (true) {
var melhor = i;
const esq = 2 * i + 1;
const dir = 2 * i + 2;
if (esq < n and self.comparador(self.items.items[esq], self.items.items[melhor]))
melhor = esq;
if (dir < n and self.comparador(self.items.items[dir], self.items.items[melhor]))
melhor = dir;
if (melhor == i) break;
std.mem.swap(T, &self.items.items[i], &self.items.items[melhor]);
i = melhor;
}
}
};
}
fn menorQue(a: i32, b: i32) bool {
return a < b;
}
fn maiorQue(a: i32, b: i32) bool {
return a > b;
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Min-Heap
var min_heap = HeapBinario(i32).init(allocator, &menorQue);
defer min_heap.deinit();
for ([_]i32{ 5, 3, 8, 1, 9, 2, 7 }) |v| {
try min_heap.inserir(v);
}
try stdout.print("Min-Heap (ordem de saída): ", .{});
while (min_heap.removerTopo()) |v| {
try stdout.print("{d} ", .{v});
}
try stdout.print("\n", .{});
// Max-Heap via heapify
var max_heap = HeapBinario(i32).init(allocator, &maiorQue);
defer max_heap.deinit();
try max_heap.heapify(&[_]i32{ 5, 3, 8, 1, 9, 2, 7 });
try stdout.print("Max-Heap (ordem de saída): ", .{});
while (max_heap.removerTopo()) |v| {
try stdout.print("{d} ", .{v});
}
try stdout.print("\n", .{});
}
Análise de Complexidade
| Operação | Tempo | Espaço |
|---|---|---|
| Inserir | O(log n) | O(1) |
| Remover topo | O(log n) | O(1) |
| Consultar topo | O(1) | O(1) |
| Heapify (build) | O(n) | O(1) |
Recursos Relacionados
- Heap Sort — Ordenação usando heap
- Fila — Fila de prioridade
- Árvore Binária — Conceito base
- Dijkstra — Usa fila de prioridade