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. smarty实例登陆、显示、分页

    1.先建立登陆页面,登陆页面的PHP文件和HTML文件是分开写的. 先建立一个登陆页的PHP文件, <?php include("../init.inc.php");//引入 ...

  2. hdoj 1231 最大连续子列和

    最大连续子序列 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  3. String类的indexOf方法的用法和举例

    2017年3月3号博主第一次去郑州互联网公司面试,背景是这样的我先前去了农大龙子湖校园招聘投简历,然后第二天去面试了那经历可以说是很失败的一次面试,当然这跟自己的水平有关了接下来重点讲一下面试的题目: ...

  4. Java垃圾回收学习笔记

    通常来说,要写Java代码,你基本上都没必要听说垃圾回收这个概念的.这不,对于已经写了5年多Java代码的我来说,我还没有哪次经历说是需要使用垃圾回收方面的知识来解决问题的.但是,我依然督促自己花了几 ...

  5. beauty

    至你我最美的邂逅 年意渐渐消失,一转眼元宵的炮竹将要响起,今天是贰零一七年二月九号,即是元宵节前两天,在这里我写下我这几天的收获. 离元宵节还有四天,我好久都没跟朋友一起认真的玩过,几天我去了我发小的 ...

  6. 转换器2:ThinkPhp模板转Django模板

    前天写了个<ThinkPhp模板转Flask模板> 居然被同事鄙视了,原因是他用Django,我用Flask,为了避免被他继续安利Django的强大.我决定写一个Django模板转换器. ...

  7. java装箱跟拆箱解析

    /** * 在jdk1.5之后,java为基本数据类型到对应的应用数据类型提供了自动拆箱装箱操作 * 不管是自动拆箱还是自动装箱都是应用数据类型有的方法,基本数据类型是没有任何方法可调用的 *从概念上 ...

  8. USACO全部月赛及GateWay数据

    月赛: 以07年open为例,网站如下 http://contest.usaco.org/OPEN07 其他的格式是http://contest.usaco.org/月份(月份的英文前三位,比如1月是 ...

  9. 用tp框架来对数据库进行增删改

    先来看添加 使用tp框架,对数据库进行添加操作,都有哪些方法 先在Main控制器中,做个方法 运行一下,注意地址,就要输tianjia了 然后再看一下数据库,有没有添加上数据 添加成功 再来看一下这个 ...

  10. Team Foundation Server 基本功能

    Team Foundation Server(以下简称TFS)作为Microsoft发布的一个主要用于团队源代码管理工具,以敏捷开发作为其最大的特点而占领部分市场.该文主要介绍 TFS 在 Visua ...