堆的相关操作

【堆的概念】

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一

一维数组中,并满足:Ki<=K2*i+1 且 Ki<= K2*i+2(Ki >= K2*i+1 且 Ki >= K2*i+2) i = 0,1,2…,则称这个

堆为最小堆(或最大堆)。


最小堆:任一结点的关键码均小于等于它的左右孩子的关键码,位于堆顶结点的关键码最小。

最大堆:任一结点的关键码均大于等于它的左右孩子的关键码,位于堆顶结点的关键码最大。


在堆中,所有的记录具有称之为堆序的关系。
具有最小堆序的结点(除没有孩子的结点)之间存在小于或等于关系,从根节点到每个结
点的路径上数组元素组成的序列都是递增的。

具有最大堆序的结点(除没有孩子的结点)之间存在大于或等于关系,从根节点到每个结
点的路径上数组元素组成的序列都是递减的。


堆存储在下标为0开始计数的数组中,因此在堆中给定小标为i的结点时:
1、如果i=0,结点i是根节点,没有双亲节点;否则结点i的双亲结点为结点(i-1)/2
2、如果2*i+1>n-1,则结点i无左孩子,否则结点i的左孩子为结点2*i+1
3、如果2*i+2>n-1,则结点i无右孩子,否则结点i的右孩子为结点2*i+2


【堆的创建】

堆的建立有两种方式,一种是建立一个空堆,另一种是通过复制一个记录数组并对其加以调整形成一个空的堆。

下面的数组用完全二叉树表示:


将上述二叉树调整为最小堆的结构:
从结点(sizeof(arr) / sizeof(arr[0])-1)/2开始调整,将每一棵子树都调整成一棵最小堆结构.

第一次i=3,检测以i为结点的子树是否满足最小堆,如果满足则不调整,否则将其调整为最小堆。完毕之后i--(以3为下标的结点满足最小堆性质,故不需要调整)。

第二次i=2,不满足最小堆性质,故需要调整。

调整方法:a.取 i 结点左右孩子中较小的节点(5->65)。

 b.与结点 i 值比较,若i>a步骤中取得值,交换i与a中选中结点的值。完毕之后i--。


第三次i=1:将i=1的子树调整为最小堆。


第四次i=0:将整棵树调整为最小堆。



【堆的插入与删除】

插入:堆的插入每次都在已经建成的而最小堆的后面插入,但插入之后,有可能破坏了对的结构,这时就需要对堆进

重新调整。


删除:从堆中删除堆顶元素。移除堆顶元素之后,用堆的最后一个节点填补取走的堆顶元素,并将堆的实际元素个数减1。但用最后一个元素取代堆顶元素之后有可能破坏堆,因此需要将对自顶向下调整,使其满足最大或最小堆。


template<class T>
class Greater//大堆
{
public:
	bool operator()(const T&left, const T&right)
	{
		return left >= right;
	}
};
template<class T>
class Less//小堆
{
public:
	bool operator()(const T&left, const T&right)
	{
		return left < right;
	}
};


template<class T, template <class>class  Compare=Less>
class Heap
{
public:
	Heap()
	{}

	Heap(const T arr[], size_t size)
	{
		//_heap.resize(size);
		//保存数据
		for (size_t idx = 0; idx < size; ++idx)
		{
			_heap.push_back(arr[idx]);
			
		}
		//找最后一个非叶子节点
		size_t root = (_heap.size() - 2)/2;
		for (int idx = root; idx >= 0; --idx)
		{
			//调整idx为根的树,使其每个节点满足堆的性质
			_AdjustDown(idx,size);
		}
	}

	void Insert(const T& data)
	{
		_heap.push_back(data);
		size_t size = _heap.size();
		if (size > 1)
		{
			size_t size = _heap.size();
			size_t root = (size - 2) / 2;
			_AdjustUp(size);
		}
	}

	void Remove()
	{
		assert(!Empty());//assert为假触发
		size_t size = _heap.size();
		if (size > 1)
		{
			std::swap(_heap[0], _heap[size - 1]);
			_heap.pop_back();
			_AdjustDown(0, size - 1);
		}
		else
		{
			_heap.pop_back();
		}
	}

	bool Empty()const
	{
		return _heap.empty();
	}

	size_t size()
	{
		return  _heap.size();
	}
	T &Top()
	{
		return _heap[0];
	}


	~Heap()
	{}
public:

	void _AdjustDown(size_t root, size_t size)
	{
		size_t parent = root;

		//默认左孩子最小
		size_t child = root * 2 + 1;
		
		while (child<size)
		{
			

			if (child + 1 < size && Compare<T>()(_heap[child+1], _heap[child]))
			{
				child = child + 1;
			}

		//	if (_heap[parent] > _heap[child])
			if (Compare<T>()(_heap[parent], _heap[child]))
			{
				std::swap(_heap[parent], _heap[child]);
				parent = child;
				child = child * 2 + 1;
			}
			else{ break; }
		}
	}

	void _AdjustUp(size_t size)
	{
		size_t parent = (size - 2) / 2;
		size_t child = size - 1;
		while (child != 0)
		{
		//	if (_heap[child]<_heap[parent])
			if (Compare<T>()(_heap[child], _heap[parent]))
			{
				std::swap(_heap[child], _heap[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
				break;
		}
	}
private:
	std::vector<T>_heap;
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值