Stack is one of the most fundamental data structures, which is based on the principle of Last In First Out (LIFO). The basic operations include Push (inserting an element onto the top position) and Pop (deleting the top element). Now you are supposed to implement a stack with an extra operation: PeekMedian -- return the median value of all the elements in the stack. With N elements, the median value is defined to be the (N/2)-th smallest element if N is even, or ((N+1)/2)-th if N is odd.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (<= 105). Then N lines follow, each contains a command in one of the following 3 formats:

Push key

Pop

PeekMedian

where key is a positive integer no more than 105.

Output Specification:

For each Push command, insert key into the stack and output nothing. For each Pop or PeekMedian command, print in a line the corresponding returned value. If the command is invalid, print "Invalid" instead.

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

算法思路

本题考查的是动态中位数的算法, 并且注意到这题时间限制是150ms,意味着至少得使用\(O(n)\)的算法,否则一定

关于中位数

当数组长度为N, 是中位数一般被定义为$ (N+1)/2 $,找中位数的算法简单想了一下, 有以下几种

  • 排序后取[(N + 1)/2]的值,几乎是万能的,即使是频繁更新这个方法也适用,但是复杂度比较高,\(\log_{2}N\), 每次都需要排序,这题是行不通的
  • 网上看到有使用两个堆, 一个大根堆, 一个小根堆,大根堆存小于中位数的数, 小根堆存大于中位数的数,因此只要保持两个堆的数字数量相差不超过1, 那么大根堆的堆顶就是中位数。然后初始化一个中位数,每当插入一个数的时候,若大于这个中位数,插入小根堆, 若小于中位数,插入大根堆,接着如果不满足数量平衡,再从一个堆的顶部拿出来一个数加入另一个堆即可。

    分析:插入操作复杂度为\(O(\log N)\), 调整也是\(O(\log N)\),取出也是\(O(\log N)\),因此速度还是可以的,如果需要执行pop,可以从堆中删除一个数,复杂度同样也是\(O(\log N)\)
  • 这里看到一种新的数据结构:树状数组,参考网上众多博客后发现这篇 搞懂树状数组 写的比较清楚,先mark一下,需要熟悉一下树状数组的基本思想才能理解以后更好的运用,希望以后自己也能总结出一篇这样的文章来。

回到题目

总结一下这题的算法, 使用树状数组的最直接的好处是在频繁更新的数组中很容易的计算出一个连续区间[1,n]的和, 有了这个性质, 当然也可以计算出任意区间[m, n]的和, 只需要减一下就行了, 计算和插入速度都是log的,那么如何使用这个性质找到中位数呢?

首先我们有这么几个数:

A 1 1 2 3 4 4 5 6
index 0 1 2 3 4 5 6 7

可以得到一个count数组

index 0 1 2 3 4 5 6
count 0 2 1 1 2 1 1

现在计算sum(0, 4) = 4就能知道数组中小于等于4的整数有(2 + 1 + 1 + 1) = 6个, 我们知道如果这个答案等于(N+1)/2这个index就是我们需要的中位数,但是我们需要从0开始遍历每一个index让后求出sum(0, index)来找到sum=4的位置吗, 那么最坏情况是要找n次。好在sum数组有一个很好的性质——非递减数组,也就是sum(i)天生就是排好序的(当然这只适用于没有负数的情况),我们可以使用一个二分查找很快的找到sum(i) = 4 的i于是就是这题的思路了。

PS:在二分查找的时候会遇到一些问题,这里有一个例子

A 1 1 2 3 5 6 100 100
index 0 1 2 3 4 5 6 7
index 0 1 2 3 4 5 6 ... 100
count 0 2 1 1 0 1 1 ... 2

这样算出来sum(0, 3) 和sum(0, 4)都等于4, 然而我们只需要的是最左边的4(很显然, 4并没有存在于我们的数组),因此有一个必须做到的是必须用binsearch找到最左边的4,这里需要注意,看完这篇博客二分查找技巧以后, 感觉自己没学过二分查找似的,还是要努力学习:

最后上源码:

#include <stdio.h>
#include <string.h> using namespace std; const int MAX = 100005; int cnt[MAX];
int N;
int stack[MAX];
int len = -1; void init()
{
memset(cnt, 0, sizeof(cnt));
}
void add(int pos, int delt)
{
while(pos <= MAX)
{
cnt[pos] += delt;
pos += pos&-pos;
}
}
int sum(int i)
{
int s = 0;
while(i)
{
s += cnt[i];
i -= i&-i;
}
return s;
}
void push()
{
int key;
scanf("%d", &key);
stack[++len] = key;
add(key, 1);
}
void pop()
{
if(len == -1)
{
printf("Invalid\n");
return;
}
int key = stack[len];
len--;
add(key, -1);
printf("%d\n", key);
}
int binsearch(int s)
{
int l = 0, r = MAX - 1;
int mid;
while(l <= r)
{
mid = ((l + r) / 2);
int k = sum(mid);
if(s <= k)
{
r = mid - 1;
}
else{
l = mid + 1;
}
}
return l;
}
void mid()
{
if(len == -1)
{
printf("Invalid\n");
return;
}
printf("%d\n", binsearch((len + 2) / 2));
}
int main()
{
scanf("%d", &N);
init();
while(N--)
{
char cmd[16];
scanf("%s", cmd);
switch(cmd[1])
{
case 'u':push();break;
case 'o':pop();break;
case 'e':mid();break;
}
} return 0;
}

最后补一句

其实这是改良过的代码,原来使用STL中的stack作为存储的容器,然后使用cin读取,好几个点没过去,后来换了自己写的stack,然后改成scanf和printf以后就过去了,看来cin和scanf速度差别还是挺大的,STL也要慎用

PAT 1057的更多相关文章

  1. PAT 1057. Stack (30)

    题目地址:http://pat.zju.edu.cn/contests/pat-a-practise/1057 用树状数组和二分搜索解决,对于这种对时间复杂度要求高的题目,用C的输入输出显然更好 #i ...

  2. PAT 1057 数零壹 (20)(代码+思路)

    1057 数零壹(20 分) 给定一串长度不超过 10​5​​ 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一下 N 的二 ...

  3. PAT——1057. 数零壹

    给定一串长度不超过105的字符串,本题要求你将其中所有英文字母的序号(字母a-z对应序号1-26,不分大小写)相加,得到整数N,然后再分析一下N的二进制表示中有多少0.多少1.例如给定字符串“PAT ...

  4. PAT 1057 数零壹

    https://pintia.cn/problem-sets/994805260223102976/problems/994805270914383872 给定一串长度不超过 10​5​​ 的字符串, ...

  5. PAT 1057 Stack [难][树状数组]

    1057 Stack (30)(30 分) Stack is one of the most fundamental data structures, which is based on the pr ...

  6. PAT 1057. 数零壹(20)

    给定一串长度不超过105的字符串,本题要求你将其中所有英文字母的序号(字母a-z对应序号1-26,不分大小写)相加,得到整数N,然后再分析一下N的二进制表示中有多少0.多少1.例如给定字符串“PAT ...

  7. PAT 1057. Stack

    Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...

  8. PAT甲级1057. Stack

    PAT甲级1057. Stack 题意: 堆栈是最基础的数据结构之一,它基于"先进先出"(LIFO)的原理.基本操作包括Push(将元素插入顶部位置)和Pop(删除顶部元素).现在 ...

  9. PAT Basic 1057

    1057 数零壹 给定一串长度不超过 10​5​​ 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一下 N 的二进制表示中有 ...

随机推荐

  1. js检测数据类型的方法你都掌握了几个?

    //1.typeof检测/*var obg = {};var ary = [];var reg = /^$/;var fn = function () {};var num = 1;var bool ...

  2. web微信开发前期准备最新详细流程

    一.申请配置测试公众号与配置本地服务器   1.打开浏览器,输入:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login,微信扫码确 ...

  3. C语言在open() FIFO文件的时候卡住了

    需要注意的是,open打开FIFO文件的时候,open函数里的flag参数 O——NONBLOCK: 置位:  如果“只写”方式打开文件,写进程会阻塞直到有一个读进程来读这个FIFO管道.就是说:没有 ...

  4. RMI原理及简单示例

    分布式对象 在学习 RMI 之前,先来分布式对象(Distributed Object):分布式对象是指一个对象可以被远程系统所调用.对于 Java 而言,即对象不仅可以被同一虚拟机中的其他客户程序( ...

  5. C/C++学习路线图

    文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 这里整理的C/C++学习路线图包含初中高三个部分,你可以通过百度云盘下载观看对应的视频 链接: http://pan.baidu.c ...

  6. PHP学习路线图

    文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 这里整理的PHP学习路线图包含初中高三个部分,你可以通过百度云盘下载观看对应的视频 链接: http://pan.baidu.com ...

  7. C#7.0之ref locals and returns (局部变量和引用返回,之前欠大家的,现在补上)

    废话不多说,直接进入正题. 首先我们知道 ref关键字是将值传递变为引用传递 那么我们先来看看ref locals(ref局部变量) 列子代码如下: static void Main(string[] ...

  8. 前端资讯周报 3.6 - 3.12: 对学习Javascript最有帮助的三本书,以及HTML标题的迷思

    每周一我都会分享上一周我订阅的技术站点中,和解决问题的过程中阅读到的值得分享的文章. 毕竟个人的阅读量有限,也欢迎大家留言或者私信给我你们阅读到的,对你们前端技术有帮助的任何内容,题材不限,语言不限. ...

  9. ajax 实现三级联动

    ajax 实现三级联动,相当于写了一个小插件,用的时候直接拿过来用就可以了,这里我用了数据库中的chinastates表, 数据库内容很多,三级联动里的地区名称都在里面,采用的是代号副代号的方式 比如 ...

  10. 一段获取app性能指标的py脚本

    #coding:utf-8 import os import timeimport datetimeimport subprocess ActivityManager = 'homepage.Main ...