二项队列

0)引论

左堆的合并,插入,删除最小的时间复杂度为O(logN)。二项队列就是为了对这些结果进一步提高的一种数据结构。利用二项队列,这三种操作的最坏时间复杂度为O(logN),但是插入的平均时间复杂度为O(1)。

1)二项队列

二项队列不是一棵树,它是一个森林,由一组堆序的树组成的深林,叫做二项队列。

二项队列有几个性质比较重要

(a) 每一颗树都是一个有约束的堆序树,叫做二项树

(b) 高度为k的第k个二项树Bk由一个根节点和B0, B1, .......B(k-1)构成

(c) 高度为k的二项树的结点个数为2^k

我们可以用二项树的结合表示任意大小的优先队列。例如,大小为13的优先队列就可以用B3,B2,B0来表示,二进制的表示为1101。对此,我深表怀疑二项队列是不是受二进制的启发而产生的。



2)二项队列的操作

查找最小项:只需要查找每个二项树的根节点就可以了,因此时间复杂度为O(logN)。

合并:通过把两个队列相加在一起完成。因为有O(logN)棵树,所以合并的时间复杂度也是O(logN)。

插入:插入也是一种合并,只不过是把插入的结点当做B0。虽然感觉插入的时间复杂度是O(logN),但是实际是O(1),因为有一定的概率是被插入的二项队列没有B0。

删除最小:在根结点找到最小值,然后把最小值所在的树单独拿出分列为二项队列,然后把这个新的二项队列与原二项队列进行合并。每一个过程的时间复杂度为O(logN)。故加起来的时间复杂度仍为O(logN)。

这些操作归根结底是合并Merge。



3)二项队列的代码实现

二项队列声明:

typedef struct BinNode *Position;
typedef struct BinNode *BinTree;
typedef struct Collection *BinQueue;
struct BinNode
{
	ElementType Element;
	Position LeftChild;
	Position Sibling;
};
struct Collection
{
	int CurrentSize;
	BinTree TheTrees[MaxTree];
}

首先定义了树BinNode,然后定义了森林Collection。

下图是TheTrees,数组里装的是指向个个二项树的指针。以及二项队列在上面定义的结构里面的表示方式。可以看出,根节点仅指向一个有最多子树的子结点,由这个结点指向各个兄弟节点,所以访问必然是逐级访问。



合并树:

合并树本质是指针的变动。当然要对两个二项树做好变换。

BinTree CombineTree(BinTree T1,BinTree T2)
{
	if(T1->Element>T2->Element)
		return CombineTree(T2,T1);
	T2->Sibling = T1->LeftChild;
	T1->LeftChild = T2;
	return T1;
}

合并两个优先队列(merge):

BinQueue Merge(BinQueue H1, BinQueue H2)
{
	BinTree T1, T2, Carry = NULL;
	int i,j;
	if(H1->CurrentSize+H2->CurrentSize>Capacity)
		Error("Exceed the Capacity");
	H1->CurrentSize = H1->CurrentSize + H2->CurrentSize;
	for(i=0,j=1;j<H1->CurrentSize;i++,j*=2)
	{
		T1 = H1->TheTrees[i];
		T2 = H2->TheTrees[i];
		switch(!!T1+2*!!T2+4*!!Carry)
		{
			case 0: //No Trees
			case 1: //Only H1
				break; 
			case 2: 
				H1->TheTrees[i] = T2;
				H2->TheTrees[i] = NULL;
				break;
			case 4: //Only Carry
				H1->TheTrees[i] = Carry;
				Carry = NULL;
				break;
			case 3: //T1,T2
				Carry = CombineTree(T1,T2);
				H1->TheTrees[i] = H2->TheTrees[i] = NULL;
				break;
			case 5:
				Carry = CombineTree(T1,Carry);
				H1->TheTrees[i] = NULL;
				break;
			case 6:
				Carry = CombineTree(T2,Carry);
				H2->TheTrees[i] = NULL;
				break;
			case 7:
				H1->TheTrees[i] = Carry;
				Carry = CombineTree(T1,T2);
				H2->TheTrees[i] = NULL;
				break;
		}
	}
	return H1;
}
在这段程序中,switch语句的加法是很不错的。

还有一个问题就是:怎么控制需要几阶二项队列,这直接导致程序要循环几次的问题。这里把两个二项队列的大小相加,假设是12的话,那么应该是4阶,因为3阶的大小为1+2+4 = 9<12,故应该为四阶,这也是循环控制的方式。

for(i=0,j=1;j<H1->CurrentSize;i++,j*=2)


删除最小值(DeleteMin):

ElementType DeleteMin(BinQueue H)
{
	int i,j;
	int MinTree;
	BinQueue DeleteQueue;
	Position DeletedTree, OldRoot;
	ElementType MinItem;

	if(IsEmpty(H))
	{
		Error("Empty BinQueue!!");
		return -Infinity;
	}
	//find the minmum
	Min = Infinity;
	for(i=0;i<MaxTree;i++)
	{
		if(H->TheTrees[i] && H->TheTrees[i]->Element<MinItem)
		{
			// Updata the minmun
			MiniItem = H->TheTrees[i]->Element;
			MinTree = i;
		}
	}
	// have found the DeleteTree
	DeleteTree = H->TheTrees[MinTree];
	OldRoot = DeleteTree;
	DeleteTree = OldRoot->LeftChild;
	free(OldRoot);

	// form the DeleteQueue
	DeletedQueue = Initialize();
	DeletedQueue->CurrentSize = (1<<MinTree) - 1;

	for(j=MinTree-1;j>=0;j--)
	{
		DeletedQueue->TheTree[j] = DeletedTree;
		DeletedTree = DeletedTree->Sibling;
		DeletedQueue->TheTree[j]->Sibling = NULL;
	}
	H->TheTrees[MiniTree] = NULL;
	H->CurrentSize -= DeletedQueue->CurrentSize+1;

	Merge(H,DeletedQueue);
	return MinItem;

}

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值