一.集合(set):集合天生就有从小到大排序的性质,所以他就是一个小顶堆。如果要做一个大顶堆怎么办?可以先将每个元素都取负数,排完序后再取反。
int main(){
typedef pair<int,int>p2;
set<p2>s;//定义pair的目的是为了能让集合可以插入重复的元素,第一个int代表插入的元素,第二个代表插入的编号。
int tot=0;
s.insert(p2(3,tot++));//插入指令
s.insert(p2(4,tot++));
s.insert(p2(5,tot++));
for(auto x:s){//x可以类似为一个指针,遍历s的所有元素。
cout<<x.first<<" "<<x.second<<endl;
}
s.erase(s.begin());//这等价于小顶堆中的pop操作。
s.insert(p2(10,tot++));//push操作。
}
二.题目练习:
1.
2.维护数据流中的中位数(对顶堆):
也就是,我们建立前后两个顶堆。如果是奇数个,我们只需要取大顶堆的堆顶元素。否则,就取两个堆的堆顶元素的均值。 这是维护的过程。
如何插入?判断一下新元素和s1堆顶元素的大小关系:如果我比堆顶s1的堆顶还小,那我就去s1中,如果比最大值大,那就去s2;
class MedianFinder {
public:
typedef pair<int,int>p2;
set<p2>s1,s2;
int tot;
MedianFinder() {
tot=0;
}
void addNum(int num) {
if(s1.size()==0||num<-s1.begin()->first){ s1.insert(p2(-num,tot++));}
else s2.insert(p2(num,tot++));
int n1=(s1.size()+s2.size()+1)/2;
if(n1==s1.size())return;
if(n1<s1.size()){
s2.insert(p2(-s1.begin()->first,tot++));
s1.erase(s1.begin());
}
if(n1>s1.size()){
s1.insert(p2(-s2.begin()->first,tot++));
s2.erase(s2.begin());
}
return;
}
double findMedian() {
if((s1.size()+s2.size())%2){
return -s1.begin()->first;
}
else {return(-s1.begin()->first+s2.begin()->first)/2.0;}
}
};
3.合并K个升序链表:
思路:既然链表都已经是有序的了,我们只需要考虑每个链表的头部,并予以比较。