6-3 斐波那契堆

斐波那契堆(Fibonacci Heap)

斐波那契堆是一种可合并堆,由一组最小堆有序的树组成。它的核心思想是"延迟合并"(lazy consolidation)——插入时只把节点加入根链表,不做任何整理,直到提取最小值时才统一合并相同度数的树。这使得插入、减小键值、合并操作的摊还时间均为 O(1)。

之所以叫"斐波那契堆",是因为堆中树的大小与斐波那契数列相关,这个关系保证了 extractMin 的摊还复杂度为 O(log n)。

斐波那契堆的结构:

  • 所有树的根通过循环双向链表(circular doubly linked list)连接。
  • 每个节点的子节点也通过循环双向链表连接。
  • 维护一个 min 指针指向最小键值的根节点。
  • 节点有 marked 标记:如果一个非根节点失去了子节点(不是因为 extractMin),就标记它。
min → [3] ⇄ [7] ⇄ [5] ⇄ (back to [3])   ← 根链表(循环双向)
        |           |
       [8]        [10] ⇄ [12]             ← 子链表
                    |
                   [15]

节点定义

C++ 实现

struct FibNode 
{
    int key;
    int degree;          // number of children
    bool marked;         // whether node has lost a child
    FibNode *parent;
    FibNode *child;      // any one child
    FibNode *left;       // circular list siblings
    FibNode *right;

    FibNode(int k) : key(k), degree(0), marked(false),
                     parent(nullptr), child(nullptr) 
    {
        left = right = this;  // point to self
    }
};

C 实现

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>

typedef struct FibNode 
{
    int key;
    int degree;
    bool marked;
    struct FibNode *parent;
    struct FibNode *child;
    struct FibNode *left;
    struct FibNode *right;
} FibNode;

FibNode* createFibNode(int key) 
{
    FibNode* node = (FibNode*)malloc(sizeof(FibNode));
    node->key = key;
    node->degree = 0;
    node->marked = false;
    node->parent = NULL;
    node->child = NULL;
    node->left = node;
    node->right = node;
    return node;
}

Python 实现

class FibNode:
    def __init__(self, key):
        self.key = key
        self.degree = 0
        self.marked = False
        self.parent = None
        self.child = None
        self.left = self    # circular list
        self.right = self

Go 实现

type FibNode struct {
    Key    int
    Degree int
    Marked bool
    Parent *FibNode
    Child  *FibNode
    Left   *FibNode
    Right  *FibNode
}

func newFibNode(key int) *FibNode {
    node := &FibNode{
        Key:    key,
        Degree: 0,
        Marked: false,
        Parent: nil,
        Child:  nil,
    }
    node.Left = node  // circular list
    node.Right = node
    return node
}

节点使用循环双向链表连接兄弟节点,LeftRight 初始化时指向自身。Degree 记录子节点数量,Marked 标记该节点是否失去过子节点。


斐波那契堆的结构定义

C++ 实现

class FibonacciHeap 
{
private:
    FibNode* minNode;
    int nodeCount;

    // add node to circular doubly linked list
    void addToRootList(FibNode* node) 
    {
        if (minNode == nullptr) 
        {
            minNode = node;
            node->left = node->right = node;
        } 
        else 
        {
            node->right = minNode->right;
            node->left = minNode;
            minNode->right->left = node;
            minNode->right = node;
        }
    }

    // remove node from its circular list
    void removeFromList(FibNode* node) 
    {
        node->left->right = node->right;
        node->right->left = node->left;
    }

    // link tree y under x (y becomes child of x)
    void link(FibNode* y, FibNode* x) 
    {
        removeFromList(y);
        y->parent = x;

        if (x->child == nullptr) 
        {
            x->child = y;
            y->left = y->right = y;
        } 
        else 
        {
            y->right = x->child->right;
            y->left = x->child;
            x->child->right->left = y;
            x->child->right = y;
        }
        x->degree++;
        y->marked = false;
    }

    // consolidate trees after extractMin
    void consolidate();

    // cut node from parent, add to root list
    void cut(FibNode* x, FibNode* y) 
    {
        // remove x from y's child list
        if (y->child == x) 
        {
            if (x->right == x) 
            {
                y->child = nullptr;
            } 
            else 
            {
                y->child = x->right;
            }
        }
        removeFromList(x);
        y->degree--;

        // add x to root list
        addToRootList(x);
        x->parent = nullptr;
        x->marked = false;
    }

    void cascadeCut(FibNode* node) 
    {
        FibNode* p = node->parent;
        if (p != nullptr) 
        {
            if (!node->marked) 
            {
                node->marked = true;
            } 
            else 
            {
                cut(node, p);
                cascadeCut(p);
            }
        }
    }

public:
    FibonacciHeap() : minNode(nullptr), nodeCount(0) {}

    void insert(int key);
    int findMin();
    int extractMin();
    void decreaseKey(FibNode* node, int newKey);
    static FibonacciHeap merge(FibonacciHeap& h1, FibonacciHeap& h2);
    bool isEmpty() { return minNode == nullptr; }
};

C 实现

typedef struct FibonacciHeap 
{
    FibNode* minNode;
    int nodeCount;
} FibonacciHeap;

FibonacciHeap* createFibHeap() 
{
    FibonacciHeap* heap = (FibonacciHeap*)malloc(sizeof(FibonacciHeap));
    heap->minNode = NULL;
    heap->nodeCount = 0;
    return heap;
}

Python 实现

class FibonacciHeap:
    def __init__(self):
        self.min_node = None
        self.node_count = 0

    def is_empty(self):
        return self.min_node is None

    def _add_to_root_list(self, node):
        if self.min_node is None:
            self.min_node = node
            node.left = node.right = node
        else:
            node.right = self.min_node.right
            node.left = self.min_node
            self.min_node.right.left = node
            self.min_node.right = node

    def _remove_from_list(self, node):
        node.left.right = node.right
        node.right.left = node.left

    def _link(self, y, x):
        """Link tree y under x (y becomes child of x)."""
        self._remove_from_list(y)
        y.parent = x

        if x.child is None:
            x.child = y
            y.left = y.right = y
        else:
            y.right = x.child.right
            y.left = x.child
            x.child.right.left = y
            x.child.right = y

        x.degree += 1
        y.marked = False

Go 实现

type FibHeap struct {
    Min   *FibNode
    Count int
}

func newFibHeap() *FibHeap {
    return &FibHeap{Min: nil, Count: 0}
}

func (h *FibHeap) isEmpty() bool {
    return h.Min == nil
}

// addToRootList 将节点加入根链表(循环双向链表)
func (h *FibHeap) addToRootList(node *FibNode) {
    if h.Min == nil {
        h.Min = node
        node.Left = node
        node.Right = node
    } else {
        node.Right = h.Min.Right
        node.Left = h.Min
        h.Min.Right.Left = node
        h.Min.Right = node
    }
}

// removeFromList 将节点从其所在循环链表中移除
func removeFromList(node *FibNode) {
    node.Left.Right = node.Right
    node.Right.Left = node.Left
}

// link 将树 y 链接到 x 下面(y 成为 x 的子节点)
func (h *FibHeap) link(y, x *FibNode) {
    removeFromList(y)
    y.Parent = x

    if x.Child == nil {
        x.Child = y
        y.Left = y
        y.Right = y
    } else {
        y.Right = x.Child.Right
        y.Left = x.Child
        x.Child.Right.Left = y
        x.Child.Right = y
    }
    x.Degree++
    y.Marked = false
}

FibHeap 维护一个 Min 指针指向根链表中的最小节点。addToRootListremoveFromList 操作循环双向链表,link 将一棵树链接为另一棵树的子树并增加度数。


插入操作

插入操作非常简单:创建一个单节点,加入根链表,更新 min 指针。实际时间 O(1)。

C++ 实现

void FibonacciHeap::insert(int key) 
{
    FibNode* node = new FibNode(key);
    addToRootList(node);

    if (minNode == nullptr || node->key < minNode->key) 
    {
        minNode = node;
    }
    nodeCount++;
}

C 实现

void fibInsert(FibonacciHeap* heap, int key) 
{
    FibNode* node = createFibNode(key);

    if (heap->minNode == NULL) 
    {
        heap->minNode = node;
    } 
    else 
    {
        // add to root list
        node->right = heap->minNode->right;
        node->left = heap->minNode;
        heap->minNode->right->left = node;
        heap->minNode->right = node;

        if (key < heap->minNode->key) 
        {
            heap->minNode = node;
        }
    }
    heap->nodeCount++;
}

Python 实现

def insert(self, key):
    node = FibNode(key)
    self._add_to_root_list(node)

    if self.min_node is None or node.key < self.min_node.key:
        self.min_node = node
    self.node_count += 1

Go 实现

func (h *FibHeap) insert(key int) {
    node := newFibNode(key)
    h.addToRootList(node)

    if h.Min == nil || node.Key < h.Min.Key {
        h.Min = node
    }
    h.Count++
}

插入时创建新节点并加入根链表,如果新节点键值更小则更新 Min 指针。操作实际时间 O(1)。


查找最小值

直接返回 min 指针指向的节点。O(1)。

C++ 实现

int FibonacciHeap::findMin() 
{
    if (minNode == nullptr) 
    {
        std::cout << "Heap is empty\n";
        return -1;
    }
    return minNode->key;
}

C 实现

int fibFindMin(FibonacciHeap* heap) 
{
    if (heap->minNode == NULL) 
    {
        printf("Heap is empty\n");
        return -1;
    }
    return heap->minNode->key;
}

Python 实现

def find_min(self):
    if self.min_node is None:
        print("Heap is empty")
        return None
    return self.min_node.key

Go 实现

func (h *FibHeap) findMin() (int, bool) {
    if h.Min == nil {
        fmt.Println("Heap is empty")
        return 0, false
    }
    return h.Min.Key, true
}

直接返回 Min 指针指向节点的键值。第二个返回值 bool 表示堆是否为空。O(1) 时间。


合并操作(Union)

将两个斐波那契堆的根链表拼接在一起,更新 min 指针。O(1) 实际时间,不需要任何树的重构。

C++ 实现

FibonacciHeap FibonacciHeap::merge(FibonacciHeap& h1, FibonacciHeap& h2) 
{
    FibonacciHeap result;

    if (h1.minNode == nullptr) 
    {
        result.minNode = h2.minNode;
    } 
    else if (h2.minNode == nullptr) 
    {
        result.minNode = h1.minNode;
    } 
    else 
    {
        // concatenate root lists
        FibNode* h1Right = h1.minNode->right;
        FibNode* h2Right = h2.minNode->right;

        h1.minNode->right = h2Right;
        h2Right->left = h1.minNode;
        h2.minNode->right = h1Right;
        h1Right->left = h2.minNode;

        // set min
        if (h1.minNode->key < h2.minNode->key) 
        {
            result.minNode = h1.minNode;
        } 
        else 
        {
            result.minNode = h2.minNode;
        }
    }
    result.nodeCount = h1.nodeCount + h2.nodeCount;
    return result;
}

Python 实现

@staticmethod
def merge(h1, h2):
    result = FibonacciHeap()

    if h1.min_node is None:
        result.min_node = h2.min_node
    elif h2.min_node is None:
        result.min_node = h1.min_node
    else:
        # concatenate root lists
        h1_right = h1.min_node.right
        h2_right = h2.min_node.right

        h1.min_node.right = h2_right
        h2_right.left = h1.min_node
        h2.min_node.right = h1_right
        h1_right.left = h2.min_node

        if h1.min_node.key < h2.min_node.key:
            result.min_node = h1.min_node
        else:
            result.min_node = h2.min_node

    result.node_count = h1.node_count + h2.node_count
    return result

Go 实现

func merge(h1, h2 *FibHeap) *FibHeap {
    result := newFibHeap()

    if h1.Min == nil {
        result.Min = h2.Min
    } else if h2.Min == nil {
        result.Min = h1.Min
    } else {
        // 拼接两个根链表
        h1Right := h1.Min.Right
        h2Right := h2.Min.Right

        h1.Min.Right = h2Right
        h2Right.Left = h1.Min
        h2.Min.Right = h1Right
        h1Right.Left = h2.Min

        // 设置新的 Min 指针
        if h1.Min.Key < h2.Min.Key {
            result.Min = h1.Min
        } else {
            result.Min = h2.Min
        }
    }
    result.Count = h1.Count + h2.Count
    return result
}

合并操作通过拼接两个堆的根链表实现,无需重构任何树。更新 Min 指针为两个堆中较小者。O(1) 实际时间。


提取最小值(Extract Min)

这是最复杂的操作。步骤:

  1. 将 min 节点的所有子节点加入根链表。
  2. 从根链表中移除 min 节点。
  3. 合并(Consolidate):将根链表中所有度数相同的树合并,直到没有两棵树度数相同。
  4. 更新 min 指针。

合并过程类似二项式队列,摊还时间 O(log n)。

C++ 实现

void FibonacciHeap::consolidate() 
{
    // max degree <= log2(n), we use 2*log2(n)+1 as upper bound
    int maxDegree = 1;
    int temp = nodeCount;
    while (temp > 1) { temp >>= 1; maxDegree++; }

    std::vector<FibNode*> degreeTable(maxDegree + 1, nullptr);

    // collect all root nodes
    std::vector<FibNode*> roots;
    FibNode* current = minNode;
    if (current != nullptr) 
    {
        roots.push_back(current);
        current = current->right;
        while (current != minNode) 
        {
            roots.push_back(current);
            current = current->right;
        }
    }

    for (FibNode* root : roots) 
    {
        FibNode* x = root;
        int d = x->degree;

        while (degreeTable[d] != nullptr) 
        {
            FibNode* y = degreeTable[d];
            // ensure x has smaller key
            if (x->key > y->key) 
            {
                FibNode* temp = x;
                x = y;
                y = temp;
            }
            link(y, x);
            degreeTable[d] = nullptr;
            d++;
        }
        degreeTable[d] = x;
    }

    // rebuild root list and find new min
    minNode = nullptr;
    for (FibNode* node : degreeTable) 
    {
        if (node != nullptr) 
        {
            node->left = node->right = node;
            if (minNode == nullptr) 
            {
                minNode = node;
            } 
            else 
            {
                addToRootList(node);
                if (node->key < minNode->key) 
                {
                    minNode = node;
                }
            }
        }
    }
}

int FibonacciHeap::extractMin() 
{
    FibNode* z = minNode;
    if (z == nullptr) 
    {
        std::cout << "Heap is empty\n";
        return -1;
    }

    // add children to root list
    if (z->child != nullptr) 
    {
        FibNode* child = z->child;
        FibNode* start = child;
        do 
        {
            FibNode* next = child->right;
            addToRootList(child);
            child->parent = nullptr;
            child = next;
        } while (child != start);
    }

    // remove z from root list
    removeFromList(z);

    if (z == z->right) 
    {
        minNode = nullptr;
    } 
    else 
    {
        minNode = z->right;
        consolidate();
    }
    nodeCount--;

    int minKey = z->key;
    delete z;
    return minKey;
}

C 实现

void fibConsolidate(FibonacciHeap* heap) 
{
    int maxDegree = 1;
    int temp = heap->nodeCount;
    while (temp > 1) { temp >>= 1; maxDegree++; }

    FibNode** degreeTable = (FibNode**)calloc(maxDegree + 1, sizeof(FibNode*));

    // collect roots
    FibNode* roots[128];
    int rootCount = 0;

    if (heap->minNode != NULL) 
    {
        FibNode* cur = heap->minNode;
        roots[rootCount++] = cur;
        cur = cur->right;
        while (cur != heap->minNode) 
        {
            roots[rootCount++] = cur;
            cur = cur->right;
        }
    }

    for (int i = 0; i < rootCount; i++) 
    {
        FibNode* x = roots[i];
        int d = x->degree;

        while (degreeTable[d] != NULL) 
        {
            FibNode* y = degreeTable[d];
            if (x->key > y->key) 
            {
                FibNode* t = x; x = y; y = t;
            }
            // link y under x
            y->left->right = y->right;
            y->right->left = y->left;
            y->parent = x;

            if (x->child == NULL) 
            {
                x->child = y;
                y->left = y->right = y;
            } 
            else 
            {
                y->right = x->child->right;
                y->left = x->child;
                x->child->right->left = y;
                x->child->right = y;
            }
            x->degree++;
            y->marked = false;
            degreeTable[d] = NULL;
            d++;
        }
        degreeTable[d] = x;
    }

    heap->minNode = NULL;
    for (int i = 0; i <= maxDegree; i++) 
    {
        if (degreeTable[i] != NULL) 
        {
            degreeTable[i]->left = degreeTable[i]->right = degreeTable[i];
            if (heap->minNode == NULL) 
            {
                heap->minNode = degreeTable[i];
            } 
            else 
            {
                // add to root list
                degreeTable[i]->right = heap->minNode->right;
                degreeTable[i]->left = heap->minNode;
                heap->minNode->right->left = degreeTable[i];
                heap->minNode->right = degreeTable[i];

                if (degreeTable[i]->key < heap->minNode->key) 
                {
                    heap->minNode = degreeTable[i];
                }
            }
        }
    }
    free(degreeTable);
}

int fibExtractMin(FibonacciHeap* heap) 
{
    FibNode* z = heap->minNode;
    if (z == NULL) 
    {
        printf("Heap is empty\n");
        return -1;
    }

    // add children to root list
    if (z->child != NULL) 
    {
        FibNode* child = z->child;
        FibNode* start = child;
        do 
        {
            FibNode* next = child->right;
            child->right = heap->minNode->right;
            child->left = heap->minNode;
            heap->minNode->right->left = child;
            heap->minNode->right = child;
            child->parent = NULL;
            child = next;
        } while (child != start);
    }

    // remove z from list
    z->left->right = z->right;
    z->right->left = z->left;

    if (z == z->right) 
    {
        heap->minNode = NULL;
    } 
    else 
    {
        heap->minNode = z->right;
        fibConsolidate(heap);
    }
    heap->nodeCount--;

    int minKey = z->key;
    free(z);
    return minKey;
}

Python 实现

def _consolidate(self):
    import math
    max_degree = int(math.log2(self.node_count)) + 2 if self.node_count > 0 else 1
    degree_table = [None] * (max_degree + 1)

    # collect all root nodes
    roots = []
    if self.min_node is not None:
        roots.append(self.min_node)
        current = self.min_node.right
        while current != self.min_node:
            roots.append(current)
            current = current.right

    for root in roots:
        x = root
        d = x.degree

        while degree_table[d] is not None:
            y = degree_table[d]
            if x.key > y.key:
                x, y = y, x
            self._link(y, x)
            degree_table[d] = None
            d += 1
        degree_table[d] = x

    # rebuild root list and find new min
    self.min_node = None
    for node in degree_table:
        if node is not None:
            node.left = node.right = node
            if self.min_node is None:
                self.min_node = node
            else:
                self._add_to_root_list(node)
                if node.key < self.min_node.key:
                    self.min_node = node

def extract_min(self):
    z = self.min_node
    if z is None:
        print("Heap is empty")
        return None

    # add children to root list
    if z.child is not None:
        child = z.child
        start = child
        while True:
            next_child = child.right
            self._add_to_root_list(child)
            child.parent = None
            if child == start:
                break
            child = next_child

    # remove z from root list
    self._remove_from_list(z)

    if z == z.right:
        self.min_node = None
    else:
        self.min_node = z.right
        self._consolidate()

    self.node_count -= 1
    return z.key

Go 实现

func (h *FibHeap) consolidate() {
    maxDeg := int(math.Log2(float64(h.Count))) + 2
    table := make([]*FibNode, maxDeg+1)

    // 收集所有根节点
    var roots []*FibNode
    if h.Min != nil {
        roots = append(roots, h.Min)
        cur := h.Min.Right
        for cur != h.Min {
            roots = append(roots, cur)
            cur = cur.Right
        }
    }

    for _, r := range roots {
        x := r
        d := x.Degree
        for d < len(table) && table[d] != nil {
            y := table[d]
            // 确保 x 的键值更小
            if x.Key > y.Key {
                x, y = y, x
            }
            h.link(y, x)
            table[d] = nil
            d++
        }
        // 扩展 table 如果需要
        for d >= len(table) {
            table = append(table, nil)
        }
        table[d] = x
    }

    // 重建根链表并找到新的 Min
    h.Min = nil
    for _, n := range table {
        if n != nil {
            n.Left = n
            n.Right = n
            if h.Min == nil {
                h.Min = n
            } else {
                h.addToRootList(n)
                if n.Key < h.Min.Key {
                    h.Min = n
                }
            }
        }
    }
}

func (h *FibHeap) extractMin() (int, bool) {
    z := h.Min
    if z == nil {
        fmt.Println("Heap is empty")
        return 0, false
    }

    // 将 min 节点的所有子节点加入根链表
    if z.Child != nil {
        child := z.Child
        start := child
        for {
            next := child.Right
            h.addToRootList(child)
            child.Parent = nil
            if child == start {
                break
            }
            child = next
        }
    }

    // 从根链表中移除 z
    removeFromList(z)

    if z == z.Right {
        h.Min = nil
    } else {
        h.Min = z.Right
        h.consolidate()
    }
    h.Count--
    return z.Key, true
}

consolidate 使用度数表合并相同度数的树,直到没有两棵树度数相同。extractMin 先将 min 的子节点提升到根链表,移除 min 后调用 consolidate 整理。摊还 O(log n)。


减小键值(Decrease Key)

将某个节点的键值减小后,如果破坏了堆性质(新键值小于父节点),就执行:

  1. 剪切(Cut):将该节点从父节点剪下,加入根链表。
  2. 级联剪切(Cascade Cut):如果父节点已被标记,则也剪下父节点,递归向上。

这个操作是斐波那契堆的核心优势之一:摊还 O(1)。

C++ 实现

void FibonacciHeap::decreaseKey(FibNode* node, int newKey) 
{
    if (newKey > node->key) 
    {
        std::cout << "New key is greater than current key\n";
        return;
    }

    node->key = newKey;
    FibNode* parent = node->parent;

    if (parent != nullptr && node->key < parent->key) 
    {
        cut(node, parent);
        cascadeCut(parent);
    }

    if (node->key < minNode->key) 
    {
        minNode = node;
    }
}

C 实现

void fibCut(FibonacciHeap* heap, FibNode* x, FibNode* y) 
{
    // remove x from y's child list
    if (y->child == x) 
    {
        if (x->right == x) 
        {
            y->child = NULL;
        } 
        else 
        {
            y->child = x->right;
        }
    }
    x->left->right = x->right;
    x->right->left = x->left;
    y->degree--;

    // add x to root list
    x->right = heap->minNode->right;
    x->left = heap->minNode;
    heap->minNode->right->left = x;
    heap->minNode->right = x;
    x->parent = NULL;
    x->marked = false;
}

void fibCascadeCut(FibonacciHeap* heap, FibNode* node) 
{
    FibNode* p = node->parent;
    if (p != NULL) 
    {
        if (!node->marked) 
        {
            node->marked = true;
        } 
        else 
        {
            fibCut(heap, node, p);
            fibCascadeCut(heap, p);
        }
    }
}

void fibDecreaseKey(FibonacciHeap* heap, FibNode* node, int newKey) 
{
    if (newKey > node->key) 
    {
        printf("New key is greater than current key\n");
        return;
    }
    node->key = newKey;
    FibNode* parent = node->parent;

    if (parent != NULL && node->key < parent->key) 
    {
        fibCut(heap, node, parent);
        fibCascadeCut(heap, parent);
    }
    if (node->key < heap->minNode->key) 
    {
        heap->minNode = node;
    }
}

Python 实现

def _cut(self, x, y):
    """Cut x from parent y, add x to root list."""
    # remove x from y's child list
    if y.child == x:
        if x.right == x:
            y.child = None
        else:
            y.child = x.right
    self._remove_from_list(x)
    y.degree -= 1

    self._add_to_root_list(x)
    x.parent = None
    x.marked = False

def _cascade_cut(self, node):
    p = node.parent
    if p is not None:
        if not node.marked:
            node.marked = True
        else:
            self._cut(node, p)
            self._cascade_cut(p)

def decrease_key(self, node, new_key):
    if new_key > node.key:
        print("New key is greater than current key")
        return
    node.key = new_key
    parent = node.parent

    if parent is not None and node.key < parent.key:
        self._cut(node, parent)
        self._cascade_cut(parent)

    if node.key < self.min_node.key:
        self.min_node = node

Go 实现

// cut 将节点 x 从父节点 y 剪下,加入根链表
func (h *FibHeap) cut(x, y *FibNode) {
    // 从 y 的子链表中移除 x
    if y.Child == x {
        if x.Right == x {
            y.Child = nil
        } else {
            y.Child = x.Right
        }
    }
    removeFromList(x)
    y.Degree--

    h.addToRootList(x)
    x.Parent = nil
    x.Marked = false
}

// cascadeCut 级联剪切:如果父节点已标记,递归向上剪切
func (h *FibHeap) cascadeCut(node *FibNode) {
    p := node.Parent
    if p != nil {
        if !node.Marked {
            node.Marked = true
        } else {
            h.cut(node, p)
            h.cascadeCut(p)
        }
    }
}

func (h *FibHeap) decreaseKey(node *FibNode, newKey int) {
    if newKey > node.Key {
        fmt.Println("New key is greater than current key")
        return
    }
    node.Key = newKey
    parent := node.Parent

    if parent != nil && node.Key < parent.Key {
        h.cut(node, parent)
        h.cascadeCut(parent)
    }
    if node.Key < h.Min.Key {
        h.Min = node
    }
}

decreaseKey 减小键值后,如果破坏了堆性质就执行剪切和级联剪切。cut 将节点移至根链表并清除标记,cascadeCut 递归处理已标记的祖先节点。摊还 O(1)。


删除操作

删除一个节点:先将键值减小到极小值(使其成为新的 min),然后执行 extractMin。摊还 O(log n)。

C++ 实现

void deleteNode(FibNode* node) 
{
    decreaseKey(node, INT_MIN);
    extractMin();
}

C 实现

void fibDelete(FibonacciHeap* heap, FibNode* node) 
{
    fibDecreaseKey(heap, node, INT_MIN);
    fibExtractMin(heap);
}

Python 实现

def delete(self, node):
    self.decrease_key(node, float('-inf'))
    self.extract_min()

Go 实现

func (h *FibHeap) delete(node *FibNode) {
    h.decreaseKey(node, math.MinInt)
    h.extractMin()
}

删除操作通过将键值减小到 math.MinInt(使其成为最小节点),然后调用 extractMin 将其移除。摊还 O(log n)。


完整实现

C++ 完整实现

#include <iostream>
#include <vector>
#include <cmath>
#include <climits>

struct FibNode 
{
    int key;
    int degree;
    bool marked;
    FibNode *parent, *child, *left, *right;

    FibNode(int k) : key(k), degree(0), marked(false),
                     parent(nullptr), child(nullptr) 
    {
        left = right = this;
    }
};

class FibonacciHeap 
{
private:
    FibNode* minNode;
    int nodeCount;

    void addToRootList(FibNode* node) 
    {
        if (!minNode) 
        {
            minNode = node;
            node->left = node->right = node;
        } 
        else 
        {
            node->right = minNode->right;
            node->left = minNode;
            minNode->right->left = node;
            minNode->right = node;
        }
    }

    void removeFromList(FibNode* node) 
    {
        node->left->right = node->right;
        node->right->left = node->left;
    }

    void link(FibNode* y, FibNode* x) 
    {
        removeFromList(y);
        y->parent = x;
        if (!x->child) 
        {
            x->child = y;
            y->left = y->right = y;
        } 
        else 
        {
            y->right = x->child->right;
            y->left = x->child;
            x->child->right->left = y;
            x->child->right = y;
        }
        x->degree++;
        y->marked = false;
    }

    void consolidate() 
    {
        int maxDeg = (int)(log2(nodeCount)) + 2;
        std::vector<FibNode*> table(maxDeg + 1, nullptr);

        std::vector<FibNode*> roots;
        if (minNode) 
        {
            roots.push_back(minNode);
            FibNode* c = minNode->right;
            while (c != minNode) { roots.push_back(c); c = c->right; }
        }

        for (FibNode* r : roots) 
        {
            FibNode* x = r;
            int d = x->degree;
            while (d < (int)table.size() && table[d]) 
            {
                FibNode* y = table[d];
                if (x->key > y->key) std::swap(x, y);
                link(y, x);
                table[d] = nullptr;
                d++;
            }
            if (d >= (int)table.size()) table.resize(d + 1, nullptr);
            table[d] = x;
        }

        minNode = nullptr;
        for (FibNode* n : table) 
        {
            if (n) 
            {
                n->left = n->right = n;
                if (!minNode) { minNode = n; }
                else 
                {
                    addToRootList(n);
                    if (n->key < minNode->key) minNode = n;
                }
            }
        }
    }

    void cutNode(FibNode* x, FibNode* y) 
    {
        if (y->child == x) 
        {
            y->child = (x->right == x) ? nullptr : x->right;
        }
        removeFromList(x);
        y->degree--;
        addToRootList(x);
        x->parent = nullptr;
        x->marked = false;
    }

    void cascadeCut(FibNode* node) 
    {
        FibNode* p = node->parent;
        if (p) 
        {
            if (!node->marked) node->marked = true;
            else { cutNode(node, p); cascadeCut(p); }
        }
    }

public:
    FibonacciHeap() : minNode(nullptr), nodeCount(0) {}

    void insert(int key) 
    {
        FibNode* node = new FibNode(key);
        addToRootList(node);
        if (!minNode || node->key < minNode->key) minNode = node;
        nodeCount++;
    }

    int findMin() 
    {
        return minNode ? minNode->key : -1;
    }

    int extractMin() 
    {
        FibNode* z = minNode;
        if (!z) return -1;

        if (z->child) 
        {
            FibNode* c = z->child;
            FibNode* start = c;
            do 
            {
                FibNode* next = c->right;
                addToRootList(c);
                c->parent = nullptr;
                c = next;
            } while (c != start);
        }

        removeFromList(z);
        if (z == z->right) minNode = nullptr;
        else { minNode = z->right; consolidate(); }

        nodeCount--;
        int k = z->key;
        delete z;
        return k;
    }

    void decreaseKey(FibNode* node, int newKey) 
    {
        if (newKey > node->key) return;
        node->key = newKey;
        FibNode* p = node->parent;
        if (p && node->key < p->key) { cutNode(node, p); cascadeCut(p); }
        if (node->key < minNode->key) minNode = node;
    }

    bool isEmpty() { return minNode == nullptr; }
};

int main() 
{
    FibonacciHeap heap;

    int values[] = {10, 20, 5, 15, 30, 3, 8};
    for (int v : values) 
    {
        heap.insert(v);
        std::cout << "Inserted: " << v << "\n";
    }

    std::cout << "Min: " << heap.findMin() << "\n";

    std::cout << "ExtractMin: " << heap.extractMin() << "\n";
    std::cout << "New Min: " << heap.findMin() << "\n";

    std::cout << "\nExtracting all:\n";
    while (!heap.isEmpty()) 
    {
        std::cout << "  " << heap.extractMin() << "\n";
    }

    return 0;
}

运行该程序将输出

Inserted: 10
Inserted: 20
Inserted: 5
Inserted: 15
Inserted: 30
Inserted: 3
Inserted: 8
Min: 3
ExtractMin: 3
New Min: 5

Extracting all:
  5
  8
  10
  15
  20
  30

C 完整实现

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <limits.h>

typedef struct FibNode 
{
    int key, degree;
    bool marked;
    struct FibNode *parent, *child, *left, *right;
} FibNode;

typedef struct 
{
    FibNode* minNode;
    int count;
} FibHeap;

FibNode* newFibNode(int key) 
{
    FibNode* n = (FibNode*)malloc(sizeof(FibNode));
    n->key = key; n->degree = 0; n->marked = false;
    n->parent = n->child = NULL;
    n->left = n->right = n;
    return n;
}

FibHeap* newFibHeap() 
{
    FibHeap* h = (FibHeap*)malloc(sizeof(FibHeap));
    h->minNode = NULL; h->count = 0;
    return h;
}

void addToRootList(FibHeap* h, FibNode* n) 
{
    if (!h->minNode) { h->minNode = n; n->left = n->right = n; }
    else 
    {
        n->right = h->minNode->right;
        n->left = h->minNode;
        h->minNode->right->left = n;
        h->minNode->right = n;
    }
}

void removeFromList(FibNode* n) 
{
    n->left->right = n->right;
    n->right->left = n->left;
}

void linkNodes(FibHeap* h, FibNode* y, FibNode* x) 
{
    removeFromList(y);
    y->parent = x;
    if (!x->child) { x->child = y; y->left = y->right = y; }
    else 
    {
        y->right = x->child->right;
        y->left = x->child;
        x->child->right->left = y;
        x->child->right = y;
    }
    x->degree++;
    y->marked = false;
}

void consolidate(FibHeap* h) 
{
    int maxD = (int)(log2(h->count)) + 2;
    FibNode** table = (FibNode**)calloc(maxD + 1, sizeof(FibNode*));

    FibNode* roots[128]; int rc = 0;
    if (h->minNode) 
    {
        roots[rc++] = h->minNode;
        FibNode* c = h->minNode->right;
        while (c != h->minNode) { roots[rc++] = c; c = c->right; }
    }

    for (int i = 0; i < rc; i++) 
    {
        FibNode* x = roots[i]; int d = x->degree;
        while (d <= maxD && table[d]) 
        {
            FibNode* y = table[d];
            if (x->key > y->key) { FibNode* t = x; x = y; y = t; }
            linkNodes(h, y, x);
            table[d] = NULL; d++;
        }
        table[d] = x;
    }

    h->minNode = NULL;
    for (int i = 0; i <= maxD; i++) 
    {
        if (table[i]) 
        {
            table[i]->left = table[i]->right = table[i];
            if (!h->minNode) h->minNode = table[i];
            else 
            {
                addToRootList(h, table[i]);
                if (table[i]->key < h->minNode->key) h->minNode = table[i];
            }
        }
    }
    free(table);
}

void fibInsert(FibHeap* h, int key) 
{
    FibNode* n = newFibNode(key);
    addToRootList(h, n);
    if (n->key < h->minNode->key) h->minNode = n;
    h->count++;
}

int fibFindMin(FibHeap* h) { return h->minNode ? h->minNode->key : -1; }

int fibExtractMin(FibHeap* h) 
{
    FibNode* z = h->minNode;
    if (!z) return -1;

    if (z->child) 
    {
        FibNode* c = z->child;
        FibNode* start = c;
        do 
        {
            FibNode* next = c->right;
            addToRootList(h, c);
            c->parent = NULL;
            c = next;
        } while (c != start);
    }

    removeFromList(z);
    if (z == z->right) h->minNode = NULL;
    else { h->minNode = z->right; consolidate(h); }

    h->count--;
    int k = z->key; free(z);
    return k;
}

int main() 
{
    FibHeap* heap = newFibHeap();
    int vals[] = {10, 20, 5, 15, 30, 3, 8};
    int n = sizeof(vals) / sizeof(vals[0]);

    for (int i = 0; i < n; i++) 
    {
        fibInsert(heap, vals[i]);
        printf("Inserted: %d\n", vals[i]);
    }

    printf("Min: %d\n", fibFindMin(heap));
    printf("ExtractMin: %d\n", fibExtractMin(heap));
    printf("New Min: %d\n", fibFindMin(heap));

    printf("\nExtracting all:\n");
    while (heap->minNode) 
    {
        printf("  %d\n", fibExtractMin(heap));
    }

    free(heap);
    return 0;
}

运行该程序将输出

Inserted: 10
Inserted: 20
Inserted: 5
Inserted: 15
Inserted: 30
Inserted: 3
Inserted: 8
Min: 3
ExtractMin: 3
New Min: 5

Extracting all:
  5
  8
  10
  15
  20
  30

Python 完整实现

import math

class FibNode:
    def __init__(self, key):
        self.key = key
        self.degree = 0
        self.marked = False
        self.parent = None
        self.child = None
        self.left = self
        self.right = self

class FibonacciHeap:
    def __init__(self):
        self.min_node = None
        self.node_count = 0

    def is_empty(self):
        return self.min_node is None

    def _add_to_root_list(self, node):
        if self.min_node is None:
            self.min_node = node
            node.left = node.right = node
        else:
            node.right = self.min_node.right
            node.left = self.min_node
            self.min_node.right.left = node
            self.min_node.right = node

    def _remove_from_list(self, node):
        node.left.right = node.right
        node.right.left = node.left

    def _link(self, y, x):
        self._remove_from_list(y)
        y.parent = x
        if x.child is None:
            x.child = y
            y.left = y.right = y
        else:
            y.right = x.child.right
            y.left = x.child
            x.child.right.left = y
            x.child.right = y
        x.degree += 1
        y.marked = False

    def _consolidate(self):
        max_deg = int(math.log2(self.node_count)) + 2
        table = [None] * (max_deg + 1)

        roots = []
        if self.min_node:
            roots.append(self.min_node)
            c = self.min_node.right
            while c != self.min_node:
                roots.append(c)
                c = c.right

        for r in roots:
            x = r
            d = x.degree
            while d < len(table) and table[d]:
                y = table[d]
                if x.key > y.key:
                    x, y = y, x
                self._link(y, x)
                table[d] = None
                d += 1
            if d >= len(table):
                table.extend([None] * (d - len(table) + 1))
            table[d] = x

        self.min_node = None
        for n in table:
            if n:
                n.left = n.right = n
                if self.min_node is None:
                    self.min_node = n
                else:
                    self._add_to_root_list(n)
                    if n.key < self.min_node.key:
                        self.min_node = n

    def insert(self, key):
        node = FibNode(key)
        self._add_to_root_list(node)
        if self.min_node is None or node.key < self.min_node.key:
            self.min_node = node
        self.node_count += 1

    def find_min(self):
        return self.min_node.key if self.min_node else None

    def extract_min(self):
        z = self.min_node
        if z is None:
            return None

        if z.child:
            child = z.child
            start = child
            while True:
                nxt = child.right
                self._add_to_root_list(child)
                child.parent = None
                if child == start:
                    break
                child = nxt

        self._remove_from_list(z)
        if z == z.right:
            self.min_node = None
        else:
            self.min_node = z.right
            self._consolidate()

        self.node_count -= 1
        return z.key

    def _cut(self, x, y):
        if y.child == x:
            y.child = x.right if x.right != x else None
        self._remove_from_list(x)
        y.degree -= 1
        self._add_to_root_list(x)
        x.parent = None
        x.marked = False

    def _cascade_cut(self, node):
        p = node.parent
        if p:
            if not node.marked:
                node.marked = True
            else:
                self._cut(node, p)
                self._cascade_cut(p)

    def decrease_key(self, node, new_key):
        if new_key > node.key:
            print("New key is greater than current key")
            return
        node.key = new_key
        p = node.parent
        if p and node.key < p.key:
            self._cut(node, p)
            self._cascade_cut(p)
        if node.key < self.min_node.key:
            self.min_node = node

    def delete(self, node):
        self.decrease_key(node, float('-inf'))
        self.extract_min()


def main():
    heap = FibonacciHeap()

    values = [10, 20, 5, 15, 30, 3, 8]
    for v in values:
        heap.insert(v)
        print(f"Inserted: {v}")

    print(f"Min: {heap.find_min()}")
    print(f"ExtractMin: {heap.extract_min()}")
    print(f"New Min: {heap.find_min()}")

    print("\nExtracting all:")
    while not heap.is_empty():
        print(f"  {heap.extract_min()}")


if __name__ == "__main__":
    main()

运行该程序将输出

Inserted: 10
Inserted: 20
Inserted: 5
Inserted: 15
Inserted: 30
Inserted: 3
Inserted: 8
Min: 3
ExtractMin: 3
New Min: 5

Extracting all:
  5
  8
  10
  15
  20
  30

Go 完整实现

package main

import (
    "fmt"
    "math"
)

type FibNode struct {
    Key    int
    Degree int
    Marked bool
    Parent *FibNode
    Child  *FibNode
    Left   *FibNode
    Right  *FibNode
}

func newFibNode(key int) *FibNode {
    n := &FibNode{Key: key}
    n.Left = n
    n.Right = n
    return n
}

type FibHeap struct {
    Min   *FibNode
    Count int
}

func newFibHeap() *FibHeap {
    return &FibHeap{}
}

func (h *FibHeap) isEmpty() bool {
    return h.Min == nil
}

func (h *FibHeap) addToRootList(node *FibNode) {
    if h.Min == nil {
        h.Min = node
        node.Left = node
        node.Right = node
    } else {
        node.Right = h.Min.Right
        node.Left = h.Min
        h.Min.Right.Left = node
        h.Min.Right = node
    }
}

func removeFromList(node *FibNode) {
    node.Left.Right = node.Right
    node.Right.Left = node.Left
}

func (h *FibHeap) link(y, x *FibNode) {
    removeFromList(y)
    y.Parent = x
    if x.Child == nil {
        x.Child = y
        y.Left = y
        y.Right = y
    } else {
        y.Right = x.Child.Right
        y.Left = x.Child
        x.Child.Right.Left = y
        x.Child.Right = y
    }
    x.Degree++
    y.Marked = false
}

func (h *FibHeap) consolidate() {
    maxDeg := int(math.Log2(float64(h.Count))) + 2
    table := make([]*FibNode, maxDeg+1)

    var roots []*FibNode
    if h.Min != nil {
        roots = append(roots, h.Min)
        cur := h.Min.Right
        for cur != h.Min {
            roots = append(roots, cur)
            cur = cur.Right
        }
    }

    for _, r := range roots {
        x := r
        d := x.Degree
        for d < len(table) && table[d] != nil {
            y := table[d]
            if x.Key > y.Key {
                x, y = y, x
            }
            h.link(y, x)
            table[d] = nil
            d++
        }
        for d >= len(table) {
            table = append(table, nil)
        }
        table[d] = x
    }

    h.Min = nil
    for _, n := range table {
        if n != nil {
            n.Left = n
            n.Right = n
            if h.Min == nil {
                h.Min = n
            } else {
                h.addToRootList(n)
                if n.Key < h.Min.Key {
                    h.Min = n
                }
            }
        }
    }
}

func (h *FibHeap) insert(key int) {
    node := newFibNode(key)
    h.addToRootList(node)
    if h.Min == nil || node.Key < h.Min.Key {
        h.Min = node
    }
    h.Count++
}

func (h *FibHeap) findMin() (int, bool) {
    if h.Min == nil {
        return 0, false
    }
    return h.Min.Key, true
}

func (h *FibHeap) extractMin() (int, bool) {
    z := h.Min
    if z == nil {
        return 0, false
    }

    if z.Child != nil {
        child := z.Child
        start := child
        for {
            next := child.Right
            h.addToRootList(child)
            child.Parent = nil
            if child == start {
                break
            }
            child = next
        }
    }

    removeFromList(z)
    if z == z.Right {
        h.Min = nil
    } else {
        h.Min = z.Right
        h.consolidate()
    }
    h.Count--
    return z.Key, true
}

func (h *FibHeap) cut(x, y *FibNode) {
    if y.Child == x {
        if x.Right == x {
            y.Child = nil
        } else {
            y.Child = x.Right
        }
    }
    removeFromList(x)
    y.Degree--
    h.addToRootList(x)
    x.Parent = nil
    x.Marked = false
}

func (h *FibHeap) cascadeCut(node *FibNode) {
    p := node.Parent
    if p != nil {
        if !node.Marked {
            node.Marked = true
        } else {
            h.cut(node, p)
            h.cascadeCut(p)
        }
    }
}

func (h *FibHeap) decreaseKey(node *FibNode, newKey int) {
    if newKey > node.Key {
        fmt.Println("New key is greater than current key")
        return
    }
    node.Key = newKey
    parent := node.Parent
    if parent != nil && node.Key < parent.Key {
        h.cut(node, parent)
        h.cascadeCut(parent)
    }
    if node.Key < h.Min.Key {
        h.Min = node
    }
}

func (h *FibHeap) delete(node *FibNode) {
    h.decreaseKey(node, math.MinInt)
    h.extractMin()
}

func main() {
    heap := newFibHeap()

    values := []int{10, 20, 5, 15, 30, 3, 8}
    for _, v := range values {
        heap.insert(v)
        fmt.Printf("Inserted: %d\n", v)
    }

    if min, ok := heap.findMin(); ok {
        fmt.Printf("Min: %d\n", min)
    }

    if val, ok := heap.extractMin(); ok {
        fmt.Printf("ExtractMin: %d\n", val)
    }

    if min, ok := heap.findMin(); ok {
        fmt.Printf("New Min: %d\n", min)
    }

    fmt.Println("\nExtracting all:")
    for !heap.isEmpty() {
        if val, ok := heap.extractMin(); ok {
            fmt.Printf("  %d\n", val)
        }
    }
}

运行该程序将输出

Inserted: 10
Inserted: 20
Inserted: 5
Inserted: 15
Inserted: 30
Inserted: 3
Inserted: 8
Min: 3
ExtractMin: 3
New Min: 5

Extracting all:
  5
  8
  10
  15
  20
  30

斐波那契堆的性质

摊还时间复杂度

操作 二叉堆 二项式队列 斐波那契堆
插入(Insert) O(log n) O(log n) O(1)
查找最小值(FindMin) O(1) O(log n) O(1)
提取最小值(ExtractMin) O(log n) O(log n) O(log n)
减小键值(DecreaseKey) O(log n) O(log n) O(1)
删除(Delete) O(log n) O(log n) O(log n)
合并(Merge) O(n) O(log n) O(1)

斐波那契堆的优势场景

  • 大量 decreaseKey 操作:Dijkstra 最短路径算法、Prim 最小生成树算法中,斐波那契堆将 decreaseKey 从 O(log n) 降为 O(1),使算法整体复杂度更优。
  • Dijkstra 算法:O(V log V + E) vs 二叉堆的 O((V + E) log V)
  • Prim 算法:同理获得更优的时间复杂度
  • 大量合并操作:merge 为 O(1),优于二叉堆的 O(n)

实际使用注意

  • 常数因子大:虽然摊还复杂度优秀,但常数因子较大,在小数据集上可能不如二叉堆。
  • 实现复杂:斐波那契堆的实现远比二叉堆复杂,代码量大。
  • 最坏情况:extractMin 的最坏情况是 O(n),但摊还 O(log n)。
  • 实际应用:理论上最优,但实践中二叉堆或配对堆(Pairing Heap)通常表现更好。Python 的 heapq 和 C++ 的 std::priority_queue 都使用二叉堆。
posted @ 2026-04-17 02:18  游翔  阅读(9)  评论(0)    收藏  举报