PAT(甲级)渡劫(十七)-Stack(30)
题目大意:
题意:有三种操作,分别是
1.Push key:将key压入stack
2.Pop:将栈顶元素取出栈
3.PeekMedian:返回stack中第(n+1)/2个小的数(中位数)
算法思想:
分桶法的基本思路是分治,在一开始的暴力解法中,我们可以认为Count数组是一个大的桶,这个大的桶里有510^5个小桶,每个小桶能装一个数,在分桶法中,我们建立多个大桶,每个桶中又有小桶,比如,我们建立多个500个大桶,每个桶的容量是100,同时记录每个大桶中存放的数据的个数,在查找的时候我们可以通过每个大桶中元素的快速定位到放置中位数的那个小桶。当然你可以认为这是一种HASH,hash(key) = key/10。设每个大桶中含有k个小桶,共有m个大桶,mk = n为定值。则一开始我们需要遍历大小为m的大桶数组,后来要遍历大小为k的单个大桶,时间复杂度为O(max(k,m))在nk为定值的情况下,易知m = k = (mk)(1/2)的时候效率最高为n(1/2)。
本题中为了方便,采用分层hash的策略,将值为key的元素放入bucke[k/320][k%320]
中。
代码如下:
#include <stdio.h>
int table[320][320];
int count[320];
int stack[320*320],top=0;
void push(int e){
stack[top++]=e;
table[e/320][e%320]++;
count[e/320]++;
}
void pop(void){
if(top==0)
printf("Invalid\n");
else{
int e=stack[--top];
printf("%d\n",e);
table[e/320][e%320]--;
count[e/320]--;
}
}
void peekmedian(void)
{
if(top==0)
printf("Invalid\n");
else
{
int k=(top+1)/2,i,c=0;
for(i=0;i<320;++i){
if(c+count[i]>=k)break;
c+=count[i];
}
for(int j=0;j<320;++j){
c+=table[i][j];
if(c>=k){
printf("%d\n",i*320+j);break;
}
}
}
}
int main(){
int n,e;
char s[12];
scanf("%d",&n);
while(n--){
scanf("%s",s);
switch(s[1]){
case 'u':{scanf("%d",&e);push(e);};break;
case 'o':pop();break;
case 'e':peekmedian();break;
}
}
return 0;
}
运行结果:
另一种更好理解的算法
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int maxn = 100010;
const int sqrN = 316;
stack<int> st;
int block[sqrN]; // 记录每一块中存在的元素个数
int table[maxn]; // hash数组,记录元素当前存在的个数
void Push(int x){
st.push(x);
block[x/sqrN]++;
table[x]++;
}
void Pop(){
int x = st.top();
st.pop();
block[x/sqrN]--;
table[x]--;
printf("%d\n",x);
}
void peekMedian(int k){
int sum = 0; // sum存放当前累计存放的数的个数
int idx = 0; // 块号
while(sum + block[idx] < k){
sum += block[idx++];
}
int num = idx*sqrN; // idx号块的第一个数
while(sum + table[num] < k){
sum += table[num++];
}
printf("%d\n",num);
}
int main(){
//freopen("in.txt","r",stdin);
int x,query;
memset(block,0,sizeof(block));
memset(table,0,sizeof(table));
char cmd[20]; // 命令
scanf("%d",&query);
for(int i = 0 ; i < query ; i++){
scanf("%s",cmd);
if(strcmp(cmd,"Push") == 0){
scanf("%d",&x);
Push(x);
}else if(strcmp(cmd,"Pop") == 0){
if(st.empty() == true){
printf("Invalid\n");
}else{
Pop();
}
}else {
if(st.empty() == true){
printf("Invalid\n");
}else{
int K = st.size();
if(K%2 == 1) K = (K+1)/2;
else K = K/2;
peekMedian(K);
}
}
}
return 0;
}