数据结构与算法C++描述(12)---堆及最大堆
2017-10-30 19:22
731 查看
1、有关堆的相关概念
最大堆(最小树):最大(最小)的完全二叉树。最大树(最小树):每个节点的值都大于(小于)或等于其子节点(如果有的话)值的数。注意,最大(小)数不一定是二叉树。
完全二叉树:从满二叉树中删除k个元素,其编号为2h−i,1<=i<=k,所得到的二叉树成为完全二叉树。
一个最大堆如下图所示:
2、最大堆的相关操作
这里,最大堆的操作包括:堆顶元素的删除、向最大堆中插入一个元素、将一个数组转化为最大堆、最大堆的输出、获取最大堆的堆顶元素等。下面利用C++语言分别实现之。
2.1 最大堆类的声明与简单函数实现
最大堆类有3个私有成员:heap指针为堆中元素数组、CurrentSize为最大堆当前大小、MaxSize为最大堆的最大容量(通过构造函数赋值)。template <class T> class MaxHeap { public: MaxHeap(int HeapSize=10) { MaxSize = HeapSize; //堆的最大容量 heap = new T[MaxSize + 1]; //新建堆元素数组 CurrentSize = 0; //设置当前容量为0 } ~MaxHeap() { delete[] heap; } //获取当前堆的大小 int Size() const { return CurrentSize; } //获取堆顶元素 T Max() { if (CurrrentSize == 0) throw OutOfRange(); else return heap[1]; //heap中的元素从1开始 } //向堆中插入元素x,并返回操作之后的堆 MaxHeap<T> &Insert(const T &x); //删除堆顶元素,并返回操作后的堆 MaxHeap<T> &DeleteMax(T &x); //初始化函数,将一个数组构造成最大堆,size为数组中元素个数,ArrayMaxSize为数组最大容量 MaxHeap<T> &Initialize(T a[], int size, int ArraySize); //输出最大堆 void Output(ostream &out) { for (int i = 1; i <= CurrentSize; i++) out << heap[i] << " "; cout << endl; } private: T *heap; //元素数组,索引值从1开始 int CurrentSize, //当前堆大小 MaxSize; //堆的最大规模 };
2.2 向最大堆中插入一个元素
若插入一个到最大堆中,可以先将元素放入最大堆的末尾,然后在调整插入元素后的完全二叉树,使其成为一个最大堆。调整规则可以根据最大堆的定义来设定。由于二叉树中最后一个父节点的索引值满足[CurrentSize/2],其中CurrentSize为当前二叉树中元素个数。当插入一个元素后,原二叉树中元素个数加1。此时,可以从最后一个父节点开始,依次访问每个父节点,直到访问完第一个父节点(即根节点)。在访问每个父节点时,分别判断父节点与其左右孩子的大小关系。先判断其左孩子(若存在),若大于父节点,则与父节点交换;再访问其右孩子(若存在),若大于父节点,则交换。
//交换a和b template <class T> void Swap(T &a, T &b) { T c; c = a; a = b; b = c; } //向堆中插入元素x,并返回操作之后的堆 template <class T> MaxHeap<T> &MaxHeap<T>::Insert(const T &x) { if (CurrentSize == MaxSize) throw NoMerm(); else { heap[CurrentSize + 1] = x; //取x的值作为堆的最后一个元素 CurrentSize++; int root_i = (CurrentSize + 1) / 2; //取当前元素的根节点 //依次访问各个根节点,调整成为最大堆 while (root_i > 0) { //若左孩子(若存在)大于根节点,则交换 if (2 * root_i <= CurrentSize && heap[root_i] < heap[2 * root_i]) Swap(heap[root_i], heap[2 * root_i]); //若右孩子(若存在)大于根节点,则交换 if (2 * root_i + 1 <= CurrentSize && heap[root_i] < heap[2 * root_i+1]) Swap(heap[root_i], heap[2 * root_i + 1]); root_i--; //访问下一个根节点 } } return *this; }
2.3 删除堆顶元素,并赋给x,返回操作后的最大堆
删除堆顶元素后,为了节省重建最大堆的时间,可以将最大堆的最后一个元素移至堆顶。从堆顶开始,依次访问各个父节点,直至最后一个父节点(索引值为[(CurrentSize-1)/2])。在依次访问父节点过程中,将父节点与其左右孩子(若存在)中的最大者交换。//删除堆顶元素,赋给x,并返回操作后的堆 template <class T> MaxHeap<T> &MaxHeap<T>::DeleteMax(T &x) { if (CurrentSize == 0) throw OutOfRange(); else { x = heap[1]; //将堆顶元素给z heap[1] = heap[CurrentSize]; //将堆的最后一个元素移到堆顶 CurrentSize--; //当前堆大小-1 int Max_root_i = CurrentSize / 2; //当前最后一个根节点的索引值 int i = 1; while (i <= Max_root_i &&(2*i<=CurrentSize || 2*i+1<=CurrentSize)) { //不满足最大堆的定义 if (heap[i] < heap[2 * i] || heap[i] < heap[2 * i + 1]) { int max_child_index; //寻找左右孩子最大者的索引值 if (2 * i < CurrentSize) //根节点左右孩子都存在 //最大者索引值为左右孩子较大者的索引值 max_child_index = (heap[2 * i] >= heap[2 * i + 1]) ? 2 * i : 2 * i + 1; else if (2 * i <= CurrentSize) //仅存在左孩子,最大值索引值为左孩子 max_child_index = 2 * i; //根节点和左右孩子的较大者交换 Swap(heap[i], heap[max_child_index]); } i++; //访问下一个根节点 } } return *this; }
2.4 将数组转化成最大堆
对于一个给定数组,首先删除原来heap数组中的数据,然后为heap分配与给定数组大小相同的空间,并将数组元素赋值给heap数组。接着,采用了正反向两次访问父节点的策略,将heap中元素调整成为最大堆。首先从最后一个父节点开始(索引值为[CurrentSize/2]),依次访问父节点,直到第一个父节点(即根节点)。在访问每个父节点时,分别判断父节点与其左右孩子的大小关系。先判断其左孩子(若存在),若大于父节点,则与父节点交换;再访问其右孩子(若存在),若大于父节点,则交换。
接着,再从第一个父节点开始,直至访问到最后一个父节点。期间,元素调整策略相同。
//将一个数组转化为最大堆,size为数组中元素个数,ArrayMaxSize为数组最大容量 template <class T> MaxHeap<T> &MaxHeap<T>::Initialize(T a[], int size, int ArrayMaxSize) { delete[] heap; //删除heap中的所有元素 MaxSize = ArrayMaxSize; //最大容量 heap = new T[size]; CurrentSize = size-1; //当前容量为数组中元素个数 for (int i = 1; i < size; i++) //将数组a的值赋给数组heap heap[i] = a[i]; int root_i = CurrentSize / 2; //最后一个根节点索引值 while (root_i>0) //从后向前依次访问每个根节点 { //若根节点小于其左孩子,则交换 if (2 * root_i <= CurrentSize && heap[root_i] < heap[2 * root_i]) Swap(heap[root_i], heap[2 * root_i]); //若根节点小于其右孩子,则交换 if (2 * root_i + 1 <= CurrentSize && heap[root_i] < heap[2 * root_i + 1]) Swap(heap[root_i], heap[2 * root_i + 1]); //访问上一个根节点 root_i--; } int i = 1; while (i <= int(CurrentSize / 2)) //从前向后依次访问每个根节点 { //若根节点小于其左孩子,则交换 if (2 * i <= CurrentSize && heap[i] < heap[2 * i]) Swap(heap[i], heap[2 * i]); //若根节点小于其右孩子,则交换 if (2 * i + 1 <= CurrentSize && heap[i] < heap[2 * i + 1]) Swap(heap[i], heap[2 * i + 1]); //访问下一个根节点 i++; } return *this; }
2.5 重载操作符“<<”
//重载输出操作符"<<" template <class T> ostream &operator<<(ostream &out, MaxHeap<T> &MH) { MH.Output(out); return out; }
2.6 测试
#include "MaxHeap.h" int main() { try { int a[] = { 12,32,43,56,74,13,9 }; MaxHeap<int> MH; //测试Insert函数 MH.Insert(3).Insert(4).Insert(10).Insert(6); cout <<"当前最大堆为: " << MH<<endl; //测试DeleteMax函数 int x; MH.DeleteMax(x); cout << "删除元素为: " << x << endl << endl; cout <<"当前最大堆为: " << MH << endl; //测试Initialize函数 MH.Initialize(a, 7, 10); cout << "输入的数组为: "; for (int i = 1; i < 7; i++) cout << a[i] << " "; cout<< endl << endl; cout <<"转化为最大堆为: "<< MH << endl; } catch (OutOfRange) { cout << "越界!!!" << endl; } catch (NoMerm) { cout << "内存不足!!!" << endl; } }
测试结果:
相关文章推荐
- 数据结构与算法C++描述(5)---模拟指针及模拟链表
- 数据结构与算法分析:C++描述(Mark Allen Weiss) 和 数据结构(刘大有)笔记
- 数据结构与算法--求最大子列和问题
- 数据结构与算法——最短路径Dijkstra算法的C++实现
- 数据结构与算法——求最大子矩阵问题
- C++队列模拟银行排队叫号(加州大学伯克利分校计算机专业数据结构与算法作业)
- 数据结构——算法之(040)(最大公约数问题)
- 数据结构与算法-求子数组的最大和
- 算法与数据结构基础8:C++实现有向图——邻接表存储
- 【数据结构与算法】寻找最大的K个数
- 数据结构与算法 Java语言描述-源代码
- 【数据结构与算法】二维数组 最大矩形和
- 数据结构笔记(C++描述)
- 算法和数据结构是程序的第一秘诀,缺之算法和数据结构是编程的最大原因
- 数据结构与算法-排序篇-Python描述
- 数据结构与算法——散列表类的C++实现(分离链接散列表)
- 数据结构与算法:python语言描述学习笔记Part3_2
- Java 数据结构与算法(1)-------------求最大公约数
- PTA 数据结构与算法题目集 5-1 最大子列和问题
- 【读书笔记】数据结构与算法经典问题解析——Java语言描述【树】