**PAT_甲级_1057 Stack (30分) (C++)【字符串处理/栈的模拟/树状数组】

目录

1,题目描述

 题目大意

2,思路

数据结构

函数讲解

1,void update(int x, int v):

2,int getsum(int x):

 3,void PeekMedian():

3,解题历程

第一搏: 

第二搏:

第三搏

第四搏:

4,代码

第一搏: 

AC代码(柳神)


1,题目描述

Sample Input:

17
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
PeekMedian
Pop
Pop
Pop
Pop

 

Sample Output:

Invalid
Invalid
3
2
2
1
2
4
4
5
3
Invalid

 题目大意

给出一系列栈的操作,输出对应操作的标志(入栈:不输出;出栈:输出弹出的值,若栈空,则输出Invalid;PeekMedian:输出当前栈内数据的中位数)

 

2,思路

这一题的解法主要有两种:分块法树状数组法

这里主要介绍树状数组的解法,不了解树状数组的同学可以参考这些大神的文章:

初步了解树状数组:@半根毛线code【树状数组彻底入门 】

进一步总结:@和山米兰【树状数组】

树状数组与本题的关系:@ 没想好呢111 【PAT 甲级 1057 Stack】

若想进一步了解树状数组,可以阅读算法笔记(晴神笔记)第十三章 专题拓展,里面有对树状数组的详细讲解。 

补充:根据晴神笔记的内容,我也整理出了一篇讲解树状数组的博客,感兴趣的同学可以戳这里:树状数组【算法笔记/晴神笔记】,很详细哟!

 

数据结构

  • 全局变量int c[maxn]:下标为key(入栈/出栈的值)。c[i]表示在i的辖区范围(lowBit(i))内,小于i的key的个数

函数讲解

1,void update(int x, int v):

当Push x时,辖区范围(lowBit(i))内包括x的全部需要更新(加一。表示对x后面的每个数来说,比它小的元素的数目加一),即update(x, i);当Pop时,正好相反,需要减一,即update(x, -1)。

2,int getsum(int x):

求出当前栈中,所有小于等于x的元素的数目。

比如,当x=7时:ans += (c[7] + c[6] + c[4]);(7,6,4即for(int i = x; i >= 1; i -= lowbit(i))得出的)

c[7]范围内小于等于7的元素数目,加上c[6]范围内小于等于6的元素数目,加上c[4]范围内小于等于4的元素数目.。

 3,void PeekMedian():

主体为二分查找。

void PeekMedian() {
    int left = 1, right = maxn, mid, k = (s.size() + 1) / 2;
    while(left < right) {
        mid = (left + right) / 2;
        if(getsum(mid) >= k)
            right = mid;
        else
            left = mid + 1;
    }
    printf("%d\n", left);
}

这里没有用if(getsum(mid) == k) break; printf("%d\n",mid),是因为,可能不存在正好 getsum(mid) == k的mid值,所以if(getsum(mid) >= k)时,right = mid;正好可以解决这个问题。

 

3,解题历程

第一搏: 

原先想着,每插入一个就sort一次可能有点浪费。。。(快排时间复杂度平均为O(nlogn)),于时就想着自定义插入删除函数(原本是有序的,只需要删除指定元素即可,类似于数组删除元素(时间复杂度为O(n))。然而有三个测试点超时。。。

vector<int> stack, temp;
void insert1(int key){
    temp.push_back(key);
    for(int i = temp.size() - 1; i > 0; i--){
        if(temp[i] < temp[i-1]){
            swap(temp[i], temp[i-1]);
        }else break;
    }
}
void delete1(int key){
    int i;
    for(i = 0; i < temp.size(); i++){
        if(temp[i] == key) break;
    }
    temp.erase(temp.begin() + i);
}

字符串的处理方式

string cmd;
for(int i = 0; i < N; i++){
    cin>>cmd;

 

第二搏:

修改了字符串的处理方式:

    char cmd[11];
    int key, out;

    for(int i = 0; i < N; i++){
        scanf("%s", cmd);
        if(strcmp(cmd, "Pop") == 0){//字符串比较大小

然而还是超时。。。(甚至比cin还要慢一点,,,可能是偶然,因为同一代码几次运行的结果也可能不一样)

第三搏

我仍不死心,于时想用map试一试,key为入栈的元素,value为key出现的次数,每次求中位数时sum从小到大累加value值,若sum>=(stack.size() + 1) / 2) 时,key即为所求。

#include<iostream>
#include<vector>
#include<map>
#include<string.h>
#include<algorithm>
using namespace std;

vector<int> stack;
map<int, int> temp;             //利用map自动对key排序的特性
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif

    int N;//操作次数
    cin>>N;

    char cmd[11];
    int key, out;
    int num = 0, sum = 0;

    for(int i = 0; i < N; i++){
        scanf("%s", cmd);
        if(strcmp(cmd, "Pop") == 0){
            if(stack.size() == 0){
                printf("Invalid\n");
            }else{
                out = stack[stack.size()-1];
                printf("%d\n", out);
                stack.pop_back();
                temp[out]--;                //out对应的值 出现次数减一
            }

        }else if(strcmp(cmd, "PeekMedian") == 0){
            if(stack.size() == 0){
                printf("Invalid\n");
            }else{
                for(auto it : temp){
                    sum += it.second;
                    if(sum >= (stack.size() + 1) / 2){//寻找中位数
                        printf("%d\n", it.first);
                        break;
                    }
                }
                sum = 0;
            }
        }else if(strcmp(cmd, "Push") == 0){
            scanf("%d", &key);
            stack.push_back(key);
            temp[key]++;                    //out对应的值 出现次数加一
        }
    }
    return 0;
}

第四搏:

没招了。

大神的做法基本都是树状数组之类的。

仿照柳神的代码敲了一遍。。。(柳神666,太不容易了)

再一次感受到了算法的魅力!!!

 

4,代码

第一搏: 

#include<iostream>
#include<vector>
#include<map>
#include<string.h>
#include<algorithm>
using namespace std;

vector<int> stack, temp;
void insert1(int key){
    temp.push_back(key);
    for(int i = temp.size() - 1; i > 0; i--){
        if(temp[i] < temp[i-1]){
            swap(temp[i], temp[i-1]);
        }else break;
    }
}
void delete1(int key){
    int i;
    for(i = 0; i < temp.size(); i++){
        if(temp[i] == key) break;
    }
    temp.erase(temp.begin() + i);
}

int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif

    int N;//操作次数
    cin>>N;

    char cmd[11];
    int key, out;

    for(int i = 0; i < N; i++){
        scanf("%s", cmd);
        if(strcmp(cmd, "Pop") == 0){//字符串比较大小
            if(stack.size() == 0){
                printf("Invalid\n");
            }else{
                out = stack[stack.size()-1];
                printf("%d\n", out);
                stack.pop_back();
                delete1(out);
            }


        }else if(strcmp(cmd, "PeekMedian") == 0){
            if(stack.size() == 0){
                printf("Invalid\n");
            }else{
                out = temp[(temp.size() + 1) / 2 - 1];
                printf("%d\n", out);
            }
        }else if(strcmp(cmd, "Push") == 0){
            scanf("%d", &key);
            stack.push_back(key);
            insert1(key);
        }
        //cout<<"YES"<<endl;
    }
    return 0;
}

AC代码(柳神)

#include <iostream>
#include <stack>
#define lowbit(i) ((i) & (-i))
const int maxn = 100010;
using namespace std;
int c[maxn];
stack<int> s;
void update(int x, int v) {
    for(int i = x; i < maxn; i += lowbit(i))
        c[i] += v;
}
int getsum(int x) {
    int sum = 0;
    for(int i = x; i >= 1; i -= lowbit(i))
        sum += c[i];
    return sum;
}
void PeekMedian() {
    int left = 1, right = maxn, mid, k = (s.size() + 1) / 2;
    while(left < right) {
        mid = (left + right) / 2;
        if(getsum(mid) >= k)
            right = mid;
        else
            left = mid + 1;
    }
    printf("%d\n", left);
}
int main() {
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif
    int n, temp;
    scanf("%d", &n);
    char str[15];
    for(int i = 0; i < n; i++) {
        scanf("%s", str);
        if(str[1] == 'u') {
            scanf("%d", &temp);
            s.push(temp);
            update(temp, 1);
        }else if(str[1] == 'o') {
            if(!s.empty()) {
                update(s.top(), -1);
                printf("%d\n", s.top());
                s.pop();
            } else {
                printf("Invalid\n");
            }
        }else {
            if(!s.empty())
                PeekMedian();
            else
                printf("Invalid\n");
        }
    }
    return 0;
}

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值