Heap是一种数据结构具有以下的特点:
1)完全二叉树
2)heap中存储的值是偏序
Min-heap: 父节点的值小于或等于子节点的值
Max-heap: 父节点的值大于或等于子节点的值


由于堆存储在下标从0开始计数的数组中,因此,在堆中给定下标为i的结点时:
(1)如果i=0,结点i是根结点,无父结点;否则结点i的父结点为结点(i-1)/2;
(2)如果2i+1>n-1,则结点i无左子女;否则结点i的左子女为结点2i+1;
(3)如果2i+2>n-1,则结点i无右子女;否则结点i的右子女为结点2i+2。
插入一个元素:新元素被加入到heap的末尾,然后更新树以恢复堆的次序。
每次插入都是将新数据放在数组最后。可以发现从这个新数据的父结点到根结点必然为一个有序的数列,现在的任务是将这个新数据插入到这个有序数据中――这就类似于直接插入排序中将一个数据并入到有序区间中。需要从下网上,与父节点的关键码进行比较,对调。

按定义,堆中每次都删除第0个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,堆的元素个数-1,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。

对于叶子节点,不用调整次序,根据满二叉树的性质,叶子节点比内部节点的个数多1.所以i=n/2 -1 ,不用从n开始。就是从最后一个有叶子结点的结点开始。
如果从小到大排序,创建大堆建好之后堆中第0个数据是堆中最大的数据。取出这个数据,放在数组最后一个元素上,将当前元素数-1,再执行下堆的删除操作。这样堆中第0个数据又是堆中最大的数据,重复上述步骤直至堆中只有一个数据时,数组元素就已经有序。
#include <iostream> using namespace std; const int DefaultSize = 50; template<typename T> class MinHeap { public: //构造函数:建立空堆 MinHeap(int sz=DefaultSize) { maxHeapSize = (DefaultSize < sz) ? sz : DefaultSize; heap = new T[maxHeapSize]; currentSize = 0; } //构造函数通过一个数组建立堆 MinHeap(T arr[],int n) { maxHeapSize = (DefaultSize < n) ? n : DefaultSize; heap = new T[maxHeapSize]; for(int i=0;i<n;i++) { heap[i] = arr[i]; } currentSize = n; int currentPos = (currentSize - 2) / 2; //找最初调整位置:最后分支结点 while (currentPos>=0) //自底向上逐步扩大形成堆 { siftDowm(currentPos, currentSize - 1); //局部自上向下下滑调整 currentPos--; //再向前换一个分支结点 } } //将x插入到最小堆中 bool Insert(const T& x) { if(currentSize==maxHeapSize) { cout << "Heap Full!" << endl; return false; } heap[currentSize] = x; //插入 siftUp(currentSize); //向上调整 currentSize++; //堆计数+1 return true; } bool RemoveMin(T& x) { if(!currentSize) { cout << "Heap Empty!" << endl; return false; } x = heap[0]; //返回最小元素 heap[0] = heap[currentSize - 1]; //最后元素填补到根结点 currentSize--; siftDowm(0, currentSize - 1); //自上向下调整为堆 return true; } void output() { for(int i=0;i<currentSize;i++) { cout << heap[i] << " "; } cout << endl; } protected: //最小堆的下滑调整算法 void siftDowm(int start, int end) //从start到end下滑调整成为最小堆 { int cur = start; int min_child = 2 * cur + 1; //先记max_child是cur的左子女位置 T temp = heap[cur]; while (min_child <=end) { if (min_child<end&&heap[min_child]>heap[min_child + 1]) //找到左右孩子中最小的一个 min_child++; if(temp<=heap[min_child]) break; else { heap[cur] = heap[min_child]; cur = min_child; min_child = 2 * min_child + 1; } } heap[cur] = temp; } //最小堆的上滑调整算法 void siftUp(int start) //从start到0上滑调整成为最小堆 { int cur = start; int parent = (cur - 1) / 2; T temp = heap[cur]; while (cur>0) { if(heap[parent]<=temp) break; else { heap[cur] = heap[parent]; cur = parent; parent = (parent - 1) / 2; } } heap[cur] = temp; //回放temp中暂存的元素 } private: //存放最小堆中元素的数组 T* heap; int currentSize; //最小堆中当前元素个数 int maxHeapSize; //最小堆最多允许元素个数 }; //------------------------主函数------------------------- int main(int argc, char* argv[]) { MinHeap<int> h; h.Insert(8); h.Insert(5); h.Insert(7); h.Insert(9); h.Insert(6); h.Insert(12); h.Insert(15); h.output(); int out; cout << static_cast<int> (h.RemoveMin(out)) << endl; h.output(); int arr[10] = { 15,19,13,12,18,14,10,17,20,11 }; MinHeap<int> h1(arr,10); h1.output(); }
来源:博客园
作者:WindSun
链接:https://www.cnblogs.com/WindSun/p/11444446.html