PAT(甲级)渡劫(十七)-Stack(30)

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;
}
运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值