堆,又可以称为优先级队列,这种数据结构插入和删除操作需要o(lgn)的时间复杂度,但是却能在o(1)的时间复杂度内取出最大值或最小值。
堆有最大堆和最小堆,最大堆中任意节点的关键码大于或等于它的左、右子女的关键码,相反,最小堆中任意节点的关键码小于或等于它的左、右子女的关键码。
如果堆的索引从0开始,则有如下关系式:
(1)左子女:2*i+1
(2)右子女:2*i+2
(3)父亲节点:向下取整((i-1)/2)
注:这是索引,给定一个数组长度,应该先通过len-1得到最后一个元素的索引,在通过第三条的公式开始调整。
堆的调整
(1)向下调整(删除堆顶元素)
/*copyright@ CNV && lsj*/ /*最小堆*/ #include<iostream> #include<algorithm> using namespace std; #define LEFT(i) (((i)<<1)+1) #define RIGHT(i) (((i)<<1)+2) #define CHILD(i) (((i)-1)>>1) typedef int RecType; /*从上往下调整 ---删除堆顶元素的调整*/ void SiftDown(RecType minHeap[],int low,int high) { int i = low; int j = LEFT(i); int base = minHeap[i]; while(j<=high){ //与小的比较,必须是j<high没有等号 if(j<high && minHeap[j+1]<minHeap[j])j++; if(base<=minHeap[j])break; else{ //上移 minHeap[i] = minHeap[j]; i = j; j = LEFT(i); } } minHeap[i] = base; };
(2)向上调整(插入元素,插入堆尾部)
/*从下往上调整 ---往堆中插入元素的调整*/ void SiftUp(RecType minHeap[],int high) { int j = high; int i = CHILD(j); int base = minHeap[j]; while(i>0){ if(minHeap[i]<=base)break; else{ //下移 minHeap[j] = minHeap[i]; j = i; i = CHILD(j); } } minHeap[j] = base; };
(3)堆排序
//逆序--》从大到小 void HeapSort(RecType arr[],int len) { int lastIndex = len -1;//由长度转换为索引 int beginIndex = (lastIndex-1)>>1; //下面构建了一个堆,o(nlogn) while(beginIndex>=0){ SiftDown(arr,beginIndex,len-1); --beginIndex; } //排序 for(int i=len-1;i>=0;i--){ swap(arr[0],arr[i]); SiftDown(arr,0,i-1); } };
STL中的堆(面试的时候用得着)
(1)利用make_heap构建堆(STL源码剖析P181)
STL提供堆结构,但却是幕后工作者,heap可以分为max_heap和min_heap。记住几个重要的接口:make_heap、push_heap、pop_heap、sort_heap。
这几个接口输入参数是这样的:
template<class RandomAccessIterator,Class Compare>
inline void make_heap(RandomAccessIterator first,RandomAccessIterator last, Compare comp);
注:通过给上面各函数传入不同的仿函数,分别构造最大堆还是最小堆
#include<iostream> #include<algorithm> #include<vector> #include<functional> using namespace std; int main() { int arr[]={0,1,2,3,4,8,9,3,5}; vector<int>ivec(arr,arr+9); //greater<int> comp;//最小堆 less<int> comp;//最大堆 make_heap(ivec.begin(),ivec.end(),greater<int>,comp); //9 5 8 3 4 0 2 3 1 for(int i=0;i<ivec.size();++i){ cout<<ivec[i]<<" "; } cout<<endl; ivec.push_back(7); push_heap(ivec.begin(),ivec.end(),comp); //9 7 8 3 5 0 2 3 1 4 for(int i=0;i<ivec.size();++i){ cout<<ivec[i]<<" "; } cout<<endl; pop_heap(ivec.begin(),ivec.end(),comp); cout<<ivec.back()<<endl;//9 ivec.pop_back(); sort_heap(ivec.begin(),ivec.end(),comp); //9 7 8 3 5 0 2 3 1 4 for(int i=0;i<ivec.size();++i){ cout<<ivec[i]<<" "; } system("pause"); return 0; }
(2)利用priority_queue构建堆(STL源码剖析P183,其实利用了上面的接口)
STL中提供了一种priority_queue,缺省情况下利用max_heap完成。STL中的priority_queue往往不被归类为容器,而是被归类为容器迭代器。
STL中的声明priority_queue声明如下:
template<class T,class Sequence = vector<T>,class Compare=less<typename Sequence::value_type>>
class priority_queue{
....
}
从上面的定义看出,STL的priority_queue采用vector实现,且Compare比较函数为仿函数less,我们可以传入greater,构造最小堆。
#include<iostream> #include<algorithm> #include<queue> using namespace std; int main() { int arr[]={0,1,2,3,4,5,8,9,3,5}; priority_queue<int> ipq(arr,arr+9); cout<<"size="<<ipq.size()<<endl; while(!ipq.empty()){ cout<<ipq.top()<<" "; ipq.pop(); } system("pause"); }
换一个仿函数就能构造最小堆:
#include<iostream> #include<algorithm> #include<queue> using namespace std; int main() { int arr[]={0,1,2,3,4,5,8,9,3,5}; priority_queue<int,vector<int>,greater<int>> ipq(arr,arr+9); cout<<"size="<<ipq.size()<<endl; while(!ipq.empty()){ cout<<ipq.top()<<" "; ipq.pop(); } system("pause"); return 0; }
(3)利用set(或者multiset)构建堆(剑指offer P169页)
#include<iostream> #include<algorithm> #include<functional> #include<set> using namespace std; typedef greater<int> Greater;//最大堆 typedef less<int> Less;//最小堆 typedef multiset<int,Less> MaxHeap; typedef multiset<int,Less>::iterator Iterator; int main() { int arr[]={0,1,2,3,4,8,9,3,5}; MaxHeap heap; for(int i=0;i<9;i++){ heap.insert(arr[i]); } for(Iterator it = heap.begin();it!=heap.end();++it){ cout<<*it<<" "; } cout<<endl; heap.erase(heap.begin()); heap.insert(10); for(Iterator it = heap.begin();it!=heap.end();++it){ cout<<*it<<" "; } system("pause"); return 0; }