b. 高性能定时器 多个定时器,需要每隔很短的时间(比如1秒)扫描一遍,查询哪个任务时间到了,需要开始执行,这样有时候很多扫描是徒劳的,如果任务列表很长,扫描很耗时。采用小顶堆,最先执行的放在堆顶,就只需要在堆顶时间到时执行堆顶任务即可,避免无谓的扫描。
a. 针对静态数据(数据不变) 建立大小为K的小顶堆,遍历数组,数组元素与堆顶比较,比堆顶大,就把堆顶删除,并插入该元素到堆 b. 针对动态数据(数据不断插入更新的) 在动态数据插入的时候就与堆顶比较,看是否入堆,始终维护这个堆,需要的时候直接返回,最坏O(n*lgK)
/** * @description: 用堆求最大的k个元素 * @author: michael ming * @date: 2019/5/30 0:06 * @modified by: */ #include "heap.cpp" int main() { int i = 0; const int len = 10; int data[len] = {2, 8, 1, 7, 12, 24, 6, 10, 90, 100}; MinHeap intheap(5);//求前5大元素 while(!intheap.full()) { if(i < len) intheap.insert(data[i++]); } while(i < len) { if(data[i] > intheap.getMin()) { intheap.delMin(); intheap.insert(data[i]); } i++; } intheap.sort(); intheap.print(); return 0; }静态数据:先排序,中间位置的数就是中位数 动态数据:动态变化,中位数位置总在变动,每次都排序的话,效率很低,借助堆可以高效的解决。 插入数据到某个堆里后,两个堆数据量(应相等或者大堆比小堆多1)若不满足括号要求,则将某个堆的堆顶元素移动到另一个堆,直到满足括号要求,堆化复杂度O(lgn),大堆的堆顶就是中位数,求中位数复杂度O(1)。
同理,可以求99%响应时间(就是大于前面99%数据的那个数据) 跟上面过程类似,只是大堆中保存 n x 0.99 个数据,小堆存 n x 0.01 个数据
/** * @description: 利用大小两个堆求中位数 * @author: michael ming * @date: 2019/5/30 20:37 * @modified by: */ #include <algorithm> #include <iostream> #include <vector> using namespace std; void printVec(vector<int> v) { for (int i = 0; i < v.size(); ++i) cout << v[i] << " "; cout << endl; } int main() { const int len = 7; int staticArr[len] = {7,-1,9,0,8,77,24};//-1,0,7,*8*,9,24,77 vector<int> maxheap, minheap; for(int i = 0; i < len; ++i) { if(maxheap.empty()) { maxheap.push_back(staticArr[i]); continue; } //----选择插入哪个堆----- if(!maxheap.empty() && staticArr[i] <= maxheap[0]) { maxheap.push_back(staticArr[i]); push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆 } else if(!maxheap.empty() && staticArr[i] > maxheap[0]) { minheap.push_back(staticArr[i]); push_heap(minheap.begin(),minheap.end(),greater<int>());//小堆,采用 > } //----平衡两个堆的节点比列---- if(maxheap.size() > minheap.size() && maxheap.size() - minheap.size() > 1) { minheap.push_back(maxheap[0]);//大堆顶进入小堆 push_heap(minheap.begin(),minheap.end(),greater<int>()); pop_heap(maxheap.begin(),maxheap.end());//堆顶到末尾了 maxheap.pop_back();//删除到末尾的"堆顶" } else if(maxheap.size() < minheap.size()) { maxheap.push_back(minheap[0]); push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆 pop_heap(minheap.begin(),minheap.end(),greater<int>()); minheap.pop_back(); } } if(maxheap.size()) cout << "中位数为:" << maxheap[0] << endl; return 0; }stl heap操作:http://www.cplusplus.com/reference/algorithm/pop_heap/ 对上面程序稍加改造,对动态数据进行求解中位数
/** * @description: 中位数求解,利用大小堆,动态数据插入 * @author: michael ming * @date: 2019/5/30 23:13 * @modified by: */ #include <algorithm> #include <iostream> #include <vector> using namespace std; void printVec(vector<int> v) { for (int i = 0; i < v.size(); ++i) cout << v[i] << " "; cout << endl; } int main() { int num; vector<int> maxheap, minheap, allnum; while(cin >> num) { allnum.push_back(num); if(maxheap.empty()) { maxheap.push_back(num); cout << "排序后的数组:" << endl; printVec(allnum); cout << "中位数为:" << maxheap[0] << endl; continue; } //----选择插入哪个堆----- if(!maxheap.empty() && num <= maxheap[0]) { maxheap.push_back(num); push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆 } else if(!maxheap.empty() && num > maxheap[0]) { minheap.push_back(num); push_heap(minheap.begin(),minheap.end(),greater<int>());//小堆,采用 > } //----平衡两个堆的节点比列---- if(maxheap.size() > minheap.size() && maxheap.size() - minheap.size() > 1) { minheap.push_back(maxheap[0]);//大堆顶进入小堆 push_heap(minheap.begin(),minheap.end(),greater<int>()); pop_heap(maxheap.begin(),maxheap.end());//堆顶到末尾了 maxheap.pop_back();//删除到末尾的"堆顶" } else if(maxheap.size() < minheap.size()) { maxheap.push_back(minheap[0]); push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆 pop_heap(minheap.begin(),minheap.end(),greater<int>()); minheap.pop_back(); } sort(allnum.begin(),allnum.end()); cout << "排序后的数组:" << endl; printVec(allnum); if(maxheap.size()) cout << "中位数为:" << maxheap[0] << endl; } return 0; }