算法导论学习笔记-第十九章-二项堆

来源:互联网 发布:anesthesiologist 软件 编辑:程序博客网 时间:2024/05/18 09:19

第十九章 二项堆

 

总结:这一章介绍了二项树、二项堆,并介绍了二项堆的搜索、删除、插入等操作,二项堆比二叉堆的优势就是二项堆的合并的复杂度只有O(lgn),而二叉堆合并的复杂度在最坏情况下有O(n),因此若进行合并操作,二项堆的性能就比二叉堆要好了。

 

1.    二项树

一个二项堆由一组二项树构成。

二项树是一种递归定义的有序树,二项树B0只包含一个结点,二项树Bk由两颗二项树Bk-1链接而成:其中一颗树的根是另一棵树的根的最左孩子

 

二项树Bk的性质:

1)  共有2k个结点

2)  树的高度为k

3)  在深度i处恰有Cik个结点

4)  根的度数(子女的个数)为k,它大于任何其他结点的度数;如果根的子女从左到右的编号设为k-1, k-2, …, 0,子女i是子树Bi的根。

 

2.    二项堆

二项堆由一组二项树组成,二项堆H满足性质:

1)  H中的每个二项树都遵循最小堆性质:结点的关键字大于等于其父节点的关键字。因此,根的关键字最小

2)  对任意非负整数k,在H中至多有一棵二项树的根具有度数k。(即至多有一棵二项树Bk,事实上,若H中有n个结点,n的二进制表示为blogn,…,b0,那么当且仅当bi=1时,二项树Bi存在于H中)

 


二项堆中结点x的表示:

1)      父结点p[x]

2)      最左孩子child[x]

3)      紧右兄弟sibling[x]

4)      degree[x]

5)      关键字key[x]

一个二项堆中各二项树的根被组织成一个链表,称之为根表。

 

3.    二项堆的操作

1) 创建新二项堆

MAKE-BINOMIAL-HEAP分配并返回一个对象H,且head[H]=NIL

复杂度:O(1)

 

2) 寻找最小关键字

遍历根表,找出根表中关键字最小的结点。

复杂度:O(lgn)

伪代码

BINOMIAL-HEAP-MINIMUM(H)

y <- NIL

x <- head[H]

min <- INF

while x!=NIL

      do if key[x] < min

             then min <- key[x]

                    y <- x

         x <- sibling[x]

return y

 

3) 合并

连接操作,即将两棵根节点度数相同的二项树Bk-1连接成一棵Bk

复杂度:O(1)

伪代码

BINOMIAL-LINK(y,z)

p[y] <- z

sibling[y] <- child[z]

child[z] <- y

degree[z] <- degree[z]+1

 

合并操作分为两个阶段:

第一阶段执行BINOMIAL-HEAP-MERGE,将两个堆H1H2的根表合并成一个按度数的单调递增次序排列的链表。MERGE的时间复杂度O(logn)nH1H2的结点总数。

 

第二阶段将相等度数的根连接起来,直到每个度数至多有一个根时为止。执行过程中,合并的堆H的根表中至多出现三个根具有相同的度数。(MERGEH中至多出现两个根具有相同的度数,但是将两个相同度数的根的二项树连接后,可能与后面的至多两棵二项树出现相同的度数的根,因此至多出现三个根具有相同的度数)

第二阶段根据当前遍历到的根表中的结点x,分四种情况考虑:

Case1degree[x]!=degree[sibling[x]]。此时,不需要做任何变化,将指针向根表后移动即可。

Case2degree[x]=degree[sibling[x]]=degree[sibling[sibling[x]]]。此时,仍不做变化,将指针后移。

Case3 & Case4degree[x]=degree[sibling[x]]!=degree[sibling[sibling[x]]]

Case3key[x]<=key[sibling[x]]。此时,将sibling[x]连接到x上。

Case4key[x]>key[sibling[x]]。此时,将x连接到sibling[x]上。

复杂度:O(logn)

 

伪代码

BINOMIAL-HEAP-UNION(H1,H2)

H <- MAKE-BINOMIAL-HEAP()

head[H] <- BINOMIAL-HEAP-MERGE(H1,H2)

free the objects H1 and H2 but not the lists they point to

if head[H]=NIL

      then return H

prev-x <- NIL

x <- head[H]

next-x <- sibling[x]

while next-x!=NIL

      do if (degree[x]!=degree[next-x]) or (sibling[next-x]!=NIL and

 degree[x]=degree[sibling[next-x]])

                 then prev-x <- x

                       x <- next-x

          else if key[x]<=key[next-x]

                      then sibling[x] <- sibling[next-x]

                              BINOMIAL-LINK(next-x,x)

           else if prev-x=NIL

                      then head[H] <- next-x

                      else sibling[prev-x] <- next-x

                  BINOMIAL-LINK(x,next-x)

                  x <- next-x

     next-x <- sibling[x]

return H

 

4) 插入

先构造一个只包含一个结点的二项堆,再将此二项堆与原二项堆合并

复杂度:O(logn)

伪代码

BINOMIAL-HEAP-INSERT(H,x)

H’ <- MAKE-BINOMIAL-HEAP()

p[x] <- NIL

degree[x] <- 0

child[x] <- NIL

sibling[x] <- NIL

head[H’] <- x

H <- BINOMIAL-HEAP-UNION(H,H’)

 

5) 抽取具有最小关键字的结点

从根表中找到最小关键字的结点,将以该结点为根的整棵二项树从堆取出,删除取出的二项树的根,将其剩下的子女倒序排列,组成了一个新的二项堆,再与之前的二项堆合并。

复杂度:O(logn)

伪代码

BINOMIAL-HEAP-EXTRACT-MIN(H)

find the root x with the minimum key in the root list of H

      and remove x from the root list of H

H’ <- MAKE-BINOMIAL-HEAP()

reverse the order of the linked list of x’s children, setting the p field of each child to NIL and set head[H’] to point to the head of the resulting list

H <- BINOMIAL-HEAP-UNION(H,H’)

return x

 

6) 减小关键字的值

复杂度:O(logn)

伪代码

BINOMIAL-HEAP-DECREASE-KEY(H,x,k)

if k > key[x]

      then error “error”

key[x] <- k

y <- x

z <- p[y]

while z!=NIL and key[y] < key[z]

      do

           exchange key[y] <-> key[z]

           y <- z

           z <- p[z]

 

7) 删除

复杂度:O(logn)

伪代码

BINOMIAL-HEAP-DELETE(H,x)

BINOMIAL-HEAP-DECREASE-KEY(H,x,-INF)

BINOMIAL-HEAP-EXTRACT-MIN(H)