[BinaryTree] 最大堆的类实现
堆的定义:
最大树(最小树):每个结点的值都大于(小于)或等于其子结点(如果有的话)值的树。
最大堆(最小堆):最大(最小)的完全二叉树。
最大堆的抽象数据结构:
1 class MaxHeap 2 { 3 private: 4 T* heapArray; //存放堆数据的数组 5 int CurrentSize;//当前堆中元素数目 6 int MaxSize; //堆中能容纳的最大元素数目 7 public: 8 MaxHeap(T* array,int num,int max); 9 virtual ~MaxHeap() 10 { 11 delete []heapArray; 12 } 13 void BuildHeap(); 14 void Swap(int pos_x,int pos_y); //交换位置x与y的元素 15 bool IsLeaf(int pos) const; //如果是叶子结点,返回true 16 int LeftChild(int pos) const; //返回左孩子位置 17 int RightChild(int pos) const; //返回右孩子位置 18 int Parent(int pos) const; //返回父结点位置 19 bool Remove(int pos,T& node); //删除给定下标元素 20 void SiftDown(int left); //筛选法函数,参数left表示开始处理的数组下标 21 void SiftUp(int position); //从position向上开始调整,使序列成为堆 22 bool Insert(const T& newNode); //向堆中插入新元素newNode 23 T& RemoveMax(); //从堆顶删除最大值 24 void print(); //输出函数 25 };
下面是一些简单函数的实现:
1 template<class T> 2 MaxHeap<T>::MaxHeap(T* array,int num,int max) 3 { 4 heapArray = array; 5 CurrentSize = num; 6 MaxSize = max; 7 } 8 template<class T> 9 void MaxHeap<T>::Swap(int pos_x,int pos_y) 10 { 11 T temp = heapArray[pos_x]; 12 heapArray[pos_x] = heapArray[pos_y]; 13 heapArray[pos_y] = temp; 14 } 15 template<class T> 16 bool MaxHeap<T>::IsLeaf(int pos) const 17 { 18 return (pos>=CurrentSize/2)&&(pos<CurrentSize); 19 } 20 template<class T> 21 int MaxHeap<T>::LeftChild(int pos) const 22 { 23 return pos*2+1; 24 } 25 template<class T> 26 int MaxHeap<T>::RightChild(int pos) const 27 { 28 return pos*2+2; 29 } 30 template<class T> 31 int MaxHeap<T>::Parent(int pos) const 32 { 33 if(pos == 0) 34 return -1; 35 return (pos-1)/2; 36 }
下面来看几个重要的操作的实现:
·堆的插入操作
(1)新元素添加到末尾(保持完全二叉树的性质);
(2)为了保持堆的性质,沿着其祖先的路径,自下而上依次比较和交换该结点与父结点的位置,直到重新满足堆的性质位置;
(3)在插入过程中,总是自下而上逐渐上升,最后停留在某个满足堆的性质的位置,故此过程又称为 “筛选”。
1 template<class T> 2 bool MaxHeap<T>::Insert(const T& newNode) 3 { 4 if(CurrentSize == MaxSize) 5 return false; 6 heapArray[CurrentSize] = newNode; 7 SiftUp(CurrentSize); 8 CurrentSize++; 9 return true; 10 }
·建堆过程
(1)首先将所有关键码放到一维数组中,这时形成的完全二叉树并不具备堆的特性,但是仅包含叶子结点的子树已经是堆 (即在有n个结点的完全二叉树中,当i > [n/2]-1时,以关键码Ki为根的子树已经是堆。
(2)这时从含有内部结点数最少的子树(这种子树在完全二叉树的倒数第二层,此时i = [n/2]-1开始,从右至左依次调整。
(3)对这一层调整完成之后,继续对上一层进行同样的工作,直到整个过程到达树根时,整棵完全二叉树就成为一个堆了
1 template<class T> 2 void MaxHeap<T>::BuildHeap() 3 { 4 for(int i = CurrentSize/2-1; i >= 0; i--) 5 { 6 SiftDown(i); 7 } 8 }
·堆的删除操作
(1)把最末端结点填入删除产生的空位(保持完全二叉树的性质)
(2)为了保持堆的性质,比较当前结点和其父节点的大小来决定向上还是向下“筛选”,直到重新满足堆的性质位置
1 template<class T> 2 bool MaxHeap<T>::Remove(int pos,T& node) 3 { 4 if(pos < 0 || pos >= CurrentSize) 5 return false; 6 node = heapArray[pos]; 7 heapArray[pos] = heapArray[--CurrentSize]; 8 if(heapArray[Parent(pos)] < heapArray[pos]) 9 { 10 SiftUp(pos); 11 } 12 else SiftDown(pos); 13 return true; 14 }
下面是删除堆顶元素的代码:
1 template<class T> 2 T& MaxHeap<T>::RemoveMax() 3 { 4 if(CurrentSize == 0) 5 { 6 cout<<"Can't delete"<<endl; 7 exit(1); 8 } 9 else 10 { 11 Swap(0,--CurrentSize); 12 if(CurrentSize>1) 13 { 14 SiftDown(0); 15 } 16 return heapArray[CurrentSize]; 17 } 18 }
下面是该类的核心代码:
向下筛选:
1 template<class T> 2 void MaxHeap<T>::SiftDown(int left) 3 { 4 int i = left; //标识父结点 5 int j = LeftChild(i); //标识关键码较小的子结点 6 T temp = heapArray[i]; //保存父结点 7 while(j < CurrentSize) //筛选 8 { 9 if((j < CurrentSize-1)&&(heapArray[j] < heapArray[j+1])) 10 {//若有右结点,且大于左结点 11 j++; //则j指向右结点 12 } 13 if(temp < heapArray[j]) 14 {//若父结点小于子结点的值则交换位置 15 heapArray[i] = heapArray[j]; 16 i = j; 17 j = LeftChild(j); 18 } 19 else break;//找到恰当的位置,跳出循环 20 } 21 heapArray[i] = temp; 22 }
向上筛选:
1 template<class T> 2 void MaxHeap<T>::SiftUp(int position) 3 { //从position开始向上调整 4 int tempos = position; 5 T temp = heapArray[tempos]; 6 while((tempos > 0)&&(temp > heapArray[Parent(tempos)])) 7 { 8 heapArray[tempos] = heapArray[Parent(tempos)]; 9 tempos = Parent(tempos); 10 } 11 heapArray[tempos] = temp; 12 }
测试函数:
1 int main() 2 { 3 int a[10] = {1,2,3,4,5,6,7,8,9,3}; 4 MaxHeap<int> S(a,10,20); 5 cout<<"构建最大堆:"<<endl; 6 S.BuildHeap(); 7 S.print(); 8 cout<<"插入元素10:"<<endl; 9 int newNode = 10; 10 S.Insert(newNode); 11 S.print(); 12 cout<<"删除堆顶元素:"<<endl; 13 S.RemoveMax(); 14 S.print(); 15 cout<<"删除pos = 1的元素:"<<endl; 16 int x; 17 S.Remove(1,x); 18 cout<<"x = "<<x<<endl; 19 S.print(); 20 return 0; 21 }
测试结果: