二项树及二项队列

二项树及二项队列

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

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];
//删除最小值并重新构成优先队列H22
bin_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;
    }
//去掉含最小值的二项树构成H11
h.Tree[min_index]=NULL;
h.size-=delete_queue.size+1;
mergequeue(h,delete_queue);
return min_value;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值