堆操作及应用
1. 堆是一个完全二叉树,堆主要设计到的操作有插入,删除,堆化。
2. 堆的主要应用有堆排序:从小到大排序,使用大顶堆;从大到小排序,使用小顶堆。
3. 下面以大顶堆为例,给出实现代码:
View Code
1 #include <iostream> 2 #include <cassert> 3 4 using namespace std; 5 6 //大顶堆的实现 7 class Myheap 8 { 9 public: 10 Myheap(int hSize=DefaultSize) 11 { 12 if (hSize>DefaultSize) 13 { 14 heapSize=hSize; 15 } 16 else 17 { 18 heapSize=DefaultSize; 19 } 20 data=new int[heapSize]; 21 currentSize=0; 22 } 23 int leftChild(int pos) 24 { 25 return 2*pos+1; 26 } 27 int rightChild(int pos) 28 { 29 return 2*pos+2; 30 } 31 int parent(int i) 32 { 33 return (i-1)>>1; 34 } 35 36 //递归的处理方式 37 //void Max_Heapify(int pos) 38 //{ 39 // assert(pos<currentSize && pos>=0); 40 // int l=leftChild(pos); 41 // int r=rightChild(pos); 42 // int maxNumber=pos; 43 // if (l<currentSize) 44 // { 45 // maxNumber=data[l]>data[maxNumber] ? l:maxNumber; 46 // } 47 // if (r<currentSize) 48 // { 49 // maxNumber=data[r]>data[maxNumber] ? r:maxNumber; 50 // } 51 // if (maxNumber!=pos) 52 // { 53 // swap(data[pos],data[maxNumber]); 54 // Max_Heapify(maxNumber); 55 // } 56 //} 57 58 //使用循环替代尾递归 59 void Max_Heapify(int pos) 60 { 61 assert(pos<currentSize && pos>=0); 62 while (pos<currentSize) 63 { 64 int l=leftChild(pos); 65 int r=rightChild(pos); 66 int maxNumber=pos; 67 if (l<currentSize) 68 { 69 maxNumber=data[l]>data[maxNumber] ? l:maxNumber; 70 } 71 if (r<currentSize) 72 { 73 maxNumber=data[r]>data[maxNumber] ? r:maxNumber; 74 } 75 if (maxNumber!=pos) 76 { 77 swap(data[pos],data[maxNumber]); 78 } 79 else 80 { 81 break; 82 } 83 pos=maxNumber; 84 } 85 } 86 bool isFull() 87 { 88 return currentSize==heapSize; 89 } 90 91 //插入,将元素插入到末尾,然后将元素上浮到合适的位置 92 void insert(int elem) 93 { 94 if (isFull()) 95 { 96 cout<<"Heap is full!!!"<<endl; 97 return ; 98 } 99 data[currentSize++]=elem; 100 int pa=parent(currentSize-1); 101 int cur=currentSize-1; 102 while ((pa>=0) && (data[pa]<data[cur])) 103 { 104 swap(data[pa],data[cur]); 105 cur=pa; 106 pa=parent(cur); 107 } 108 } 109 110 //删除堆顶的元素 111 //将堆顶元素和最后一个元素交换,然后将堆顶元素下降 112 //到一个合适的位置 113 void deleteMax() 114 { 115 swap(data[currentSize-1],data[0]); 116 currentSize--; 117 Max_Heapify(0); 118 } 119 120 //打印 121 void print() 122 { 123 for (int i=0;i<heapSize;i++) 124 { 125 cout<<data[i]<<" "; 126 } 127 cout<<endl; 128 } 129 130 //堆排序 131 void HeapSort() 132 { 133 int i=currentSize-1; 134 for (;i>=1;i--) 135 { 136 swap(data[i],data[0]); 137 currentSize--; 138 Max_Heapify(0); 139 } 140 } 141 ~Myheap() 142 { 143 delete data; 144 } 145 protected: 146 private: 147 enum {DefaultSize=10}; 148 int heapSize; 149 int currentSize; 150 int *data; 151 }; 152 153 int main() 154 { 155 enum {length=11}; 156 int number[length]={12,23,2,34,5,123,6,0,1,34,0}; 157 Myheap myheap(length); 158 for(int i=0;i<length;i++) 159 { 160 myheap.insert(number[i]); 161 } 162 myheap.print(); 163 //int pPos=myheap.parent(length-1); 164 //for (int i=pPos;i>=0;i--) 165 //{ 166 // myheap.Max_Heapify(i); 167 //} 168 myheap.HeapSort(); 169 myheap.print(); 170 //myheap.deleteMax(); 171 //myheap.print(); 172 }
4. 堆的应用:
(1) 一个文件中包含了1亿个随机整数,如何快速的找到最大(小)的100万个数字?(时间复杂度:O(n lg k))。
答:若要找出100万个最大的数,需要维护一个含有100万元素的小顶堆。遍历1亿个数,如果某个数比小顶堆最小元素大,则用这个数替代小顶堆中的最小数,之后使小顶堆最小堆化。遍历结束之后,堆中存储的就是最大的100万个数。遍历一次的时间复杂度O(n),最小堆化O(lgk)。总的时间复杂度O(n lg k)。
5.
堆是一种非常基础但很实用的数据结构,很多复杂算法或者数据结构的基础就是堆,因而,了解和掌握堆这种数据结构显得尤为重要。
参考资料:
http://dongxicheng.org/structure/bitmap/
算法导论