PAT 1057
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的更多相关文章
- PAT 1057. Stack (30)
题目地址:http://pat.zju.edu.cn/contests/pat-a-practise/1057 用树状数组和二分搜索解决,对于这种对时间复杂度要求高的题目,用C的输入输出显然更好 #i ...
- PAT 1057 数零壹 (20)(代码+思路)
1057 数零壹(20 分) 给定一串长度不超过 105 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一下 N 的二 ...
- PAT——1057. 数零壹
给定一串长度不超过105的字符串,本题要求你将其中所有英文字母的序号(字母a-z对应序号1-26,不分大小写)相加,得到整数N,然后再分析一下N的二进制表示中有多少0.多少1.例如给定字符串“PAT ...
- PAT 1057 数零壹
https://pintia.cn/problem-sets/994805260223102976/problems/994805270914383872 给定一串长度不超过 105 的字符串, ...
- PAT 1057 Stack [难][树状数组]
1057 Stack (30)(30 分) Stack is one of the most fundamental data structures, which is based on the pr ...
- PAT 1057. 数零壹(20)
给定一串长度不超过105的字符串,本题要求你将其中所有英文字母的序号(字母a-z对应序号1-26,不分大小写)相加,得到整数N,然后再分析一下N的二进制表示中有多少0.多少1.例如给定字符串“PAT ...
- PAT 1057. Stack
Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...
- PAT甲级1057. Stack
PAT甲级1057. Stack 题意: 堆栈是最基础的数据结构之一,它基于"先进先出"(LIFO)的原理.基本操作包括Push(将元素插入顶部位置)和Pop(删除顶部元素).现在 ...
- PAT Basic 1057
1057 数零壹 给定一串长度不超过 105 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一下 N 的二进制表示中有 ...
随机推荐
- 2017-2-22 if语句 if语句的嵌套 以及课堂练习 经典猜拳游戏,闰年判断,输出当前时间的天数等
(一)if语句 1.格式 if(){ }else if() { } 注意:如果if后面不写花括号,只执行下面第一句 (二)语句1:顺序语句 2:循环语句 3:分支语句 课后练习: 1.猜拳游戏(用 ...
- 好公司、行业、领导?应届生应根据什么选offer?
两个年轻人大学毕业了,一个去了收入更高的大企业工作,一个去了收入较低的小作坊式工厂工作.你们说他们谁的青春时光最能升值呢?表面上看应该是大企业,可是大企业是做马车制造的,小作坊是做汽车的.现在人们都知 ...
- jquery与js的区别与基础操作
一.什么是 jQuery jQuery是一个JavaScript库,它通过封装原生的JavaScript函数得到一整套定义好的方法.它的作者是John Resig,于2006年创建的一个开源项目,随着 ...
- 【js】性能问题
执行环境和作用域 一.全局变量和局部变量 每个执行环境都有与之关联的变量对象(变量和函数存储在这里),全局执行环境是最外围的执行环境,根据ECMA实现所在的宿主不同,表示的执行环境的对象也不同.在we ...
- Ajax基础与登入
Ajax 是 Asynchronous JavaScript and XML的缩写. Ajax的优点: 优点:减轻服务器的负担,按需取数据,最大程度的减少冗余请求 局部刷新页面,减少用户心理和实际的等 ...
- 2017-03-10 T-sql 语句 高级查询
T-SQL语句: 创建数据库: 1,点击新建查询,在弹出的页面上进行代码编写.点击可用数据库,编写前确定当前操作的页面是自己想要进行操作的界面. 2,数据库创建语句 Create datebase ...
- 你真的会 snapshot 吗? - 每天5分钟玩转 OpenStack(163)
这是 OpenStack 实施经验分享系列的第 13 篇. instance snapshot 操作可用于备份或者将 instance 保存为新的 image.如果在生产系统中执行 snapshot ...
- idea中编译项目报错 java: javacTask: 源版本 1.8 需要目标版本 1.8
问题如上面所叙: > idea中编译项目报错 java: javacTask: 源版本 1.8 需要目标版本 1.8 解决方案: > Setting->Compiler->Ja ...
- Spring 4 支持的 Java 8 特性
Spring 框架 4 支持 Java 8 语言和 API 功能.在本文中,我们将重点放在 Spring 4 支持新的 Java 8 的功能.最重要的是 Lambda 表达式,方法引用,JSR-310 ...
- X-Scan使用教程
下载X-Scan扫描器,解压缩,双击Xscan_gui.exe即可运行,不需要安装.X-Scan采用多线程的方式,对指定主机或者网段进行扫描. 其扫描功能(插件)有: 开放服务:扫描TCP端口状态,根 ...