PAT 1057 Stack C++版
1.题意
给出一系列栈的操作,让我们模拟动态查询(即在线查询)。
本体的在线查询主要是找出中位数,即一个数组中中间的那个数。关于一个数组中中间数的定义如下:
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.
因为N的值比较大(N= 10^5
),所以很难进行一遍排序,然后再找出其中间的那个数。故这里提供如下两种思路:
2.分析
2.1 分块思想
这个思想比较简单。使用如下的例子来说明:
假设现在有5个数(1, 2 , 3 , 4, 5)需要进行排序,找出其中的中间数。如何使用分块的思想实现呢?
-
step 1: 分块
分块的过程至关重要!
我们首先将这个 5个元素分成3(ceil(sqrt(5)) = 3
)块,而且每块有ceil(sqrt(5))
个元素 **[虽然这里的总数已经超过了5,但是便于计算而且不影响结果的生成,其实块数和每块的个数不影响结果] ** ,然后剩下的元素就放在最后一组里。
针对上面的数字,我们得到的分块分别是
block[0] = {1,2}
,block[1] = {3,4}
,block[2] = {5}
。 -
step 2:
针对每个元素使用一个数组table[n]
进行一个计数操作,table[i]
表示的就是i这个元素的个数。
针对上面的数据,得到的结果就是:
table[1] = 1, table[2] = 1, table[3] = 1, table[4] = 1, table[5] = 1
-
step 3: 对块和table进行操作
例如,如果是Push 3
,则表示需要往block[3/2],即block[1]中添加一个元素,这样block[1] = 3
; 同时将table[3]++
; 如果是Pop
操作,则进行逆操作即可。 -
step 4:找中间数
首先找根据block 的数量确定在哪个block里;找到所在的block之后,再根据table确定相应的数据即可。
3.代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<stack>
#define maxn 100001//表示最多有15 [1 - 15] 个整数
#define sqrN 317 //表示最大有317组
using namespace std;
int N;
stack<int> sta;//用于装数字的栈
int table[maxn];//table[i] 表示的就是i这个元素的个数
int block[sqrN];//表示每组的所有数的个数
int total ;//表示当前的栈中的所有元素
//寻找中间数
void findMid(){
total = sta.size();//得到栈中的所有元素
int mid ;// 中间数
if(total % 2 == 0) {//如果为偶数
mid = total/2;
}else{
mid = (total+1)/2;
}
int i;
//开始遍历找到中间数
int sum = 0;
for(i = 0;i < 317;i++){//注意这里i的上限是个定值 ,在这里是4
if(sum + block[i] < mid){//如果加上当前的组数还未到
sum += block[i];//则加上
}
else{
break;//跳出循环
}
}
//说明在下标为i的block中, 能够找到中间数
int startNum ,endNum ; //开始数和截止数
startNum = i * 317;
endNum = (i + 1) * 317;
for(int j = startNum; j < endNum;j++){
if(sum + table[j] < mid){
sum += table[j];
}
else {
cout << j <<"\n";
break;//说明找到中间数了
}
}
}
//针对给出的字符串进行操作
void ope(string str){
//Pop PeekMedian Push
int tempNum;// 表示当前需要处理的数字(数字)
string strNum;// 表示当前需要处理的数字 (字符串)
if(str.compare("Pop") == 0){//说明是出栈操作
if(sta.size() > 0){//如果有元素出栈
tempNum = sta.top();//栈顶元素
table[tempNum] --;//该元素数目减1
block[tempNum/317] -- ;//该大组数目减1
sta.pop();//将头元素出栈
cout << tempNum <<"\n";//将栈顶元素出栈
}
else cout << "Invalid\n";
}
else if (str.compare("Push") == 0){//说明是入栈栈操作
cin >> tempNum;//输入需要入栈的数
sta.push(tempNum);//将头元素入栈
table[tempNum]++;
block[tempNum/317]++;
}
else{//说明需要打印中间数
if(sta.size() > 0)//如果栈中有元素
findMid();
else cout << "Invalid\n";
}
}
int main(){
int i ,j;
string str;
cin >> N;
for(i = 0;i < N;i++){
cin >> str; //进行简单的输入
ope(str);//执行相应的操作
}
}
4.测试用例
18
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
Push 4293
PeekMedian
Pop
Pop
Pop
Pop
6
Push 11
Push 2
Push 3
PeekMedian
Pop
PeekMedian