[C++]小根堆 插入/删除/初始化

小根堆的性质:是完全二叉树,根节点的关键字小于等于两个子节点的关键字。
插入和删除操作都应当保证小根堆的性质不变。
由于小根堆是完全二叉树,所以使用数组存放具有显著优势:按照层次遍历将具有n个元素的小根堆的存放到数组(命名为heap)的[1]~[n]项中。此时对于任一元素heap[i],其父节点为heap[i/2],其子节点为heap[i * 2] 和 heap[i*2+1]。
1、插入元素:从新的叶节点开始,向上考察其父节点,若父节点的关键字大于新节点的关键字,则二者交换位置,然后继续向上考察;否则循环停止,插入位置确定。
2、删除根节点(删除最小节点):首先删除heap[1],然后考察最后一个叶节点能否填入空位(保证填入后依然是小根堆),如果能,则将该叶节点填入空位;如果不能,则将空位的两个子节点中较小的一个填入空位,然后对新的空位进行同样的的操作。
3、将数组堆化:从最后一个有子节点的节点(由完全二叉树的性质可知,该节点在数组中的下标是节点个数长度/2)开始向前考察,对于考察到的每个节点,比较该节点的关键字与其较小子节点的关键字,若该节点的关键字更大,则二者交换位置,然后继续比较该节点的关键字与新位置上较小子节点的关键字,直到该节点位置确定。

代码

template<class T>
class minTree//小根堆类
{
public:
	minTree(int initlength=100)
	{
		arrayLength = initlength;
		heap = new T[arrayLength];
		heap[0] = NULL;
		heapSize = 0;
	}
	minTree(T* theHeap, int theSize,int theLength)//利用已知数组创建小根堆
	{
		heap = theHeap;
		heapSize = theSize;
		arrayLength = theLength;
		//将传入的数组堆化
		for (int root = heapSize / 2; root >= 1; root--)//heap[heapSize/2]是堆中最后一个有子节点的节点,从该节点开始向前遍历
		{
			T rootElement = heap[root];
			int child = root * 2;//左子节点的下标
			while (child <= heapSize)
			{
				if (child<heapSize && heap[child]>heap[child + 1])//从左子节点和右子节点中找出较小的
					child++;
				if (rootElement <= heap[child])//若根节点比两个子节点都小,则不再需要交换,又因为子节点以下都已经排好顺序,所以直接退出循环
					break;
				heap[child / 2] = heap[child];//若根节点大于其中一个子节点,则交换,然后在交换完之后的位置上继续向下考察
				child *= 2;
			}
			heap[child / 2] = rootElement;
		}

	}
	T front()//获取堆中首项,即最小值
	{
		return heap[1];
	}
	bool empty()
	{
		return heapSize == 0;
	}
	void push(const T& theElement)//插入元素
	{
		if (heapSize == arrayLength - 1)//数组若满长度加倍
		{
			T* newHeap = new T[arrayLength * 2];
			for (int i = 0; i < arrayLength; i++)
				newHeap[i] = heap[i];
			delete[]heap;
			heap = newHeap;
			arrayLength *= 2;
		}
		heapSize++;
		int currentNode = heapSize;//新节点是最后一个节点
		while (currentNode != 1 && heap[currentNode / 2] > theElement)//从新节点开始向上考察其父节点,若父节点比当前节点大,则交换位置
		{
			heap[currentNode] = heap[currentNode / 2];
			currentNode /= 2;
		}
		heap[currentNode] = theElement;
	}
	void pop()//删除最小元素,即删除heap[1]
	{
		if (heapSize == 0)//当前堆为空,则直接退出
		{
			cout << "空堆" << endl;
			return;
		}
		heap[1].~T();
		//重建小根堆,由于需要保持完全二叉树的性质,所以在尽可能调用堆的最后一个节点来补足空位
		T lastElement = heap[heapSize];
		heapSize--;
		int currentNode = 1;//此时该位为空,需要由最后一个节点或子节点来补足
		int child = 2;
		while (child <= heapSize)
		{
			if (child<heapSize && heap[child]>heap[child + 1])//选择子节点中较小的一个
				child++;
			if (lastElement <= heap[child])//若最后一个结点比子节点小,则结束循环,由最后一个节点来补足空位
				break;
			heap[currentNode] = heap[child];//否则由子节点来补足空位,然后继续向下考察
			currentNode = child;
			child *= 2;
		}
		heap[currentNode] = lastElement;
	}
private:
	T* heap;//堆中元素存放在数组中
	int arrayLength;//数组容量
	int heapSize;//堆中元素个数
};
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值