二项树及二项队列

来源:互联网 发布:手机上淘宝网怎么付款 编辑:程序博客网 时间:2024/05/21 08:56

二项树及二项队列

二项队列是由二项树按照一定规则组成的森林,故先介绍二项树:

1 二项树

二项树是这样的一种树:设高度为k的树Bk有

单节点树  when k=0

高度为k的树是由上一棵高度k-1的树作为孩子附接到另一棵高度k-1的树而构成   when k~=0

例如:


分析二项树特点可知,当高度k=0时树中节点数为1

当k~=0时,设此时节点数目N(k)=2*N(k-1)=2^2N(k-2)=...=2^k*N(0)=2^k

且第d层的节点树满足二项式系数Cd/k,故命名这种树为二项树。

2 二项队列(森林)

将上述二项树多棵组成森林按照以下原则:

a.符合堆序(根值最大或最小)

b.每一高度k上最多只能有一棵树,例如有高度k=2二项树多棵时需要合并。

可以知道二项队列有如下特点:

a.它是由二项树构成的森林;

b.每种高度最多只有一棵二项树,故最多含有Log(N)棵树;

c.二项队列中每棵树按照树高度可记为B0 B1 B2。。。;

d.任意正整数可由二项队列表示如上图中表示的7=二进制111(B2B1B0)。

3 二项队列操作

搜索最小元:

可以通过搜索所有树的根来找出。由于最多有有log(N)棵树故搜索最小元耗时O(log N)

合并二项队列:

合并的核心操作是:

a 两个队列若只存在一个高度k的二项树,保留

b 两个队列若存在两个高度k相同的二项树H1中的t1和H2中的t2,要让大的根成为小的根的子树从而产生高度为k+1的类进位树记为carry

c 两个队列若只存在三个高度k的二项树,选择一个保留,剩下两个相合并

设H1 H2是两个二项队列,H3是合并之后二项队列,基本操作如下(0为不存在,1为存在):carry  t2     t1表示含义0        0      0该高度没有树0        0      1该高度只有t1树0        1      0该高度只有t2树1        0      0该高度只有carry树0        1      1该高度t2和carry树1        0      1该高度t1和carry树1        1      0该高度t1,t2树1        1      1该高度t1,t2和carry树

合并两棵树只需常数时间,而最多log N棵树,故最多花费时间O(log N)

另外插入操作也可以归为合并,只不过是单节点的合并。以空队列插入1-7为例:


删除最小元:

主要操作:

a 找出一棵具有最小根的二项树并记为Bk,该二项队列为H

b 从H中的Bk删去根,剩下的为若干棵二项树B0,B1,B2。。。,Bk-1共同组成二项队列H11

c 从H中删去Bk,剩下的构成二项队列H22

d 合并H11 H22


合并:

查找最小和创建队列耗时O(log N),最终合并也是耗时O(log N)故最终耗时O(log N)。

4 二项队列C++实现

右上面分析可知,删除最小值需要快速找出根的所有子树,故每一个二项树表示方法采用左孩子右兄弟方法,且由一个指针数组存储每棵二项树的根节点

注意:

指针数组的大小一般至少是二项队列中二项树数目×2+1;

数组第 i 号索引处,存储的是高度为 i 的二项树。如,第0号索引,存储高度为0的二项树,该二项树只有一个结点,结点权值为13;

每棵二项树由左孩子右兄弟的链表保存;

诸孩子按照它们子树的大小排序。

.h 文件

#ifndef _binary_tree_#define _binary_tree_class binary_tree;class bin_node{    private:int key;bin_node*leftchild;bin_node*brother;    public:bin_node():key(-10000),leftchild(NULL),brother(NULL){}friend binary_tree;};class binary_tree{    private:int size;bin_node *Tree[M];    public:binary_tree(){int i;for(i=0;i<M;i++)Tree[i]=new bin_node();size=0;}~binary_tree(){int i;for(i=0;i<M;i++)delete Tree[i];}//not delete []bin_node*combinetree(bin_node*t1,bin_node*t2);binary_tree mergequeue(binary_tree h1,binary_tree h2);int deletemin(binary_tree h);};#endif

需要注意的是析构函数对指针数组的处理。指针数组中每一个都是一个数组指针,每一个都是delete[] 数组名,故加一个for循环,而不能简单的delete[]指针数组名。

.cpp文件

//**************************binary_tree**********************//bin_node*binary_tree::combinetree(bin_node*t1,bin_node*t2){if(t1->key>t2->key)    return combinetree(t2,t1);t2->brother=t1->leftchild;t1->leftchild=t2;return t1;}//相同高度binary_tree binary_tree::mergequeue(binary_tree h1,binary_tree h2){int i,j;bin_node*carry=NULL;//上一步得来的树类似加法里的进位bin_node*t1=NULL,*t2=NULL;h1.size+=h2.size;for(i=0,j=1;j<h1.size;i++,j*=2)  {t1=h1.Tree[i];t2=h2.Tree[i];   switch(4*!!carry+2*!!t2+!!t1)// c t2 t1                                // 0 0 0    {case 0:break;//no any trees     case 1:break;//only t1     case 2:h1.Tree[i]=t2;h2.Tree[i]=NULL;break;//only t2     case 4:h1.Tree[i]=carry;carry=NULL;break;//only carry     case 3:carry=combinetree(t1,t2);    h1.Tree[i]=h2.Tree[i]=NULL;break;//t1,t2,carry     case 5:carry=combinetree(t1,carry);    h1.Tree[i]=NULL;break;//t1 carry     case 6:carry=combinetree(t2,carry);    h2.Tree[i]=NULL;break;//t2 carry     case 7:h1.Tree[i]=carry;    carry=combinetree(t1,t2);h2.Tree[i]=NULL;break;// t1 t2 carry    }  }//一共log2(size)棵时故执行log2(size)次return h1;}int binary_tree::deletemin(binary_tree h){int i,j;int min_index=0,min_value=h.Tree[min_index]->key;//先找到最小值所在的二项树for(i=0,j=1;j<h.size;i++,j*=2) { if(h.Tree[i]&&h.Tree[i]->key<min_value)     {min_index=i;min_value=h.Tree[i]->key;} }bin_node*root=h.Tree[min_index];//删除最小值并重新构成优先队列H22bin_node*delete_tree=root->leftchild;free(root);binary_tree delete_queue;delete_queue.size=(1<<min_index)-1;//原始大小为2^(min_index)    for(j=min_index-1;j>=0;j--)    {delete_queue.Tree[j]=delete_tree;     delete_tree=delete_tree->brother;     delete_queue.Tree[j]->brother=NULL;    }//去掉含最小值的二项树构成H11h.Tree[min_index]=NULL;h.size-=delete_queue.size+1;mergequeue(h,delete_queue);return min_value;}




0 0