PAT 1057. Stack (30) 求动态数据的中位数, 堆的插入和删除
程序员文章站
2022-07-15 12:30:39
...
/*************************
题意:
pop和push不难,关键在于如何快速求栈中元素的中位数。
************************/
/************************
解题思路与注意点:
树状数组显然是最简便的,但是感觉有点超纲
我自己用的是最大最小堆去处理,只不过中间走了很多坑。。。
想维持中位数,只要维持2个堆,左堆HeapL和右堆HeapR
HeapL是大顶堆,HeapR是小顶堆。
根据中位数的定义,偶数是第N/2个,奇数是第(N+1)/2个
则我们始终维持堆的个数为 0=<HeapL-HeapR<=1
这样HeapL的第一位即最大值就是中位数
那么如何维持呢这个等式被?根据以下规则:
1.当插入的数字小于等于当前中位数时,插入左堆
根据堆的插入方法(上溯法)插入后,判断一下左堆是否比右堆大2
如果大2,说明左堆数量过多,把左堆顶的元素放入右堆。
2.当插入的数字大于当前中位数时,插入右堆
插入后,判断一下右堆是否大于左堆
若大于,则右堆数量过多,把右堆堆顶的元素放入左堆
3.当Pop时,需要定位到那个pop元素的位置(在左堆还是右堆,在堆里的哪个位置)
并执行删除操作。
大家常见的堆的删除只基于堆顶元素删除,但这里必须进行任意位置的删除。
删除方法如下(这里假设为大顶堆):
先定位到pop元素所在的位置,然后将堆的最后一个元素A放到该位置
接着和父节点B比较一下大小
如果A比B小,那么进行向下调整。
如果A比B大,那么进行向上调整(这一步很关键)
直到A放到了合适的位置
堆的删除并调整完成后,判断一下左右堆的数量关系
再确定是否需要将左堆元素给右堆,或者右堆元素给左堆。
堆的插入删除复杂度只有logN,故总的复杂度为NlogN。
比较麻烦的点在于
1.要用2个数组去确定各点的位置,以便未来删除。该位置必须时刻更新。
2.每次插入删除时,都要确定一下左右堆的数量关系,这样才能使左堆的第一个就是中位数。
3.左堆和右堆各写了2个函数,很容易写错,l或者r之类的,下次lr用大写不要用小写
*************************/
/***********************
笔记:
*********************/
#include<iostream>
#include<stdio.h>
#include<string>
#include<vector>
#include<queue>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<stack>
#include<map>
#include<unordered_map>
using namespace std;
#define M 110000
#define INF 0x7fffffff
struct node{
int k;
int data;
};
struct node heapl[M],heapr[M];
int flag[M],place[M];
int len;
int ln=0,rn=0;
stack<int> heap;
stack<int> s;
void deleL(int p){
int i=p,j;
struct node Node= heapl[ln];
if(p>1 && Node.data > heapl[p/2].data){
i=p;
while(i>1){
j=i/2;
if(Node.data < heapl[j].data){
heapl[i] = heapl[j];
place[heapl[j].k]=i;
}
else
break;
i=j;
}
}
else{
while(i*2 < ln){
j=i*2;
if(i*2+1< ln && heapl[i*2+1].data > heapl[i*2].data){
j=i*2 + 1;
}
if(Node.data < heapl[j].data){
place[heapl[j].k] = i;
heapl[i]=heapl[j];
i=j;
}else
break;
}
}
heapl[i] = Node;
place[Node.k]=i;
ln--;
}
void deleR(int p){
int i=p,j;
struct node Node=heapr[rn];
if(p>1 && Node.data < heapr[p/2].data){
i=p;
while(i>1){
j=i/2;
if(Node.data < heapr[j].data){
heapr[i] = heapr[j];
place[heapr[j].k]=i;
}
else
break;
i=j;
}
}
else{
while(i*2 < rn){
j=i*2;
if(i*2+1< rn && heapr[i*2+1].data < heapr[i*2].data){
j=i*2 + 1;
}
if(Node.data>heapr[j].data){
place[heapr[j].k] = i;
heapr[i]=heapr[j];
i=j;
}
else break;
}
}
heapr[i] = Node;
place[Node.k]=i;
rn--;
}
void insertL(struct node Node){
ln++;
heapl[ln]=Node;
int i,j;
i=ln;
while(i>1){
j=i/2;
if(Node.data > heapl[j].data){
heapl[i] = heapl[j];
place[heapl[j].k]=i;
}
else
break;
i=j;
}
heapl[i]=Node;
place[Node.k]=i;
flag[Node.k]=0;
}
void insertR(struct node Node){
rn++;
heapr[rn]=Node;
int i,j;
i=rn;
while(i>1){
j=i/2;
if(Node.data < heapr[j].data){
heapr[i] = heapr[j];
place[heapr[j].k]=i;
}
else
break;
i=j;
}
heapr[i]=Node;
place[Node.k]=i;
flag[Node.k]=1;
}
void heappop(int delei){
if(flag[delei]==0){
deleL(place[delei]);
if(ln<rn){
flag[heapr[1].k]=0;
insertL(heapr[1]);
deleR(1);
}
}
else{
deleR(place[delei]);
if(ln-rn>=2){
flag[heapl[1].k]=1;
insertR(heapl[1]);
deleL(1);
}
}
}
int main(){
int n,j;
char cmd[28];
scanf("%d",&n);
int pushn;
len=0;
struct node Node;
int delei,i;
for(i=0;i<n;i++){
scanf("%s",cmd);
if(strcmp(cmd,"Pop")==0){
if(s.empty()){
printf("Invalid\n");
}
else{
printf("%d\n",s.top());
delei = heap.top();
heappop(delei);
heap.pop();
s.pop();
}
}
else if(strcmp(cmd,"Push")==0){
scanf("%d",&pushn);
if(ln==0 || pushn <= heapl[1].data){
flag[i]=0;
Node.data=pushn;
Node.k=i;
insertL(Node);
if(ln-rn>=2){
flag[heapl[1].k]=1;
insertR(heapl[1]);
deleL(1);
}
}
else{
flag[i]=1;
Node.data=pushn;
Node.k=i;
insertR(Node);
if(rn>ln){
flag[heapr[1].k]=0;
insertL(heapr[1]);
deleR(1);
}
}
heap.push(i);
s.push(pushn);
}
else if(strcmp(cmd,"PeekMedian")==0){
if(s.empty()){
printf("Invalid\n");
}
else{
printf("%d\n",heapl[1].data);
}
}
else printf("Invalid\n");
}
return 0;
}
下一篇: 别做过于详细的计划