堆及相关问题

来源:互联网 发布:双十一数据增长文献 编辑:程序博客网 时间:2024/06/06 07:30

堆的本质

vector+向下调整算法/向上调整算法
注意:这棵树是完全二叉树

向下调整算法:
已知条件:从一个节点开始向下调整,已知这个节点的左右子树已经是大堆或小堆
所以需要从第一个不是叶子节点的节点开始调整,而这个节点正好是最后一个节点的父节点。即i=(_v.size()-2)/2; i即是最后一个不是叶子节点的节点。

代码如下:
#include<iostream>using namespace std;#include<vector>template<class T>struct Greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T>struct Less{bool operator()(const T&x, const T& y){return x < y;}};template<class T,class Compare = Grater<T>>class Heap{public:Heap(){}Heap(const T* a,size_t n){//vector(动态数组) //vector reserve() 改变Capacity 配套用push_back 建议使用这个//vector resize()  改变size大小  a[i] = array[i]_v.reserve(n);for (size_t i = 0; i < n; i++){_v.push_back(a[i]);}//建堆for (int i = ((int)_v.size()-2)/2; i >=0 ; i--){AdjustDown(i);}//向下调正算法给的第一个就是父节点 向上给的第一个是叶子节点//向下调正算法 假设左右都是大堆 往下换 叶子节点不用算,从最后一个非叶子节点算起(倒着算)}void Push(const T& data){_v.push_back(data);//向上调正算法 只在push()这里用,把新填的叶子节不断向上换 直到符合大/小堆AdjustUp(_v.size() - 1);}void Pop(){//1.先将堆顶元素与堆的最后一个元素交换swap(_v[_v.size() - 1], _v[0]);//2.删除最后一个节点_v.pop_back();//3.向下调整AdjustDown(0);}T& Top(){return _v[0];}protected:void AdjustDown(int root){int parent = root;int child = root * 2 + 1;while (child<(int)_v.size()){if (child + 1 < (int)_v.size() && Compare()(_v[child+1], _v[child])){child++;}if (Compare()(_v[child], _v[parent])){swap(_v[parent], _v[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void AdjustUp(int child){int parent = (child - 1) / 2;//注意这里是child>0 不是parent>0while (child>0){if (Compare()(_v[parent], _v[child])){break;}else{swap(_v[child], _v[parent]);child = parent;parent = (child - 1) / 2;}}}private:vector<T> _v;};void TestHeap(){int array[3] = {5,4,3};//小堆Heap<int, Less<int>> h(array,sizeof(array)/sizeof(array[0]));cout<<h.Top()<<endl;h.Push(2);cout << h.Top() << endl;h.Pop();cout << h.Top() << endl;}
运行结果:

堆的应用:
1.TopK问题(海量数据处理问题)
问题:需要从十亿个数据里面找出最大的第K个数字
分析思路:
首先不能使用排序,数据太多了,计算机压根就存不下,我们可以先从十亿个数据里面拿出K个数据建立一个小堆,剩余的数据每次和Top比较,如果比Top大,就互换,然后用向下调整,保持是一个小堆,以此类推,处理完所有的数据。这里可以很明显的知道最大的前K个数据一定会进堆,而Top里面存的就是第K大的数据,这里不可以建立大堆,因为建立大堆,每次取出Top和其余数据比较,之后保持大堆,最后Top里面存的就是最大的那个数字。
代码如下:
void TopK(){int array1[3] = { 1, 2, 3 };Heap<int, Less<int>> h(array1, sizeof(array1) / sizeof(array1[0]));int array2[7] = { 4, 5, 6, 7, 8, 9, 10 };for (size_t i = 0; i <sizeof(array2) / sizeof(array2[0]); i++){if (array2[i]>h.Top()){h.Pop();h.Push(array2[i]);}}cout << h.Top() << endl;}
运行结果:



2.堆排序
升序(需要建大堆)

将0号下标的数据和最后一个数据交换,堆中数据个数减1,然后再向下调整,使得0号下标的数据是最大值,然后继续和堆中最后一个数据交换,继续将堆中元素个数减一。同理降序(小堆)。

代码如下:

void AdjustDown(int* heap,int n ,int root){int parent = root;int child = root * 2 + 1;while (child<n){if (child + 1 < n && heap[child + 1]>heap[child]){child++;}if (heap[child] >heap[parent]){swap(heap[parent], heap[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void HeapSort(int* a, int n){//建了个大堆for (int i = (n-2)/2; i >=0; i--){AdjustDown(a, n, i);}while (n>1){swap(a[0], a[n - 1]);--n;AdjustDown(a, n, 0);}}int main(){int a[3] = { 3, 2, 1 };HeapSort(a,sizeof(a)/sizeof(a[0]));for (size_t i = 0; i < 3; i++){cout << a[i] << " ";}cout << endl;system("pause");return 0;}
运行结果: