斐波那契堆

2021-07-09 01:58:12 字數 4832 閱讀 7365

## 斐波那契堆的介紹 ##
斐波那契堆是堆的一種,它和二項堆一樣,也是一種可合併堆,可用於實現合併優先佇列。而斐波那契堆比二項堆具有更好的平攤分析效能,它的合併操作的時間複雜度是o(1).

與二項堆一樣,它也是由一組堆最小有序樹組成,並且是一種可合併堆。

與二項堆不同的是,斐波那契堆中的樹不一定是二項樹;而且二項堆中的樹是有序排列的,但斐波那契堆中的樹都是有根而無序的。

## 斐波那契堆的基本操作 ##

## 基本定義 ##

typedef

int type;

typedef

struct _fibonaccinode

fibonaccinode fibnode;

fibnode是斐波那契堆的節點類,它包含的資訊比較多。key適用於比較節點大小的,degree 是記錄節點的度的,left和right 分別指向節點的左右兄弟,child是節點的第乙個孩子,parnet是節點的父親,marked是記錄該節點是否被刪除第乙個孩子(marked在刪除節點是有用).

typedef struct _fibonacciheap

fibonacciheap,fibheap;

fibheap是斐波那契對應的類。min是儲存當前堆的最小節點,keynum用於記錄節點的總數,,maxdegree用於記錄堆的最大度,而cons在刪除節點時來暫時儲存堆 資料的臨時空間,

## 斐波那契堆的記憶體結構圖 ##

從圖中可以看出,斐波那契堆是由一組最小堆構成,這些最小堆的根節點組成了雙向鍊錶(後文稱之為根鍊錶)斐波那契堆中的最小節點就是根煉表中的最小節點。。

## 2.插入操作 ##
插入操作非常簡單,插入乙個節點到堆中,直接將該節點插入到「根鍊錶的min節點」之前即可;若被插入節點比min節點小,則更新min節點為被插入節點。

斐波那契堆的根煉表是雙向鍊錶,這裡將min節點看成是雙向鍊錶的表頭。在插入節點時,每次都是將節點插入到min節點之前(即插入到雙鏈表末尾)。此外,對於根煉表中最小堆只有乙個節點的情況,插入操作就會演化成雙向鍊錶的插入操作。

static void fib_node_add(fibnode *node, fibnode *root)

/*將節點node插入到斐波那契堆heap中*/

static void fib_heap_insert_node(fibheap * heap, fibnode *node)

heap->keynum++;

}

## 3.合併操作 ##
合併操作與插入操作的原理非常類似,將乙個堆的根鍊錶插入到另乙個堆的根煉表上即可,簡單來說呢就是將兩個雙鏈表拼接成乙個雙向鍊錶。

## 合併操作** ##
/*

* 將雙向鍊錶b鏈結到雙向鍊錶a的後面

* * 注意: 此處a和b都是雙向鍊錶

*/static void fib_node_cat(fibnode *a, fibnode *b)

/* * 將h1, h2合併成乙個堆,並返回合併後的堆

*/fibheap* fib_heap_union(fibheap *h1, fibheap *h2)

if((h1->

min) ==

null) // h1無"最小節點"

else

if((h2->

min) ==

null) // h1有"最小節點" && h2無"最小節點"

// h1有"最小節點" && h2有"最小節點"

else

return h1;

}

## 取出最小節點 ##
抽取最小節點操作是斐波那契堆中較複雜的操作。

(1)將要抽取最小節點的子樹都直接串聯在根表中;

(2)合併所有degfree相等的樹,直到沒有相等的degree的樹。

## 取出最小節點 ##
/*

* 移除最小節點,並返回移除節點後的斐波那契堆

*/fibnode* _fib_heap_extract_min(fibheap *heap)

// 將min從根煉表中移除

fib_node_remove(min);

// 若min是堆中唯一節點,則設定堆的最小節點為null;

// 否則,設定堆的最小節點為乙個非空節點(min->right),然後再進行調節。

if (min

->right ==

min)

heap->

min=

null;

else

heap->keynum--;

return

min;

}

## 減小節點值 ##
減少斐波那契堆中的節點的鍵值,這個操作的難點是:如果減少節點後破壞了」最小堆」性質,如何去維護呢?下面對一般性情況進行分析。

(1) 首先,將」被減小節點」從」它所在的最小堆」剝離出來;然後將」該節點」關聯到」根鍊錶」中。 倘若被減小的節點不是單獨乙個節點,而是包含子樹的樹根。則是將以」被減小節點」為根的子樹從」最小堆」中剝離出來,然後將該樹關聯到根煉表中。

(2) 接著,對」被減少節點」的原父節點進行」級聯剪下」。所謂」級聯剪下」,就是在被減小節點破壞了最小堆性質,並被切下來之後;再從」它的父節點」進行遞迴級聯剪下操作。

而級聯操作的具體動作則是:若父節點(被減小節點的父節點)的marked標記為false,則將其設為true,然後退出。

否則,將父節點從最小堆中切下來(方式和」切被減小節點的方式」一樣);然後遞迴對祖父節點進行」級聯剪下」。

marked標記的作用就是用來標記」該節點的子節點是否有被刪除過」,它的作用是來實現級聯剪下。而級聯剪下的真正目的是為了防止」最小堆」由二叉樹演化成鍊錶。

(3) 最後,別忘了對根鍊錶的最小節點進行更新。

* 將斐波那契堆heap中節點node的值減少為key

*/static void fib_heap_decrease(fibheap *heap, fibnode *node, type key)

node->key = key;

parent

= node->

parent;

if (parent

!=null

&& node->key <

parent

->key)

// 更新最小節點

if (node->key < heap->

min->key)

heap->

min= node;

}

## 增加節點值 ##
增加節點值和減少節點值類似,這個操作的難點也是如何維護」最小堆」性質。思路如下:

(1) 將」被增加節點」的」左孩子和左孩子的所有兄弟」都鏈結到根煉表中。

(2) 接下來,把」被增加節點」新增到根鍊錶;但是別忘了對其進行級聯剪下。

* 將斐波那契堆heap中節點node的值增加為key

*/static void fib_heap_increase(fibheap *heap, fibnode *node, type key)

// 將node每乙個兒子(不包括孫子,重孫,...)都新增到"斐波那契堆的根鍊錶"中

while (node->child !=

null)

node->degree =

0; node->key = key;

// 如果node不在根煉表中,

// 則將node從父節點parent的子鏈結中剝離出來,

// 並使node成為"堆的根鍊錶"中的一員,

// 然後進行"級聯剪下"

// 否則,則判斷是否需要更新堆的最小節點

parent

= node->

parent;

if(parent

!=null)

else

if(heap->

min== node)}}

## 刪除節點 ##
刪除節點,本文採用了操作是:」取出最小節點」和」減小節點值」的組合。

(1) 先將被刪除節點的鍵值減少。減少後的值要比」原最小節點的值」即可。

(2) 接著,取出最小節點即可。

/*

* 刪除結點node

*/static void _fib_heap_delete(fibheap *heap, fibnode *node)

詳細解釋請參閱這裡,還有完整源**

斐波那契堆

以下是實現的程式 肯定可以再優化的。include include include include using namespace std class node delete m child m child null class fibonacciheap node insert int key v...

斐波那契堆

ifndef finbonacci heap h define finbonacci heap h include stdlib.h include math.h define error0 printf error at file s line d n file line 定義乙個求有符號的無窮大...

斐波那契堆

斐波那契堆同二項堆一樣,也是一種可合併堆。相對於二項堆,斐波那契堆的優勢在於如果不涉及刪除元素的操作,則它的平攤執行時間為o 1 但是由於其演算法過於複雜,因而實際上更多的是用二項堆。乙個斐波那契堆具有如下性質 堆有一組有序樹組成,但是堆中的樹不一定是二項樹 斐波那契堆中的樹之間是無序的 二項堆中的...