【数据结构】二叉堆(堆)

二叉堆(也叫堆)是一个部分排序的二叉树,其排序规则体现在它的堆序性质上:最大堆和最小堆,最大堆就是其对于任一节点,每个节点的键值都大于等于它的孩子节点,所以根节点键值最大。最小堆则相反。

堆是一棵完全二叉树,具备完全二叉树的性质,可以用一个数组表示而不需要指针,在起始位置为 0 的数组中任一位置 i 上的元素,其左儿子在位置 2*1+1 上,右儿子在左儿子的后面邻近位置上,它的父节点则在位置 (i-1)/2。因此,一个堆数据结构将由一个数组(不管键值是什么类型)、一个代表最大容量的整数以及当前的堆大小组成。

下面用C++ 来实现堆,并补充基于堆这一数据结构上的堆排序,不同于前面介绍的堆排序

1. 堆的类结构

template <class Elem>
class BinaryHeap
{
public:
	BinaryHeap(int MaxSize = 50);
	BinaryHeap(const BinaryHeap<Elem> &rhs);
	BinaryHeap(Elem *Array,int ElemNum,int MaxSize);
	~BinaryHeap(void);

	Elem *Sort(void);              //堆排序
	bool Add(const Elem &Item);    //添加元素
	Elem Remove(void);             //移除(堆顶)元素

	inline int GetSize(void);      //获取当前堆大小

protected:
	Elem *Data;                    
	int CurrentNum;
	const int MAX_SIZE;

	void HeapifyUp(int Node);            //向上调整堆  
	void HeapifyDown(int Node);          //向下调整堆

	inline int ParentOf(int Node);       //得到节点的父节点位置
	inline int LeftChildOf(int Node);    //得到节点的左儿子位置
};
2. 构造函数,拷贝构造函数,析构函数

//constructor function
template <class Elem>
BinaryHeap<Elem>::BinaryHeap(int MaxSize) :MAX_SIZE(MaxSize)
{
	Data = new Elem[MAX_SIZE];
	CurrentNum = 0;
}

//copy constructor function
template <class Elem>
BinaryHeap<Elem>::BinaryHeap(const BinaryHeap<Elem> &rhs) :MAX_SIZE(rhs.MAX_SIZE),CurrentNum(rhs.CurrentNum)
{
	Data = new Elem[MAX_SIZE];
	strcpy(Data,rhs.Data);
}

//array constructor
//将数组元素构建成最大堆
template <class Elem>
BinaryHeap<Elem>::BinaryHeap(Elem *Array,int MaxSize) :MAX_SIZE(MaxSize)
{
	Data = new Elem[MAX_SIZE];
	CurrentNum = ElemNum;

	for (int i = 0; i < ElemNum; ++i)
		Data[i] = Array[i];

	for (int i = ParentOf(CurrentNum - 1); i >= 0; --i)   //单步向前,数组从后往前,树从上到下调整
		HeapifyDown(i);
}

template <class Elem>
BinaryHeap<Elem>::~BinaryHeap(void)
{
	if (Data)
		delete[] Data;
}
3. 最大堆调整(最小堆只需修改比较即可)

template <class Elem>
int BinaryHeap<Elem>::ParentOf(int Node)
{
	//assert(Node > 0);
	return (Node - 1) / 2;
}

template <class Elem>
int BinaryHeap<Elem>::LeftChildOf(int Node)
{
	return (Node * 2) + 1;
}

//最大堆调整:从Node位置向上调整(数组向前,Node后不管)
template <class Elem>
void BinaryHeap<Elem>::HeapifyUp(int Node)
{
	int Current = Node;
	int Parent = ParentOf(Node);
	Elem Item = Data[Current];

	while (Current > 0)
	{
		if (Item > Data[Parent])
		{
			Data[Current] = Data[Parent];
			Current = Parent;
			Parent = ParentOf(Current);
		}
		else
			break;
	}
	Data[Current] = Item;
}

//最大堆调整:从Node位置向下调整(数组向后,Node前不管)
template <class Elem>
void BinaryHeap<Elem>::HeapifyDown(int Node)
{
	int Current = Node;
	int Child = LeftChildOf(Node);
	Elem Item = Data[Current];

	while (Child < CurrentNum)
	{
		if (Child < (CurrentNum - 1))
		{
			if (Data[Child] < Data[Child + 1])
				++Child;
		}

		if (Item < Data[Child])
		{
			Data[Current] = Data[Child];
			Current = Child;
			Child = LeftChildOf(Current);
		}
		else
			break;
	}
	Data[Current] = Item;
}
此处给出最大堆向上调整 HeapifyUp 演示:Node = ParentOf(CurrentNum - 1);


那什么时候向上调整,什么时候向下调整呢?很明显构建二叉堆的时候必须向上迭代(递归)调整,另外添加元素也必须向上调整;而移除堆顶元素(排序)后,则需要从上向下调整。也就是说当原数组不是二叉堆或是往后面添加元素打乱稳定时,需要从下往上调整;移除元素后,虽然数组不是二叉堆形式了,但是有一子树是不需移动的,只需调整另外一边子树。通俗点就是抽走前面的需要向下调整,后面添加需要向前调整。

4. 添加、移除元素

template <class Elem>
bool BinaryHeap<Elem>::Add(const Elem &Item)
{
	if (CurrentNum >= MAX_SIZE)
		return false;

	Data[CurrentNum] = Item;
	HeapifyUp(CurrentNum++);
	return true;
}

template <class Elem>
Elem BinaryHeap<Elem>::Remove(void)
{
	assert(CurrentNum > 0);

	Elem Temp = Data[0];
	Data[0] = Data[--CurrentNum];  //此处是用数组最后一个填补
	HeapifyDown(0);                //从上往下调整
	return Temp;

	//或选择键值大的儿子填补空位,重复该步骤直到最后一个元素填补了空位 	
}
5. 堆排序
template <class Elem>
inline int BinaryHeap<Elem>::GetSize(void)
{
	return CurrentNum;
}

template <class Elem>
Elem* BinaryHeap<Elem>::Sort(void)
{
	Elem *NewArray = new Elem[CurrentNum];

	//升序
	for (int ElemNum = CurrentNum - 1; ElemNum >= 0; --ElemNum)
		NewArray[ElemNum] = Remove();

	/*降序
	int ElemNum = CurrentNum;
	for (int i = 0; i < ElemNum; ++i)
		NewArray[i] = Remove();
	*/

	return NewArray;
}

相关文章

键树的基本概念 键树又称数字查找树(Digital Search Tree)。 它是一棵度大于等于2的树,树中的每个结...
[TOC] 基本概念 数据: 数据 是对客观事物的符号表示,在计算机科学中指所有能输入到计算机中并被计算机...
[TOC] 反证法 基本概念: 一般地,假设原命题不成立(即 在原命题的条件下,结论不成立),经过正确的推...
最近抽空整理了&quot;数据结构和算法&quot;的相关文章。在整理过程中,对于每种数据结构和算法...
[TOC] 矩阵在计算机图形学、工程计算中占有举足轻重的地位。在数据结构中考虑的是如何用最小的内存空间...
[TOC] 大O表示法:算法的时间复杂度通常用大O符号表述,定义为T[n] = O(f(n))。称函数T(n)以f(n)为界或...