堆是一种优先队列的实现。堆是一颗完全二叉树,所谓完全二叉树就是除了最后一层以外,其他层都是满的,而且最后一层所缺的叶结点都在右边。
在完全二叉树中,节点的序列号有如下关系:
特性4:设完全二叉树中一元素的序号为i,1<=i<=n.则有以下关系:
当i=1时,节点为树的根。若i>1,则该元素父节点为i/2(向下取整)
当2*i>n时,该元素无左孩子。否则,其左孩子为2*i。
当2*i+1>n时,无右孩子。否则,右孩子为2*i+1。
可以用公式化描述的树来高效存储完全二叉树。
1.定义
最大树:每个节点的值都大于或等于其子节点(如果有的话)值得树。(最大树可以多于二叉)
最大堆:最大的完全二叉树
由于堆是完全的二叉树,因而可以利用它的这一特性方便的用数组实现堆。
2.最大堆的插入
插入时,从最后一个元素向根结点方向搜索,找到对应的位置插入。O(log2n)
3.最大堆的删除
删除根结点,然后将最后一个叶结点放入根结点,并向下搜索,找到对应位置插入。O(log2n)
4.最大堆的初始化
可以用n个数组元素初始化最大堆。初始化的过程可以分为n个插入操作,所需时间为O(nlogn)。或者用下面这种方式,时间为O(n)
从第一个具有孩子的节点开始(即节点 n),这个元素在数组中的位置为 i = [n / 2 ],如果以这个元素为根的子树已是最大堆,则此时不需调整,否则必须调整子树使之成为堆。随后,继续检查以 i-1, i-2等节点为根的子树,直到检查到整个二叉树的根节点(其位置为1)。
5.实现
类定义:
1 template<typename T> 2 class MaxHeap 3 { 4 public: 5 MaxHeap(int MaxHeapSize = 10); 6 ~MaxHeap() 7 { 8 if (heap!=NULL) 9 { 10 delete[] heap; 11 heap = NULL; 12 } 13 } 14 15 int Size() const{ return CurrentSize; } 16 T Max() 17 { 18 if (CurrentSize==0) 19 { 20 throw OutofBounds(); 21 } 22 23 return heap[1]; 24 } 25 26 MaxHeap<T>& Insert(const T& x); 27 MaxHeap<T>& DeleteMax(T& x); 28 void Initialize(T a[], int size, int ArraySize); 29 private: 30 int CurrentSize; 31 int MaxSize; 32 T* heap; 33 };
操作:
1 template<typename T> 2 MaxHeap<T>::MaxHeap(int MaxHeapSize=10):MaxSize(MaxHeapSize),CurrentSize(0) 3 { 4 heap = new T[MaxSize + 1]; 5 } 6 7 template<typename T> 8 MaxHeap<T>& MaxHeap<T>::Insert(const T& x) 9 { 10 size_t index = ++CurrentSize; 11 //从最后一个叶结点向上搜索,找到对应的位置 12 while (index!=1&&x>heap[index/2]) 13 { 14 15 heap[index] = heap[index / 2];//父结点下移 16 index = index / 2;//移向父节点 17 } 18 19 heap[index] = x; 20 21 return *this; 22 } 23 24 template<typename T> 25 MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x) 26 { 27 if (CurrentSize==0) 28 { 29 throw OutofBounds(); 30 } 31 32 x = heap[1]; 33 T temp = heap[CurrentSize--]; 34 size_t index = 1;//父节点 35 size_t cindex = 2;//子节点 36 37 while(cindex<=CurrentSize) 38 { 39 //找到左右子节点的大值 40 if (cindex<CurrentSize&&heap[cindex]<heap[cindex+1]) 41 { 42 ++cindex; 43 } 44 //如果当前点大于子节点,插入此处子节点的父节点 45 if (temp>=heap[cindex]) 46 { 47 break; 48 } 49 50 heap[index] = heap[cindex];//父节点向下移 51 index = cindex;//父节点向下移 52 cindex *= 2;//子节点下移 53 } 54 55 heap[index] = temp; 56 return *this; 57 } 58 59 template<typename T> 60 void MaxHeap<T>::Initialize(T a[], int size, int ArraySize) 61 { 62 delete[] heap; 63 heap = new T[ArraySize + 1]; 64 MaxSize = ArraySize; 65 CurrentSize = size; 66 67 memcpy(heap+1, a, (CurrentSize)*sizeof(T)); 68 size_t cindex; 69 /************************************************************************/ 70 /*从第一个具有孩子的节点开始(即节点 n), 71 这个元素在数组中的位置为 i = [n / 2 ], 72 如果以这个元素为根的子树已是最大堆,则此时不需调整,否则必须调整子树使之成为堆。 73 随后,继续检查以 i-1, i-2等节点为根的子树,直到检查到整个二叉树的根节点(其位置为1)。*/ 74 /************************************************************************/ 75 for (size_t index = CurrentSize / 2; index >= 1;--index) 76 { 77 T temp = heap[index]; 78 79 cindex = 2 * index; 80 while (cindex<=CurrentSize) 81 { 82 if (cindex<CurrentSize&&heap[cindex + 1]>heap[cindex]) 83 { 84 ++cindex; 85 } 86 87 if (temp>heap[cindex]) 88 { 89 break; 90 } 91 92 heap[cindex/2] = heap[cindex]; 93 cindex *= 2; 94 } 95 96 heap[cindex / 2] = temp; 97 } 98 99 }
完整定义:
1 #ifndef MAXHEAP_H 2 #define MAXHEAP_H 3 4 #include<iostream> 5 #include<algorithm> 6 #include "exceptionerror.h" 7 using namespace std; 8 9 template<typename T> 10 class MaxHeap 11 { 12 public: 13 MaxHeap(int MaxHeapSize = 10); 14 ~MaxHeap() 15 { 16 if (heap!=NULL) 17 { 18 delete[] heap; 19 heap = NULL; 20 } 21 } 22 23 int Size() const{ return CurrentSize; } 24 T Max() 25 { 26 if (CurrentSize==0) 27 { 28 throw OutofBounds(); 29 } 30 31 return heap[1]; 32 } 33 34 MaxHeap<T>& Insert(const T& x); 35 MaxHeap<T>& DeleteMax(T& x); 36 void Initialize(T a[], int size, int ArraySize); 37 private: 38 int CurrentSize; 39 int MaxSize; 40 T* heap; 41 }; 42 43 template<typename T> 44 MaxHeap<T>::MaxHeap(int MaxHeapSize=10):MaxSize(MaxHeapSize),CurrentSize(0) 45 { 46 heap = new T[MaxSize + 1]; 47 } 48 49 template<typename T> 50 MaxHeap<T>& MaxHeap<T>::Insert(const T& x) 51 { 52 size_t index = ++CurrentSize; 53 //从最后一个叶结点向上搜索,找到对应的位置 54 while (index!=1&&x>heap[index/2]) 55 { 56 57 heap[index] = heap[index / 2];//父结点下移 58 index = index / 2;//移向父节点 59 } 60 61 heap[index] = x; 62 63 return *this; 64 } 65 66 template<typename T> 67 MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x) 68 { 69 if (CurrentSize==0) 70 { 71 throw OutofBounds(); 72 } 73 74 x = heap[1]; 75 T temp = heap[CurrentSize--]; 76 size_t index = 1;//父节点 77 size_t cindex = 2;//子节点 78 79 while(cindex<=CurrentSize) 80 { 81 //找到左右子节点的大值 82 if (cindex<CurrentSize&&heap[cindex]<heap[cindex+1]) 83 { 84 ++cindex; 85 } 86 //如果当前点大于子节点,插入此处子节点的父节点 87 if (temp>=heap[cindex]) 88 { 89 break; 90 } 91 92 heap[index] = heap[cindex];//父节点向下移 93 index = cindex;//父节点向下移 94 cindex *= 2;//子节点下移 95 } 96 97 heap[index] = temp; 98 return *this; 99 } 100 101 template<typename T> 102 void MaxHeap<T>::Initialize(T a[], int size, int ArraySize) 103 { 104 delete[] heap; 105 heap = new T[ArraySize + 1]; 106 MaxSize = ArraySize; 107 CurrentSize = size; 108 109 memcpy(heap+1, a, (CurrentSize)*sizeof(T)); 110 size_t cindex; 111 /************************************************************************/ 112 /*从第一个具有孩子的节点开始(即节点 n), 113 这个元素在数组中的位置为 i = [n / 2 ], 114 如果以这个元素为根的子树已是最大堆,则此时不需调整,否则必须调整子树使之成为堆。 115 随后,继续检查以 i-1, i-2等节点为根的子树,直到检查到整个二叉树的根节点(其位置为1)。*/ 116 /************************************************************************/ 117 for (size_t index = CurrentSize / 2; index >= 1;--index) 118 { 119 T temp = heap[index]; 120 121 cindex = 2 * index; 122 while (cindex<=CurrentSize) 123 { 124 if (cindex<CurrentSize&&heap[cindex + 1]>heap[cindex]) 125 { 126 ++cindex; 127 } 128 129 if (temp>heap[cindex]) 130 { 131 break; 132 } 133 134 heap[cindex/2] = heap[cindex]; 135 cindex *= 2; 136 } 137 138 heap[cindex / 2] = temp; 139 } 140 141 } 142 #endif
6.应用:堆排序,复杂度O(nlogn)
步骤:
1.用待排序数组初始化最大堆(最小堆)O(n)
2.从堆中逐一取出元素即可组成有序的堆O(logn)
1 #include<iostream> 2 #include "MaxHeap.h" 3 using namespace std; 4 5 void HeapSort(int a[],int n) 6 { 7 if (a == NULL||n<=0) 8 { 9 throw exception("Invalid input"); 10 } 11 12 MaxHeap<int> H(1); 13 H.Initialize(a, n, n); 14 15 for (int i = 0; i < n;++i) 16 { 17 H.DeleteMax(a[i]); 18 } 19 } 20 21 22 int main() 23 { 24 int a[] = { 0, 3, 4, 6, 0, 5, 8, 9 }; 25 cout << "Array to be sorted is:" << endl; 26 int length = sizeof(a) / sizeof(int); 27 for (int i = 0; i < length;++i) 28 { 29 cout << a[i] << ' '; 30 } 31 cout << endl; 32 33 HeapSort(a, length); 34 cout << "After sort:" << endl; 35 for (int i = 0; i < length; ++i) 36 { 37 cout << a[i] << ' '; 38 } 39 40 return 0; 41 }
最小堆:
1 #ifndef MinHeap_H 2 #define MinHeap_H 3 4 #include<iostream> 5 #include<algorithm> 6 #include "exceptionerror.h" 7 using namespace std; 8 9 template<typename T> 10 class MinHeap 11 { 12 public: 13 MinHeap(int MaxHeapSize = 10); 14 ~MinHeap() 15 { 16 if (heap!=NULL) 17 { 18 delete[] heap; 19 heap = NULL; 20 } 21 } 22 23 int Size() const{ return CurrentSize; } 24 T Min() 25 { 26 if (CurrentSize==0) 27 { 28 throw OutofBounds(); 29 } 30 31 return heap[1]; 32 } 33 34 MinHeap<T>& Insert(const T& x); 35 MinHeap<T>& DeleteMin(T& x); 36 void Initialize(T a[], int size, int ArraySize); 37 private: 38 int CurrentSize;//堆中元素的个数 39 int MaxSize;//堆的最大容量 40 T* heap; 41 }; 42 43 template<typename T> 44 MinHeap<T>::MinHeap(int MaxHeapSize=10):MaxSize(MaxHeapSize),CurrentSize(0) 45 { 46 heap = new T[MaxSize + 1];//元素从1开始存储 47 } 48 49 //从叶结点往上移 50 template<typename T> 51 MinHeap<T>& MinHeap<T>::Insert(const T& x) 52 { 53 size_t index = ++CurrentSize;//从新的叶结点开始,沿树上升 54 while (index!=1&&x<heap[index/2]) 55 { 56 heap[index] = heap[index / 2];//元素下移 57 index = index / 2;//移向父节点 58 } 59 60 heap[index] = x; 61 62 return *this; 63 } 64 65 template<typename T> 66 MinHeap<T>& MinHeap<T>::DeleteMin(T& x) 67 { 68 if (CurrentSize==0) 69 { 70 throw OutofBounds(); 71 } 72 73 x = heap[1];//最小值 74 T temp = heap[CurrentSize--];//最后一个叶结点值,找到它的待插位置 75 size_t index = 1; 76 size_t cindex = 2; 77 while(cindex<=CurrentSize) 78 { 79 if (cindex<CurrentSize&&heap[cindex]>heap[cindex+1])//找到左右节点的最小值 80 { 81 ++cindex; 82 } 83 //是否找到位置 84 if (temp<heap[cindex]) 85 { 86 break; 87 } 88 89 heap[index] = heap[cindex];//未找到,move down 90 index = cindex; 91 cindex *= 2; 92 } 93 94 heap[index] = temp; 95 return *this; 96 } 97 98 /************************************************************************/ 99 /* 从第一个具有孩子的节点开始 100 这个元素在数组中的位置为 i = [n / 2 ],如果以这个元素为根的子树已是最大堆,则此时不需调 101 整,否则必须调整子树使之成为堆。随后,继续检查以 i-1, i-2等节点为根的子树,直到检查 102 到整个二叉树的根节点(其位置为1)。 */ 103 /************************************************************************/ 104 105 template<typename T> 106 void MinHeap<T>::Initialize(T a[], int size, int ArraySize) 107 { 108 delete[] heap; 109 heap = new T[ArraySize + 1]; 110 MaxSize = ArraySize; 111 CurrentSize = size; 112 113 memcpy(heap+1, a, (CurrentSize)*sizeof(T)); 114 size_t cindex; 115 for (size_t index = CurrentSize / 2; index >= 1;--index) 116 { 117 T temp = heap[index]; 118 119 cindex = 2 * index; 120 while (cindex<=CurrentSize) 121 { 122 if (cindex<CurrentSize&&heap[cindex + 1]<heap[cindex]) 123 { 124 ++cindex; 125 } 126 127 if (temp<heap[cindex]) 128 { 129 break; 130 } 131 132 heap[cindex/2] = heap[cindex]; 133 cindex *= 2; 134 } 135 136 heap[cindex / 2] = temp; 137 } 138 139 } 140 #endif