二项队列

///heap.h~
#ifndef _HEAP_H
#define _HEAP_H

class BinQueue
{
private:

	int currentSize;//当前结点数目
	int maxSize;//树的最多个数
	struct BinNode {
		int element; //结点的元素
		BinNode *parent;
		BinNode *child; //左孩子
		BinNode *sibling; //右邻居
		BinNode(int x = 0, BinNode* p = nullptr);//结点构造函数
	
	};//结点
	BinNode** BinTree; //二项树
	
	BinNode* combineTrees(BinNode* t1, BinNode* t2); //连接两棵树
	void traversal(BinNode* p); //前序遍历
	BinNode* findKey(BinNode* h, int key);
	BinQueue(int size,int current);//二项队列构造函数
	void destroy(BinNode* h);

public:

	BinQueue(int size = 10); //二项队列构造函数
	~BinQueue();

	void insert(int x);//插入
	void findAndDecreaseKey(int k,int x);//找到值为x的关键字并减为k
	void decreaseKey(int k, BinNode* x);//减少关键字为k
	void merge(const BinQueue &h); //合并
	int deleteMin(); //删除最小值
	void print();//二项队列的输出
};

#endif


#include"heap.h"
#include<algorithm>
#include<cstdio>
#include<climits>

extern double sum1;

BinQueue::BinNode::BinNode(int x,BinNode* p)
{
	parent = p;
	element = x;//存储结点
	child = sibling = nullptr;
}

BinQueue::~BinQueue()
{
	for (int i = 0; i < maxSize; i++) {
		if (BinTree[i]) {
			destroy(BinTree[i]);
		}
	}
}

void BinQueue::destroy(BinNode* h)
{
	BinNode* child = h->child;
	BinNode* sibling = h->sibling;
	if(child)destroy(child);
	if(sibling)destroy(sibling);
	delete h;
}

//私有的构造函数,不为用户提供指定结点大小的方法
BinQueue::BinQueue(int size,int current)
{
	currentSize = current; //存储当前结点数
	maxSize = size; //存储树的最大个数
	BinTree = new BinNode*[size]; //为二项树申请内存
	for (int i = 0; i < size; i++) {
		BinTree[i] = nullptr; //初始化每个树的根节点为null
	}
}

//构造函数,传入二项队列的大小
BinQueue::BinQueue(int size)
{
	currentSize = 0; //存储当前结点数(初始为0)
	maxSize = size; //存储树的最大个数
	BinTree = new BinNode*[size]; //为二项树申请内存
	for (int i = 0; i < size; i++) {
		BinTree[i] = nullptr; //初始化每个树的根节点为null
	}
}

//找到关键字并减小(接口函数)
//attention:统计时间时,find的时间不要计入
void BinQueue::findAndDecreaseKey(int k, int x)
{
	BinNode* tmp = nullptr;
	for (int i = 0; i < maxSize; i++) {
		if (BinTree[i]) { 
			tmp = findKey(BinTree[i], x); //搜索每一棵不为空的二项树
			if (tmp)break; //找到了退出循环
		}
	}
	if (tmp) { //如果存在,执行减少关键字的步骤;
		decreaseKey(k, tmp);
	}
}

//找到关键字
BinQueue::BinNode* BinQueue::findKey(BinNode* h, int key)
{
	BinNode* p, *x = nullptr;
	p = h;
	while (p) {  //当p不为空时
		if (p->element == key)return p; //当前结点的值为key,返回当前结点
		else {
			x = findKey(p->child, key); //否则继续搜索其孩子
			if (x) return x;//在它的后代找到了关键字,返回其后代
			p = p->sibling; // 继续搜索其兄弟
		}
	}
	return nullptr;//没找到,则返回null
}

//减小关键字
void BinQueue::decreaseKey(int k, BinNode* x)
{
	if (k >= x->element) return;//修改的值比原来大,不符合条件
	x->element = k;//更新关键字
	BinNode* now = x;
	BinNode* parent = now->parent; //记录当前结点及其父节点
	//父节点不为空,且父节点值大于当前结点时(违背了最小堆性质)
	while (parent != nullptr && now->element < parent->element) {
		std::swap(parent->element, now->element);//交换两者
		now = parent;//继续向上寻找
		parent = now->parent;
	}
	return;
}

//插入
void BinQueue::insert(int x)
{
	if (currentSize == 0) { //如果没有结点,直接插入结点
		BinTree[0] = new BinNode(x);
		currentSize++;
		return;
	}
	//如果已经有结点,新建一个只含一个结点的二项队列,与其合并
	BinQueue *tmp = new BinQueue(maxSize,1);
	tmp->BinTree[0] = new BinNode(x);
	merge(*tmp);
	return;
}

//合并
void BinQueue::merge(const BinQueue &h)
{
	int i, j;
	BinNode *t1, *t2;
	BinNode *carry = nullptr;//carry代表进位
	currentSize += h.currentSize;//更新当前结点数
	for (i = 0, j = 1; j <= currentSize; i++, j *= 2) {
		t1 = BinTree[i];
		t2 = h.BinTree[i];
		//   将三棵树的状态表示为二进制的形式
		//   | carry | t2 | t1 |
		//   null为0,非null为1
		//   总共有7种可能结果
		switch ((!!carry)*4 + (!!t2)*2 + !!t1) {
			case 0:/*000*/
			case 1:/*001*/
				break;
			case 2:/*010*/
				BinTree[i] = t2, h.BinTree[i] = nullptr;
				break;
			case 3:/*011*/
				carry = combineTrees(t1, t2);
				h.BinTree[i] = BinTree[i] = nullptr;
				break;
			case 4:/*100*/
				BinTree[i] = carry, carry = nullptr; 
				break;
			case 5:/*101*/
				carry = combineTrees(t1, carry);
				BinTree[i] = nullptr;
				break;
			case 6:/*110*/
				carry = combineTrees(t2, carry);
				h.BinTree[i] = nullptr; 
				break;
			case 7:/*111*/
				BinTree[i] = carry;
				carry = combineTrees(t1, t2);
				h.BinTree[i] = nullptr;
				break;
		}
	}
}

//连接
BinQueue::BinNode* BinQueue::combineTrees(BinNode* t1, BinNode* t2)
{
	//维护t1始终小于t2,保证t1为根节点,t2是其孩子
	if (t1->element > t2->element) {
		std::swap(t1, t2);
	}
	t2->parent = t1;
	//t2在成为t1的孩子之前,t1的孩子先成为它的兄弟
	t2->sibling = t1->child;
	//t2再成为t1的孩子
	t1->child = t2;
	return t1;
}

//输出二项队列
void BinQueue::print()
{
	for (int i = 0; i < maxSize; i++) {
		if (BinTree[i] != nullptr) {//不为空的话
			printf("Tree %d: ",i);
			traversal(BinTree[i]);//输出其元素
			printf("\n");
		}
	}
	printf("\n");
	return;
}

//前序遍历
void BinQueue::traversal(BinNode* p)
{
	if (!p)return;
	if (!p->parent)
	printf("(%d null),",p->element);
	else
	printf("(%d %d),", p->element,p->parent->element);
	traversal(p->child);//继续搜索孩子
	traversal(p->sibling);//继续搜索邻居
}

int BinQueue::deleteMin()
{
	int i, j, minTree;
	BinNode* deleteTree = nullptr;
	BinNode* oldRoot = nullptr;
	int min = INT_MAX;
	if (currentSize == 0)return -1;

	//找到最小的树,记录最小元素及其下标
	for (i = 0; i < maxSize; i++) {
		if (BinTree[i] && BinTree[i]->element < min) {
			min = BinTree[i]->element;
			minTree = i;
		}
	}

	deleteTree = BinTree[minTree];//临时存储要删除的那个结点
	BinTree[minTree] = nullptr;//删除结点
	oldRoot = deleteTree; //记录要删除的结点
	deleteTree = deleteTree->child; //跟踪要删除结点的孩子结点
	free(oldRoot);//释放删除结点的内存
	
	BinQueue deleteQueue(maxSize,((1 << minTree) - 1));
	//被删除后树的大小为2^minTree - 1,其中minTree为其下标
	//当一个树被删除根结点后,相当于100…000 -> 11…111
	//所以只需要去除根结点的子树一个个后移即可
	for (j = minTree - 1; j >= 0; j--) {
		deleteTree->parent = nullptr;
		deleteQueue.BinTree[j] = deleteTree; 
		deleteTree = deleteTree->sibling;
		deleteQueue.BinTree[j]->sibling = nullptr;
	}
	currentSize -= deleteQueue.currentSize + 1;//更新删除结点后二项队列的结点数
	merge(deleteQueue);//将删除结点的子树与原二项队列合并
	return min;
}


你可能感兴趣的:(二项队列)