1. 程式人生 > >二項佇列分析及實現

二項佇列分析及實現

定義:

二項佇列不同於左式堆和二叉堆等優先佇列的實現之處在於,一個二項佇列不是一棵堆序的樹,而是堆序樹的集合,即森林。堆序樹中的每棵樹都是由約束形式的,叫做二項樹。每一個高度上至多存在一棵二項樹。高度為0的二項樹是一顆單節點樹,高度為k的二項樹Bk通過將一棵二項樹Bk-1附接到另一顆二項樹Bk-1的根上而構成。下圖顯示二項樹B0,B1,B2,B3以及B4。


二項佇列的複雜度

        左堆的合併,插入,刪除最小的時間複雜度為O(logN)。二項佇列就是為了對這些結果進一步提高的一種資料結構。利用二項佇列,這三種操作的最壞時間複雜度為O(logN),但是插入的平均時間複雜度為O(1)。

基本操作:
合併:
二項佇列的合併操作可以看做一個簡單的二進位制加法運算,從低位運算到高位。首先是兩個佇列的B0相加,如果兩個佇列都存在B0(二進位制為1),因此將兩個B0合併成一個B1樹,生成的B1樹當做進位,參與下一步的B1運算,直到運算到最高位結束。


刪除最小值/最大值:

刪除最小值首先要做的事情就是找到最小值。那麼只要尋找二項佇列對應的每一刻Bk樹的根節點中的最小值即可。然後把擁有最小值的Bk樹刪去根節點。此時剩下的樹為B0,B1...Bk-1樹。這些樹構成一個新的二項佇列,然後呼叫上述的合併操作,既可以完成刪除操作。

插入:

插入操作等同於合併操作,非常好完成。

編碼實現:

二項佇列定義:

在對二項佇列編碼之前,需要明白如何表示二項佇列。因為二項佇列的根節點所指向的節點可能是無限的,所以不能像二叉樹那樣使用兩個指標來指向兩個兒子(這裡有無數個兒子)。

具體的表示方式如下圖所示:

第一張圖代表我們畫出來的二項佇列。

第二張圖上半部分的陣列是指向樹節點的指標,即指向Bk的根節點。

每個樹節點有三個元素,Element,Leftchild, NextSibling。

其中NextSibling指的是和它本身同級的兄弟。如第一張圖中的B3,

12沒有同級兄弟,21,24,23互為同級兄弟,65,51,24互為同級兄弟。

那麼Leftchild元素指向誰呢,當然是指向有最多孩子的節點,提取出來就是B2的23節點了。

理解了這裡,在看第二張圖想必會明白多了。

合併:

合併操作的主要內容就是做二進位制加法運算,使用switch來進行判斷。具體到兩個Bk樹的合併非常的簡單,如下圖所示:


然較小的根節點變成新的根,另一個Bk成為它的左孩子,它原來的左孩子成為另一個Bk根節點的兄弟。


詳細程式碼:

標頭檔案:

  1. typedeflong ElementType;  
  2. #define Infinity    (30000L)
  3. #ifndef  _BinHeap_H
  4. #define _BinHeap_H
  5. #define MaxTrees    (14)        //二項佇列中的二項樹高度最大為13;
  6. #define Capacity    (16383)     //高度0,1,2,3,...,13的二項樹節點數目之和
  7. struct BinNode;                     //二項樹節點
  8. typedefstruct BinNode *BinTree;    //二項樹
  9. struct Collection;  
  10. typedefstruct Collection *BinQueue;  
  11. BinQueue Initialize(void);  
  12. void Destroy( BinQueue H);  
  13. BinQueue MakeEmpty(BinQueue H);  
  14. BinQueue Insert(ElementType Item, BinQueue H);  
  15. ElementType DeleteMin(BinQueue H);  
  16. BinTree CombineTrees( BinTree T1, BinTree T2 );     //合併兩棵相同大小的樹
  17. BinQueue Merge(BinQueue H1, BinQueue H2);  
  18. ElementType FindMin(BinQueue H);  
  19. int IsEmpty(BinQueue H);  
  20. int IsFull(BinQueue H);  
  21. #endif
原始檔:
  1. #include "binomial.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. typedefstruct BinNode *Position;  
  5. struct BinNode  
  6. {  
  7.     ElementType Element;  
  8.     Position LeftChild;  
  9.     Position NextSibling;  
  10. };  
  11. struct Collection  
  12. {  
  13.     int CurrentSize;  
  14.     BinTree TheTrees[MaxTrees];     //陣列,該陣列每個元素都是一棵二項樹
  15. };  
  16. BinQueue Initialize()  
  17. {  
  18.     BinQueue H;  
  19.     int i = 0;  
  20.     H = malloc(sizeof(struct Collection));  
  21.     if (H == NULL){  
  22.         printf("Out of space!!\n");  
  23.         return NULL;  
  24.     }  
  25.     H->CurrentSize = 0;      //設定二項佇列初始大小為0,每棵二項樹均為NULL
  26.     for (i = 0; i < MaxTrees; ++i){  
  27.         H->TheTrees[i] = NULL;  
  28.     }  
  29.     return H;  
  30. }  
  31. staticvoid DestroyTree(BinTree T)  
  32. {  
  33.     //遞迴刪除二項樹的所有節點
  34.     if (T != NULL){  
  35.         DestroyTree(T->LeftChild);  
  36.         DestroyTree(T->NextSibling);  
  37.         free(T);  
  38.     }  
  39. }  
  40. void Destroy(BinQueue H)  
  41. {  
  42.     //釋放二項佇列所佔空間:通過刪除所有的二項樹完成
  43.     int i = 0;  
  44.     for (i = 0; i < MaxTrees; ++i){  
  45.         DestroyTree(H->TheTrees[i]);  
  46.     }  
  47. }  
  48. BinQueue MakeEmpty(BinQueue H)  
  49. {  
  50.     int i = 0;  
  51.     Destroy(H);     //首先釋放H所佔空間
  52.     for (i = 0; i < MaxTrees; ++i){  
  53.         H->TheTrees[i] = NULL;  
  54.     }  
  55.     H->CurrentSize = 0;      //二項隊列當前大小
  56.     return H;  
  57. }  
  58. BinQueue Insert(ElementType Item, BinQueue H)  
  59. {  
  60.     BinTree NewNode;    //二項樹B0
  61.     BinQueue OneItem;   //只有B0的二項佇列
  62.     NewNode = malloc(sizeof(struct BinNode));  
  63.     if (NewNode == NULL){  
  64.         printf("Out of space!\n");  
  65.         return H;  
  66.     }  
  67.     NewNode->Element = Item;  
  68.     NewNode->LeftChild = NewNode->NextSibling = NULL;  
  69.     OneItem = Initialize();  
  70.     OneItem->CurrentSize = 1;  
  71.     OneItem->TheTrees[0] = NewNode;  
  72.     return Merge(H, OneItem);   //合併單節點的二項樹構成的二項佇列與H
  73. }  
  74. ElementType FindMin(BinQueue H)  
  75. {  
  76.     int i = 0;  
  77.     ElementType MinItem;  
  78.     if (IsEmpty(H)){  
  79.         printf("Empty binomial queue");  
  80.         return -1;  
  81.     }  
  82.     MinItem = Infinity;  
  83.     //遍歷二項佇列中的所有二項樹,比較它們的根
  84.     for (i = 0; i < MaxTrees; ++i){  
  85.         if (H->TheTrees[i] && H->TheTrees[i]->Element < MinItem){  
  86.             MinItem = H->TheTrees[i]->Element;  
  87.         }  
  88.     }  
  89.     return MinItem;  
  90. }  
  91. int IsEmpty(BinQueue H)  
  92. {  
  93.     return H->CurrentSize == 0;      //currentsize存放二項佇列中節點的個數
  94. }  
  95. int IsFull(BinQueue H)  
  96. {  
  97.     return H->CurrentSize == Capacity;  
  98. }  
  99. BinTree CombineTrees( BinTree T1, BinTree T2 )  
  100. {  
  101.     //合併相同大小的兩顆二項樹
  102.     if (T1 == NULL)  
  103.         return T2;  
  104.     elseif (T2 == NULL)  
  105.         return T1;  
  106.     if (T1->Element > T2->Element)  
  107.         return CombineTrees(T2, T1);  
  108.     //根大的樹做為根小的樹的左兒子
  109.     T2->NextSibling = T1->LeftChild;  
  110.     T1->LeftChild = T2;  
  111.     return T1;  
  112. }  
  113. BinQueue Merge(BinQueue H1, BinQueue H2)  
  114. {  
  115.     BinTree T1, T2, Carry = NULL;  
  116.     int i = 0, j = 0;  
  117.     //首先判斷合併是否會超出二項佇列限制的大小
  118.     if (H1->CurrentSize + H2->CurrentSize > Capacity){  
  119.         printf("Merge would exceed capacity!\n");  
  120.         return H1;  
  121.     }  
  122.     H1->CurrentSize += H2->CurrentSize;  
  123.     //遍歷H1,H2中所有的二項樹
  124.     for (i = 0, j = 1; j <= H1->CurrentSize; ++i, j *= 2){  
  125.         T1 = H1->TheTrees[i];  
  126.         T2 = H2->TheTrees[i];  
  127.         //若T1為空,!!T1則為0,否則為1
  128.         switch(!!T1 + 2* (!!T2) + 4 * (!!Carry)){  
  129.             case 0:  
  130.             case 1:  
  131.                 break;  
  132.             case 2:  
  133. 相關推薦

    佇列分析實現

    定義: 二項佇列不同於左式堆和二叉堆等優先佇列的實現之處在於,一個二項佇列不是一棵堆序的樹,而是堆序樹的集合,即森林。堆序樹中的每棵樹都是由約束形式的,叫做二項樹。每一個高度上至多存在一棵二項樹。高度為0的二項樹是一顆單節點樹,高度為k的二項樹Bk通過將一棵二項樹B

    資料結構--佇列分析實現

    一,介紹 什麼是二項佇列,為什麼會用到二項佇列? 與二叉堆一樣,二項佇列也是優先順序佇列的一種實現方式。在 資料結構--堆的實現之深入分析 的末尾 ,簡單地比較了一下二叉堆與二項佇列。 對於二項佇列而言,它可以彌補二叉堆的不足:merge操作的時間複雜度為O(N)。二項佇列的merge操作的最壞時間複雜

    信息摘要算法之:SHA1算法分析實現

    專家 臨時 總結 tro sha-1 即使 img md4 stand SHA算法,即安全散列算法(Secure Hash Algorithm)是一種與MD5同源的數據加密算法,該算法經過加密專家多年來的發展和改進已日益完善,現在已成為公認的最安全的散列算法之一,並被廣泛使

    資訊摘要演算法之:SHA1演算法分析實現

    SHA演算法,即安全雜湊演算法(Secure Hash Algorithm)是一種與MD5同源的資料加密演算法,該演算法經過加密專家多年來的發展和改進已日益完善,現在已成為公認的最安全的雜湊演算法之一,並被廣泛使用。1、概述SHA演算法能計算出一個數位資訊所對應到的,長度固定

    Java併發包原始碼學習系列:阻塞佇列BlockingQueue實現原理分析

    [toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

    RabbitMQ可用性分析實現

    RabbitMQ實戰:可用性分析和實現,場景可以使用「發後即忘」的模式,不需要響應,如果需要響應,可以使用RabbitMQ的RPC模型。 RabbitMQ以非同步的方式解耦系統間的關係,呼叫者將業務請求傳送到Rabbit伺服器,就可以返回了,Rabbit會確保請求被正確處理,即使遇到網路異常、R

    LinkedHashMap原始碼分析實現LRU演算法

    PS: 要先了解HashMap的實現原理HashMap原始碼分析 一、簡單介紹 public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

    B樹的原理分析實現

        B樹是為磁碟或其他直接存取的輔助裝置而設計的一種平衡搜尋樹。許多資料庫系統使用B樹或B樹的變種來儲存資訊。為何會採用這種樹結構進行設計呢,《演算法導論》18章講得很到位,值得細細品味。    下面直接開始瞭解B樹的實現細節吧!     一、B樹的定義     它與二

    密碼強度檢測演算法分析實現(JavaScript)案例說明

    用正則表示式做使用者密碼強度的通過性判定,過於簡單粗暴,不但使用者體驗差,而且使用者帳號安全性也差。那麼如何準確評價使用者密碼的強度,保護使用者帳號安全呢?本文分析介紹了幾種基於規則評分的密碼強度檢測演算法,並給出了相應的演示程式。大家可以根據自己專案安全性需要,做最適合於自

    基於DLNA的UPNP協議的分析實現

    1 引言 UPnP 全名是Universal Plug and Play,主要是微軟在推行的一個標準。簡單的來說,UPnP 最大的願景就是希望任何裝置只要一接上網路,所有在網路上的裝置馬上就能知道有新裝置加入,這些裝置彼此之間能互相溝通,更能直接使用或控制它,一切都不需要設定,完全的Plug and Pla

    佇列定義實現、迴圈佇列實現

    一、佇列     佇列是一種特殊的線性表,它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。佇列中沒有元素時,稱為空佇列。在佇列這種資料結構中,最先插入的元素將是最先被刪除的元素;反

    經典排序演算法分析實現

    一、排序概念 1. 排序:將一組雜亂無章的資料按照一定的規律組織起來,就稱為排序。 2. 資料表:待排序的資料元素的有限集合。 3. 排序碼:通常資料元素有多個屬性域,其中有一個屬性域可以用來區分元素,作為排序依據,該域即為排序碼。 * 若在資料表中各個元

    分散式系統選主場景分析實現

    一:需要選主的場景 1:服務有多臺機器,取其中一臺去執行任務。多臺機器同時執行會出問題,如將資料庫中狀態為失敗的記錄取出來重新執行,如果多臺機器同時執行,會導致一個失敗的任務被多臺機器同時執行。 2:服務有多臺機器,選其中一臺作為主,主負責任務的分發,大家一起消費並處理任務。還是將資料庫中狀態為失敗的記錄取

    windows server,nginx安裝,配置,運行nodeJS後端的web目的實現,以及錯誤分析解決方法

    lease args app clu ali real-ip directory 很多 命令 如果對nodeJS的後端的系統,源代碼在github上,https://github.com/saucxs/nodeJSBlog ,如果覺得可以,請請star並fork項目 項目

    spring-boot-admin原始碼分析單機監控spring-boot-monitor的實現

    SpringBootMonitor spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(一) spring-boot-admin原始碼分析及單機監控spring-boot-monitor的實現(二)

    叉樹時間複雜度分析增刪改查操作java實現

    順序表和連結串列的時間複雜度由給定條件不同從而會得出不同的時間複雜度結果,對於程式設計時並不總是最好用的儲存方式。二叉樹是一種更加穩定的資料儲存方式,其複雜度總是能表示為一個固定的形式。以下來分析二叉樹增刪改查操作做的時間複雜度。 設有如下資料需要進行二叉樹形式儲存:

    linux核心分析———SLAB原理實現

    //填充CPU快取記憶體 static void *cache_alloc_refill(structkmem_cache *cachep, gfp_t flags) { int batchcount; struct kmem_list3 *l3; struct arra

    MVC目實踐()——需求分析

    用例 分析 strong span 詳細 現在 同時 喜歡 發揮 需求: 作為一名觀眾,我希望知道詳細的比分變化和得分信息,以便於了解比賽走向和隊員的精彩得分。 用例故事: 裏約奧運女排決賽進行中... Ht7:現在比分多少了? LP:2:1,中國隊領先。 Ht7:那小比

    Java中String、StringBuilder、StringBuffer常用源碼分析比較():StringBuilder、StringBuffer源碼分析

    string類型 character private 字符 代碼 less pri des over StringBuilder: 一、構造方法: /** * Constructs a string builder with no characters in i

    實現自定義查詢的數據庫設計實現

    表名 table abr bigint sts 處理 update 關聯表 creat 上部分大概講了一下表設計,這部分講一下處理。 處理的結構 處理結構的內容比較多,分為幾個部分分別講解一下。首先講解一下尋找關系表。 尋找關系表 尋找關系表根據“表間關系登記表”進行處