在计算机科学中，**二项堆**（）是一种类似于二叉堆的堆结构。与二叉堆相比，其优势是可以快速合并两个堆，因此它属于可合并堆（）抽象数据类型的一种。

二项树
---

二项树递归定义如下：

* 度数为0的二项树只包含一个節点
* 度数为k的二项树有一个根節点，根節点下有k个子女，每个子女分别是度数分别为{\displaystyle k-1,k-2,...2,1,0}的二项树的根

二项树（从左至右度数分别为0至3
度数为k的二项树共有2^{k}个節点，高度为k。在深度d处有{\displaystyle {\tbinom {k}{d}}}（二项式系数）个節点。

度数为k的二项树可以很容易从两颗度数为k-1的二项树合并得到：把一颗度数为k-1的二项树作为另一颗原度数为k-1的二项树的最左子树。这一性质是二项堆用于堆合并的基础。

二项堆
---

二项堆是指满足以下性质的二项树的集合：

* 每棵二项树都满足最小堆性质，即節点关键字大于等于其父節点的值
* 不能有两棵或以上的二项树有相同度数（包括度数为0）。换句话说，具有度数k的二项树有0个或1个。

以上第一个性质保证了二项树的根節点包含了最小的关键字。第二个性质则说明節点数为n的二项堆最多只有 {\displaystyle \log {n}} 棵二项树。实际上，包含n个节点的二项堆的构成情况，由n的二进制表示唯一确定，其中每一位对应于一颗二项树。例如，13的二进制表示为1101, {\displaystyle 2^{3}+2^{2}+2^{0}}, 因此具有13个节点的二项堆由度数为3, 2, 0的三棵二项树组成： 

Example of a binomial heap  
示例：一个含13个節点的二项堆

二项堆的操作
------

由于并不需要对二项树的根節点进行随机存取，因而这些節点可以存成链表结构。

### 合并

合并分支度相同的二项树
合并两个二项堆示例，实际上把两棵分支度为1的二项树合并为一棵分支度为2的二项树。
最基本的为二个分支度相同的二项树的合并。由于二项树根節點包含最小的关键字，因此在二棵树合并时，只需比较二个根節點关键字的大小，其中含小关键字的節點成为结果树的根節點，另一棵树则变成结果树的子树。

```
**function** mergeTree(p, q)
    **if** p.root <= q.root
        **return** p.addSubTree(q)
    **else**
        **return** q.addSubTree(p)
```

  

两个二项堆的合并则可按如下步骤进行：分支度j从小取到大，在两个二项堆中如果其中只有一棵树的分支度为j，即将此树移动到结果堆，而如果只两棵树的分支度都为j，则根据以上方法合并为一个分支度为{\displaystyle j+1}的二项树。此后这个分支度为{\displaystyle j+1}的树将可能会和其他分支度为{\displaystyle j+1}的二项树进行合并。因此，对于任何分支度j，可能最多需要合并3棵二项树。

此操作的时间复杂度为{\displaystyle {O}(\log n)}。

```
**function** merge(p, q)
    **while** **not** (p.end() **and** q.end())
        tree = mergeTree(p.currentTree(), q.currentTree())
        
        **if** **not** heap.currentTree().empty()
            tree = mergeTree(tree, heap.currentTree())
        
        heap.addTree(tree)
        heap.next(); p.next(); q.next()
```

### 插入

创建一个只包含要插入元素的二项堆，再将此堆与原先的二项堆进行合并，即可得到插入后的堆。由于需要合并，插入操作需要{\displaystyle {O}(\log n)}的时间。平摊分析的时间复杂度为{\displaystyle {O}(1)}。

### 查找最小关键字所在節点

由于满足最小堆性质，只需查找二项树的的根節点即可，因为一共有\log n棵子树，所以用所时间为{\displaystyle {O}(\log n)}。

可以保存一个指向最小元素的指针，使得查找最小关键字所在節点需要{\displaystyle {O}(1)}的时间。在执行其他操作时，需要修改该指针。

### 删除最小关键字所在節点

先找到最小关键字所在節点，然后将它从其所在的二项树中删除，并获得其子树。将这些子树看作（合并为）一个独立的二项堆，再将此堆合并到原先的堆中即可。由于每棵树最多有\log n棵子树，创建新堆的时间为{\displaystyle {O}(\log n)}。同时合并堆的时间也为{\displaystyle {O}(\log n)}，故整个操作所需时间为{\displaystyle {O}(\log n)}。

```
**function** deleteMin(heap)
    min = heap.trees().first()
    **for each** current **in** heap.trees()
        **if** current.root < min **then** min = current
    **for each** tree **in** min.subTrees()
        tmp.addTree(tree)
    heap.removeTree(min)
    merge(heap, tmp)
```

### 减小特定節點(關鍵字)的值(Decreasing a key)

在减小特定節點（關鍵字）的值后，可能会不满足最小堆積性质。此时，将其所在節点与父節点交换，如还不满足最小堆性质则再与祖父節点交换……直到最小堆性质得到满足。操作所需时间为{\displaystyle {O}(\log n)}。

### 删除

将需要删除的節点的关键字的值减小到负无穷大（比二项堆中的其他所有关键字的值都小即可），执行"减小关键字的值"算法，使其调整到当前二项树的根节点位置，再删除最小关键字的根節点即可。

运行时间
----

以下对于二项堆操作的运行时间都为{\displaystyle {O}(\log n)}（節點数为n）：

* 在二项堆中插入新節点
* 查找最小关键字所在節点
* 从二项堆中删除最小关键字所在節点
* 减小给定節点关键字的值
* 删除给定節点
* 合并两个二项堆

参见
--

* 斐波那契堆

参考资料
----

* Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein（潘金贵等译）.. 机械工业出版社.
* Vuillemin, J. (1978). A data structure for manipulating priority queues. *Communications of the ACM* **21**, 309–314.

外部链接
----

* （英文）二项堆的Java applet摸拟
* （英文）二项堆的Python实现（页面存档备份，存于）
* （英文）二项堆的C实现（页面存档备份，存于）
* （英文）二项堆的Java实现

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
数据结构 |
| --- |
|  |
| 类型 | 
* 集合
* 容器

 |
|  |
| 抽象类型 | 
* 关联数组
	+ 多重关连数组（英语：）
* 列表
* 堆栈
* 队列
	+ 双端队列
* 优先队列
	+ 双端优先队列
* 集合
	+ 多重集
	+ 併查集
* 可持久化数据结构
	+ 线段树

 |
|  |
| 数组 | 
* 字串
* 位数组
* 环形缓冲器
* 动态数组
* 哈希表
* 哈希数组树（英语：）
* 稀疏矩阵

 |
|  |
| 链（英语：） | 
* 关联表（英语：）
* 链表
* 跳跃列表
* 松散链表（英语：）
* 异或链表

 |
|  |
| 树 | 
* 线段树
* 自平衡二叉查找树
* B树
* 二叉树
	+ AA树
	+ AVL树
	+ 红黑树
	+ 平衡树
	+ 伸展树
	+ 二叉查找树
* 堆
	+ 二叉堆
	+ 左偏树
	+ 二项堆
	+ 斐波那契堆
* R树
	+ R\*树
	+ R+树
	+ Hilbert R树（英语：）
* 前缀树
	+ 哈希树

 |
|  |
| 图 | 
* 有向图
* 有向无环图
* 二元决策图
* 无向图
* 确定性非循环有限自动机（英语：）

 |
|  |
| 数据结构术语 |

 |

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
计算机科学中的树 |
| --- |
|  |
| 二叉树 | 
* 二元搜尋樹
* 笛卡尔树
* MVP树
* Top tree（英语：）
* T树
* 线索二叉树

 |
|  |
| 自平衡二叉查找树 | 
* AA树
* AVL树
* 左倾红黑树
* 红黑树
* 替罪羊树
* 伸展树
* 树堆
* 加权平衡树

 |
|  |
| B树 | 
* B+树
* B\*树
* Bx树
* UB树
* 2-3树
* 2-3-4树
* (a,b)-树
* Dancing tree（英语：）
* H树

 |
|  |
| 堆 | 
* 二叉堆
* 二项堆
* 斐波那契堆
* 左偏树
* 配对堆
* 斜堆
* Van Emde Boas tree（英语：）

 |
|  |
| Trie | 
* 后缀树
* 基数树
* 三叉查找树
* X-快速前缀树
* Y-快速前缀树
* AC自动机

 |
|  |
| 二叉空间分割（BSP）树 | 
* 四叉树
* 八叉树
* *k*-d树
* 隐式*k*-d树
* VP树

 |
|  |
| 非二叉树 | 
* 指数树（英语：）
* 融合树（英语：）
* PQ树（英语：）
* SPQR树（英语：）

 |
|  |
| 空间数据分割树 | 
* R树
* R\*树
* R+树
* X树
* M树
* 線段樹 (儲存區間)
* 线段树 (区间查询)
	+ 可持久化线段树
* 希尔伯特R树
* 优先R树

 |
|  |
| 其他树 | 
* 散列日历
* 散列树
* Finger tree（英语：）
* 顺序统计树
* Metric tree（英语：）
* Cover tree（英语：）
* BK树
* Doubly chained tree（英语：）
* iDistance（英语：）
* Link-cut tree（英语：）
* Log-structured merge-tree（英语：）
* 树状数组
* 哈希树

 |

 |