二项式堆

Posted masteryan576356467

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二项式堆相关的知识,希望对你有一定的参考价值。

#ifndef BINOMIAL_HEAP
#define BINOMIAL_HEAP

#include <vector>
#include <algorithm>
#include <iostream>

template<typename T>
struct BinomialNode {
	T val;
	BinomialNode *leftChild;
	BinomialNode *nextSibling;
	BinomialNode(const T &data, BinomialNode *left, BinomialNode *next) :
		val{ data }, leftChild{ left }, nextSibling{ next }{}
	BinomialNode(T &&data, BinomialNode *left, BinomialNode *next) :
		val{ std::move(data) }, leftChild{ left }, nextSibling{ next }{}
};



template<typename K, typename Cmp = std::less<K>>
class BinomialHeap {

	using BinomialNode = BinomialNode<K>;

public:

	BinomialHeap() : curSize{ 0 }, trees(DEFAULT_TREES), cmp{}{
		for (auto &root : trees)
			root = nullptr;
	}

	explicit BinomialHeap(const K &x) : curSize{ 1 }, trees(1), cmp{}{
		trees[0] = new BinomialNode{ x, nullptr, nullptr };
	}

	BinomialHeap(const BinomialHeap &rhs) : trees(rhs.trees.size()), curSize{ rhs.curSize } {
		for (std::size_t i = 0; i < rhs.trees.size(); ++i) {
			trees[i] = clone(rhs.trees[i]);
		}
	}
	BinomialHeap(BinomialHeap &&rhs) : trees{ std::move(rhs.trees) }, curSize{ rhs.curSize }{}

	BinomialHeap(const std::initializer_list<K> &ls) {
		BinomialHeap();
		for (const auto &i : ls)
			push(i);
	}

	~BinomialHeap() {
		clear();
	}


	BinomialHeap &operator =(const BinomialHeap &rhs) {
		BinomialHeap copy{ rhs };
		std::swap(*this, copy);
		return *this;
	}
	BinomialHeap &operator =(BinomialHeap &&rhs) {
		std::swap(curSize, rhs.curSize);
		std::swap(trees, rhs.trees);
		return *this;
	}

	// 清空堆
	void clear() {
		for (auto &root : trees)
			clear(root);
		curSize = 0;
	}

	//是否为空
	bool empty() const { return curSize == 0; }

	// 获取堆顶元素
	const K &top() const {
		return trees[findMinIndex()]->val; // assert(curSize == 0)
	}

	//获取元素个数
	std::size_t size() const { return curSize; }
	//合并堆
	void merge(BinomialHeap &rhs) {
		if (this == &rhs)  // 避免重名
			return;

		curSize += rhs.curSize;

		if (curSize > capacity()) { // 扩容
			auto oldNumTrees = trees.size();
			auto newNumTrees = std::max(trees.size(), rhs.trees.size()) + 1;
			trees.resize(newNumTrees);
			for (std::size_t i = oldNumTrees; i < newNumTrees; ++i)
				trees[i] = nullptr;        //对新的部分进行初始化
		}

		BinomialNode *carry = nullptr; // 上一次合并过来的树, 一开始为空

		//逐一合并, 类比二进制加法, 逐位相加
		for (std::size_t i = 0, j = 1; j <= curSize; ++i, j <<= 1) {
			// 获得两棵树对应位置的结点 
			// t1->thisNode t2->rhsNode
			BinomialNode *t1 = trees[i];
			BinomialNode *t2 = i < rhs.trees.size() ? rhs.trees[i] : nullptr;

			// 二进制 000-111 第一位是carry, 然后是t2 ,最后是t1
			char whichCase = t1 == nullptr ? 0 : 1;
			whichCase += t2 == nullptr ? 0 : 2;
			whichCase += carry == nullptr ? 0 : 4;

			switch (whichCase) {
				// 000 and 001
			case 0: /*non trees*/
			case 1: /*only this*/
				break;

				// 010
			case 2: /*only rhs*/
				trees[i] = t2;
				rhs.trees[i] = nullptr;
				break;
			case 3: // 011 this and rhs but no carry
				carry = combineTrees(t1, t2);
				trees[i] = rhs.trees[i] = nullptr;
				break;

			case 4: // 100 only carry
				trees[i] = carry;
				carry = nullptr;
				break;

			case 5: // 101 carry and this
				carry = combineTrees(carry, t1);
				trees[i] = nullptr;
				break;

			case 6: // 110 carry and rhs
				carry = combineTrees(carry, t2);
				rhs.trees[i] = nullptr;
				break;

			case 7: // 111 carry and rhs and this --> all 
				trees[i] = carry;
				carry = combineTrees(t1, t2);
				rhs.trees[i] = nullptr;
				break;
			default:
				break;
			}

		}
		// 不用的东西归0
		for (auto &root : rhs.trees)
			root = nullptr;
		rhs.curSize = 0;

	}

	//插入元素
	void push(const K &x) { 
		BinomialHeap oneIten(x);
		merge(oneIten);
	}
	void push(K &&x) { 
		BinomialHeap oneItem(std::move(x));
		merge(oneItem); 
	}
	
	//删除堆顶元素
	void pop() {
		
		auto minIndex = findMinIndex();
		BinomialNode *oldRoot = trees[minIndex];
		BinomialNode *deletedTree = oldRoot->leftChild;
		delete oldRoot;

		//构建新的堆
		BinomialHeap deletedHeap;
		deletedHeap.trees.resize(minIndex + 1);
		deletedHeap.curSize = (1 << minIndex) - 1;
		for (int j = minIndex - 1; j >= 0; --j) {
			deletedHeap.trees[j] = deletedTree;
			deletedTree = deletedTree->nextSibling;
			deletedHeap.trees[j]->nextSibling = nullptr;
		}

		//新的堆合并过来
		trees[minIndex] = nullptr;
		curSize -= deletedHeap.curSize + 1;
		merge(deletedHeap);

	}

	//auto show() const { return trees.size(); }

private:
	const static int DEFAULT_TREES = 1;

	std::vector<BinomialNode *> trees;
	std::size_t curSize;
	Cmp cmp;

	std::size_t capacity() const { return (1 << (trees.size())) - 1; }

	void clear(BinomialNode *&r) {
		if (r != nullptr) {
			clear(r->leftChild);
			clear(r->nextSibling);
			delete r;
			r = nullptr;
		}
	}


	BinomialNode *clone(BinomialNode *r) const {
		if (r == nullptr)
			return nullptr;
		return new BinomialNode{ r->val, clone(r->leftChild), clone(r->nextSibling) };
	}


	// 找到最小值对应的rank
	std::size_t findMinIndex() const {
		std::size_t i, index;
		for (i = 0; trees[i] == nullptr; ++i);
		// 顺序遍历获取最小值
		for (index = i; i < trees.size(); ++i)
			if (trees[i] != nullptr && cmp(trees[i]->val, trees[index]->val))
				index = i;
		return index;
	}

	//合并同样大小的两棵树
	BinomialNode *combineTrees(BinomialNode *t1, BinomialNode *t2) {
		if (cmp(t2->val, t1->val))
			return combineTrees(t2, t1); //让小的在前面 
		t2->nextSibling = t1->leftChild;
		t1->leftChild = t2;
		return t1;
	}

};



#endif // !BINOMIAL_HEAP

  

以上是关于二项式堆的主要内容,如果未能解决你的问题,请参考以下文章

高级数据结构—二项堆与斐波那契堆详细介绍(算法导论中科大USTC)

高级数据结构—斐波那契堆与二项堆详细介绍(算法导论中科大USTC)

[硕.Love Python] BinomialHeap(B堆 & 二项堆)

(数据结构)堆

【数据结构】堆(优先队列):二叉堆、d堆、左式堆、斜堆与二项队列

14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段