大堆和小堆的实现

堆的概念

如果有一个关键码的集合,将这个集合中的所有元素按照完全二叉树的顺序存储在一个一维数组中,并满足最小堆或最大堆的性质。

最小堆:任一结点的关键码均小于等于它的左右孩子的关键码,其中堆顶的元素最小。(任一路径中的元素降序排列)

最大堆:任一结点的关键码均大于等于它的左右孩子的关键码,其中堆顶的元素最大。(任一路径中的元素升序排列)

一、堆的创建(以最小堆为例)

堆的创建按照完全二叉树创建,底层是vector结构,存储的时候按照完全二叉树创建,从倒数第一个叶子结点开始到根结点对当前堆进行向下调整。

//创建最小堆--->时间复杂度O(Nlog2N)
	Heap(const T* array, size_t size)
	{
		//将数组中的元素存起来,按照完全二叉树
		for (size_t i = 0; i < size; ++i)
		{
			_array.push_back(array[i]);
		}
		_array.resize(size);//底层用vector搭载,插入元素后调整vector的大小
		
		//找完全二叉树中倒数第一个非叶子结点
		int Root = (size - 2) >> 1;
		for (; Root >= 0; Root--)
			_Adjustdown(Root);//向下调整,从第一个非叶子结点到根结点调整
	}

二、堆的插入

堆的插入在以建成的最小堆的后面插入,插入后,有可能破坏了堆的结构,从第一个非叶子结点的位置开始到根节点,对当前堆进行向上调整。

//插入元素
	void Insert(const T& data)
	{
		_array.push_back(data);
		int size = _array.size();

		int Root = (size - 2) >> 1;
		for (; Root >= 0; Root--)
			_Adjustup(Root);//向上调整,从第一个非叶子结点到根结点调整
	}

三、堆的删除

堆的删除是删除堆顶元素。

方法一:删除堆顶元素后,将堆中的其他元素向前搬移,但可行性差,调整次数多,可能破坏了堆的性质。

方法二:堆中最后一个元素与堆顶元素进行替换,删除最后一个元素,从堆顶开始,对堆进行向下调整。

//删除元素
	void Delete()
	{
		//堆为空
		if (_array.empty())
			return;

		//堆中含有一个元素,直接删除
		else if (_array.size() == 1)
		{
			_array.pop_back();
			return;
		}

		//堆中含有多个元素
		else
		{
			//1.将最后一个元素与堆顶元素交换
			int size = _array.size();
			swap(_array[0], _array[size - 1]);

			//2.删除最后一个元素
			if (!_array.empty())
				_array.pop_back();

			//3.从堆顶开始,向下调整
			int Root = (size - 2) >> 1;//找完全二叉树中倒数第一个非叶子结点
			for (; Root >= 0; Root--)
				_Adjustdown(Root);
		}
	}

封装调整函数

//向下调整
	void _Adjustdown(size_t pRoot)
	{
		size_t parent = pRoot;
		size_t child = parent * 2 + 1;//默认左孩子为左右孩子中最小的结点
		size_t size = _array.size();//计算堆中元素的个数

		//找出左右孩子中最小的结点
		if ((child + 1 < size) && (_array[child] > _array[child + 1]))//首先需要保证右孩子存在
			swap(_array[child], _array[child + 1]);

		while (child < size)
		{
			if (_array[child] < _array[parent])
			{
				swap(_array[child], _array[parent]);
				parent = child;//继续向下走
				child = parent * 2 + 1;
			}
			else
				break;
		}
	}

	//向上调整
	void _Adjustup(size_t pRoot)
	{
		size_t parent = pRoot;
		size_t child = parent * 2 + 1;//默认左孩子为最小孩子
		size_t size = _array.size();

		if ((child + 1 < size) && (_array[child] > _array[child + 1]))
			swap(_array[child], _array[child + 1]);

		while (child > 0)
		{
			parent = (size - 2) >> 1;
			if (_array[parent] > _array[child])
			{
				swap(_array[parent], _array[child]);
				child = parent;//向上继续
				parent = (child - 1) >> 1;
			}
			else
				break;
		}
	}
但这只是针对于小堆的情况,如何将一个小堆改为大堆呢?

利用仿函数来实现

template <class T>
struct Less
{
	bool operator()(const T& left, const T& right)
	{
		return left < right;
	}
};
template <class T>
struct Greater
{
	bool operator()(const T& left, const T& right)
	{
		return right < left;
	}
};

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

	//创建最小堆--->时间复杂度O(Nlog2N)
	Heap(const T* array, size_t size)
	{
		//将数组中的元素存起来,按照完全二叉树
		for (size_t i = 0; i < size; ++i)
		{
			_array.push_back(array[i]);
		}
		_array.resize(size);//底层用vector搭载,插入元素后调整vector的大小

		//找完全二叉树中倒数第一个非叶子结点
		int Root = (size - 2) >> 1;
		for (; Root >= 0; Root--)
			_Adjustdown(Root);//向下调整,从第一个非叶子结点到根结点调整
	}

	//插入元素
	void Insert(const T& data)
	{
		_array.push_back(data);
		int size = _array.size();

		int Root = (size - 2) >> 1;
		for (; Root >= 0; Root--)
			_Adjustup(Root);//向上调整,从第一个非叶子结点到根结点调整
	}

	//删除元素
	void Delete()
	{
		//堆为空
		if (_array.empty())
			return;

		//堆中含有一个元素,直接删除
		else if (_array.size() == 1)
		{
			_array.pop_back();
			return;
		}

		//堆中含有多个元素
		else
		{
			//1.将最后一个元素与堆顶元素交换
			int size = _array.size();
			swap(_array[0], _array[size - 1]);

			//2.删除最后一个元素
			if (!_array.empty())
				_array.pop_back();

			//3.从堆顶开始,向下调整
			int Root = (size - 2) >> 1;//找完全二叉树中倒数第一个非叶子结点
			for (; Root >= 0; Root--)
				_Adjustdown(Root);
		}
	}

private:
	//向下调整
	void _Adjustdown(size_t pRoot)
	{
		size_t parent = pRoot;
		size_t child = parent * 2 + 1;//默认左孩子为左右孩子中最小的结点
		size_t size = _array.size();//计算堆中元素的个数

		//找出左右孩子中最小的结点(小堆)/最大的结点(大堆)
		if ((child + 1 < size) && Compare()(_array[child+1],_array[child]))//首先需要保证右孩子存在
			swap(_array[child], _array[child + 1]);

		while (child < size)
		{
			if (Compare()(_array[child], _array[parent]))
			{
				swap(_array[child], _array[parent]);
				parent = child;//继续向下走
				child = parent * 2 + 1;
			}
			else
				break;
		}
	}

	//向上调整
	void _Adjustup(size_t pRoot)
	{
		size_t parent = pRoot;
		size_t child = parent * 2 + 1;//默认左孩子为最小孩子
		size_t size = _array.size();

		if ((child + 1 < size) && Compare()(_array[child + 1], _array[child]))
			swap(_array[child], _array[child + 1]);

		while (child > 0)
		{
			parent = (size - 2) >> 1;
			if (Compare()(_array[child],_array[parent]))
			{
				swap(_array[parent], _array[child]);
				child = parent;//向上继续
				parent = (child - 1) >> 1;
			}
			else
				break;
		}
	}

private:
	vector<T> _array;
};
也改为模板的模板参数

template <class T>
struct Less
{
	bool operator()(const T& left, const T& right)
	{
		return left < right;
	}
};
template <class T>
struct Greater
{
	bool operator()(const T& left, const T& right)
	{
		return right < left;
	}
};

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

	//创建最小堆--->时间复杂度O(Nlog2N)
	Heap(const T* array, size_t size)
	{
		//将数组中的元素存起来,按照完全二叉树
		for (size_t i = 0; i < size; ++i)
		{
			_array.push_back(array[i]);
		}
		_array.resize(size);//底层用vector搭载,插入元素后调整vector的大小

		//找完全二叉树中倒数第一个非叶子结点
		int Root = (size - 2) >> 1;
		for (; Root >= 0; Root--)
			_Adjustdown(Root);//向下调整,从第一个非叶子结点到根结点调整
	}

	//插入元素
	void Insert(const T& data)
	{
		_array.push_back(data);
		int size = _array.size();

		int Root = (size - 2) >> 1;
		for (; Root >= 0; Root--)
			_Adjustup(Root);//向上调整,从第一个非叶子结点到根结点调整
	}

	//删除元素
	void Delete()
	{
		//堆为空
		if (_array.empty())
			return;

		//堆中含有一个元素,直接删除
		else if (_array.size() == 1)
		{
			_array.pop_back();
			return;
		}

		//堆中含有多个元素
		else
		{
			//1.将最后一个元素与堆顶元素交换
			int size = _array.size();
			swap(_array[0], _array[size - 1]);

			//2.删除最后一个元素
			if (!_array.empty())
				_array.pop_back();

			//3.从堆顶开始,向下调整
			int Root = (size - 2) >> 1;//找完全二叉树中倒数第一个非叶子结点
			for (; Root >= 0; Root--)
				_Adjustdown(Root);
		}
	}

private:
	//向下调整
	void _Adjustdown(size_t pRoot)
	{
		size_t parent = pRoot;
		size_t child = parent * 2 + 1;//默认左孩子为左右孩子中最小的结点
		size_t size = _array.size();//计算堆中元素的个数

		//找出左右孩子中最小的结点(小堆)/最大的结点(大堆)
		if ((child + 1 < size) && Compare<T>()(_array[child + 1], _array[child]))//首先需要保证右孩子存在
			swap(_array[child], _array[child + 1]);

		while (child < size)
		{
			if (Compare<T>()(_array[child], _array[parent]))
			{
				swap(_array[child], _array[parent]);
				parent = child;//继续向下走
				child = parent * 2 + 1;
			}
			else
				break;
		}
	}

	//向上调整
	void _Adjustup(size_t pRoot)
	{
		size_t parent = pRoot;
		size_t child = parent * 2 + 1;//默认左孩子为最小孩子
		size_t size = _array.size();

		if ((child + 1 < size) && Compare<T>()(_array[child + 1], _array[child]))
			swap(_array[child], _array[child + 1]);

		while (child > 0)
		{
			parent = (size - 2) >> 1;
			if (Compare<T>()(_array[child], _array[parent]))
			{
				swap(_array[parent], _array[child]);
				child = parent;//向上继续
				parent = (child - 1) >> 1;
			}
			else
				break;
		}
	}

private:
	vector<T> _array;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值