二叉堆(也叫堆)是一个部分排序的二叉树,其排序规则体现在它的堆序性质上:最大堆和最小堆,最大堆就是其对于任一节点,每个节点的键值都大于等于它的孩子节点,所以根节点键值最大。最小堆则相反。
堆是一棵完全二叉树,具备完全二叉树的性质,可以用一个数组表示而不需要指针,在起始位置为 0 的数组中任一位置 i 上的元素,其左儿子在位置 2*1+1 上,右儿子在左儿子的后面邻近位置上,它的父节点则在位置 (i-1)/2。因此,一个堆数据结构将由一个数组(不管键值是什么类型)、一个代表最大容量的整数以及当前的堆大小组成。
下面用C++ 来实现堆,并补充基于堆这一数据结构上的堆排序,不同于前面介绍的堆排序
堆的类结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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) ; };
构造函数,拷贝构造函数,析构函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 template <class Elem >BinaryHeap <Elem>: :BinaryHeap(int MaxSize) :MAX_SIZE(MaxSize){ Data = new Elem[MAX_SIZE]; CurrentNum = 0 ; } 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); } template <class Elem >BinaryHeap <Elem>: :BinaryHeap(Elem *Array, int ElemNum, 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; }
最大堆调整(最小堆只需修改比较即可)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 template <class Elem > int BinaryHeap <Elem >::ParentOf (int Node ){ return (Node - 1 ) / 2 ; } template <class Elem > int BinaryHeap <Elem >::LeftChildOf (int Node ){ return (Node * 2 ) + 1 ; } 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前不管)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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);
此处给出最大堆向上调整 HeapifyUp 演示:Node = ParentOf(CurrentNum - 1);
那什么时候向上调整,什么时候向下调整呢?很明显构建二叉堆的时候必须向上迭代(递归)调整,另外添加元素也必须向上调整;而移除堆顶元素(排序)后,则需要从上向下调整。也就是说当原数组不是二叉堆或是往后面添加元素打乱稳定时,需要从下往上调整;移除元素后,虽然数组不是二叉堆形式了,但是有一子树是不需移动的,只需调整另外一边子树。通俗点就是抽走前面的需要向下调整,后面添加需要向前调整。
添加、移除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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; }
堆排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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(); return NewArray; }