跳转至

斐波那契堆

概述

斐波那契堆是一种支持高效合并的堆结构,大部分操作达到O(1)摊还时间复杂度。

斐波那契堆特点

  1. 松散结构:树的组织较为松散
  2. 延迟合并:合并操作延迟到删除时
  3. 高效插入:O(1)摊还时间
  4. 标记机制:记录节点是否失去孩子

数据结构

C
typedef struct FibonacciNode {
    int key;
    int degree;
    int marked;
    struct FibonacciNode *parent;
    struct FibonacciNode *child;
    struct FibonacciNode *left;
    struct FibonacciNode *right;
} FibonacciNode;

typedef struct {
    FibonacciNode *min;
    int n;
} FibonacciHeap;

创建节点和堆

C
FibonacciNode* createFibonacciNode(int key) {
    FibonacciNode *node = (FibonacciNode*)malloc(sizeof(FibonacciNode));
    node->key = key;
    node->degree = 0;
    node->marked = 0;
    node->parent = NULL;
    node->child = NULL;
    node->left = node;
    node->right = node;
    return node;
}

FibonacciHeap* createFibonacciHeap() {
    FibonacciHeap *heap = (FibonacciHeap*)malloc(sizeof(FibonacciHeap));
    heap->min = NULL;
    heap->n = 0;
    return heap;
}

插入操作

C
void insertFibonacci(FibonacciHeap *heap, int key) {
    FibonacciNode *node = createFibonacciNode(key);
    
    if (heap->min == NULL) {
        heap->min = node;
    } else {
        node->left = heap->min;
        node->right = heap->min->right;
        heap->min->right->left = node;
        heap->min->right = node;
        
        if (node->key < heap->min->key) {
            heap->min = node;
        }
    }
    
    heap->n++;
}

合并操作

C
FibonacciHeap* unionFibonacci(FibonacciHeap *h1, FibonacciHeap *h2) {
    FibonacciHeap *heap = createFibonacciHeap();
    
    if (h1->min == NULL) {
        heap->min = h2->min;
    } else if (h2->min == NULL) {
        heap->min = h1->min;
    } else {
        FibonacciNode *temp = h1->min->right;
        h1->min->right = h2->min->right;
        h2->min->right->left = h1->min;
        h2->min->right = temp;
        temp->left = h2->min;
        
        heap->min = (h1->min->key < h2->min->key) ? h1->min : h2->min;
    }
    
    heap->n = h1->n + h2->n;
    
    free(h1);
    free(h2);
    
    return heap;
}

提取最小值

C
void addToRootList(FibonacciHeap *heap, FibonacciNode *node) {
    if (heap->min == NULL) {
        heap->min = node;
    } else {
        node->left = heap->min;
        node->right = heap->min->right;
        heap->min->right->left = node;
        heap->min->right = node;
    }
}

void removeNode(FibonacciNode *node) {
    node->left->right = node->right;
    node->right->left = node->left;
}

void linkFibonacci(FibonacciNode *y, FibonacciNode *x) {
    removeNode(y);
    
    if (x->child == NULL) {
        x->child = y;
        y->left = y;
        y->right = y;
    } else {
        y->left = x->child;
        y->right = x->child->right;
        x->child->right->left = y;
        x->child->right = y;
    }
    
    y->parent = x;
    x->degree++;
    y->marked = 0;
}

void consolidate(FibonacciHeap *heap) {
    int maxDegree = (int)(log(heap->n) / log(2)) + 1;
    FibonacciNode **degreeArray = (FibonacciNode**)calloc(maxDegree + 1, sizeof(FibonacciNode*));
    
    int rootCount = 0;
    FibonacciNode *curr = heap->min;
    if (curr) {
        rootCount = 1;
        while (curr->right != heap->min) {
            rootCount++;
            curr = curr->right;
        }
    }
    
    FibonacciNode **roots = (FibonacciNode**)malloc(sizeof(FibonacciNode*) * rootCount);
    curr = heap->min;
    for (int i = 0; i < rootCount; i++) {
        roots[i] = curr;
        curr = curr->right;
    }
    
    for (int i = 0; i < rootCount; i++) {
        FibonacciNode *x = roots[i];
        int d = x->degree;
        
        while (degreeArray[d] != NULL) {
            FibonacciNode *y = degreeArray[d];
            
            if (x->key > y->key) {
                FibonacciNode *temp = x;
                x = y;
                y = temp;
            }
            
            linkFibonacci(y, x);
            degreeArray[d] = NULL;
            d++;
        }
        
        degreeArray[d] = x;
    }
    
    heap->min = NULL;
    for (int i = 0; i <= maxDegree; i++) {
        if (degreeArray[i] != NULL) {
            addToRootList(heap, degreeArray[i]);
            
            if (heap->min == NULL || degreeArray[i]->key < heap->min->key) {
                heap->min = degreeArray[i];
            }
        }
    }
    
    free(degreeArray);
    free(roots);
}

int extractMinFibonacci(FibonacciHeap *heap) {
    FibonacciNode *z = heap->min;
    
    if (z != NULL) {
        FibonacciNode *child = z->child;
        if (child != NULL) {
            int childCount = 0;
            FibonacciNode *temp = child;
            do {
                childCount++;
                temp = temp->right;
            } while (temp != child);
            
            FibonacciNode **children = (FibonacciNode**)malloc(sizeof(FibonacciNode*) * childCount);
            temp = child;
            for (int i = 0; i < childCount; i++) {
                children[i] = temp;
                temp = temp->right;
            }
            
            for (int i = 0; i < childCount; i++) {
                addToRootList(heap, children[i]);
                children[i]->parent = NULL;
            }
            
            free(children);
        }
        
        removeNode(z);
        
        if (z == z->right) {
            heap->min = NULL;
        } else {
            heap->min = z->right;
            consolidate(heap);
        }
        
        heap->n--;
    }
    
    int minKey = z ? z->key : -1;
    if (z) free(z);
    return minKey;
}

时间复杂度

操作 时间复杂度
插入 O(1) 摊还
合并 O(1)
获取最小值 O(1)
删除最小值 O(log n) 摊还
减少键值 O(1) 摊还
删除 O(log n) 摊还

应用场景

  1. Dijkstra算法:优化到O(V log V + E)
  2. Prim算法:优化到O(E + V log V)
  3. 大规模图算法:边数远大于顶点数时优势明显

参考资料

  • 《算法导论》第19章
  • Fredman, Tarjan (1987) 原始论文