题目大意:
给出一个栈的入栈出栈过程,并随时通过PeekMedian命令要求输出栈中中位数,Pop命令输出出栈的数。
当栈中没有元素时,Pop命令和PeekMedian命令都应该输出"Invalid"。
思路:
1)关于实时查询数组元素中第k大的元素。
所谓实时查询就是在查询过程中会有元素不断的添加与删除,如果每次都排序会超时,因此考虑如下策略:
1.考虑将N个元素分为sqrt(N)块,其中每块sqrt(N)个元素。则除最后一块,每一块都应该有sqrt(N)个元素。
2.元素序列中都是不超过10^5的非负整数,设置hashTable[100010],其中hashTable[x]表示整数x的存在个数。
3.定义整型数组block[317],其中block[i]表示第i块中存在的元素个数,加入要新增一个元素,则令block[x/316]加一,表示该块中元素多了1,同时令table[x]+1,表示整数x的当前个数+1。
4.查询第K大的数,从小到大枚举块号,利用block数组累加前i-1块元素总个数,判断加入i号块之后元素的总个数能否达到k,如果可以,说明第k大的数就在当前枚举的这个块中,此时只需要从小到大遍历该块中的每个元素,利用hashTable数组
累加元素存在的个数,直到总数累计到达k,则说明找见了第K大的数。
2)根据入栈出栈指令,对栈进行操作,push入栈,pop指令输出栈顶元素,同时对hashTable与block数组进行操作,PeekMedian输出当前栈中中间的元素,即k取中间位置进行查询。
AC代码:
//PAT_A 1040
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
const int maxn = 100010;
const int sqrN = 316;//分块个数
stack<int> st;
int block[sqrN], table[maxn];
void PeekMedian(int k) {
int sum = 0, index = 0;//当前累计存在的个数,块号
while (sum + block[index] < k) {
sum += block[index];
index++;
}
int num = index * sqrN;
while (sum + table[num] < k) {
sum += table[num];
num++;
}
printf("%d\n", num);
return;
}
void push(int x) {
st.push(x);
block[x / sqrN]++;
table[x]++;
}
void pop() {
int x = st.top();
st.pop();
block[x / sqrN]--;
table[x]--;
printf("%d\n", x);
}
int main() {
int x, query;
char cmd[20];
fill(block, block + sqrN, 0);
fill(table, table + maxn, 0);
(void)scanf("%d", &query);
for (int i = 0; i < query; i++) {
(void)scanf("%s", cmd);
if (strcmp(cmd, "Push") == 0) {
(void)scanf("%d", &x);
push(x);
}
else if (strcmp(cmd, "Pop") == 0) {
if (st.empty() == true)printf("Invalid\n");
else pop();
}
else {
if (st.empty() == true)printf("Invalid\n");
else {
int k = st.size();
if (k % 2 == 1)k = k / 2 + 1;
else k /= 2;
PeekMedian(k);
}
}
}
return 0;
}