文章目录
- 1 题目
- 2 解析
- 2.1 题意
- 2.2 思路
- 3 参考代码
- 3.1 思路一
- 3.2 思路二
1 题目
1057 Stack (30分)
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
2 解析
2.1 题意
模拟栈的入栈、出栈,遇到PeekMedian命令时,输出中位数,当栈中没有元素时,输出Invalid.
2.2 思路
- 思路一(分块)
在模拟栈的操作同时,还需要支持在线查询,如果暴力求解肯定会超时(数据为105),因此使用分块来求解。
对于N次查询,总的时间复杂度为O(N),对于N=105时,总复杂度才107.5,这是在理想情况最坏复杂度。
- 思路二(树状数组)
对于这个问题,之前是用分块思想来解决的,现在尝试用树状数组来解决。
对于一个序列,如果用hash数组记录每个元素出现的个数,那么序列第K大,就是在求最小的i,使得成立。也就是说,如果用树状数组来解决hash数组求和的问题,那么这个问题就等价于寻找第一个满足条件的"
"的i。
由于hash数组的前缀和是递增的,可以令i=1、r=MAXN,然后在[l,r]范围内进行二分,对当前的mid,判断是否成立,
- 如果成立,说明所求位置不超过mid,因此令r=mid;
- 如果不成立,说明所求位置大于mid,因此令l=mid+1.
- 显然二分的时间复杂度为O(logn),求和的时间复杂度为O(logn),因此总时间复杂度是O(log2n)
3 参考代码
3.1 思路一
#include
#include
#include
#include
using std::stack;
const int MAXN = 100010;
const int sqrtN = 317;//有多少块元素
const int blockNum = 316;//每块有多个元素
int table[MAXN];//每个元素有多个
int block[sqrtN];//每块有多少个元素
stack<int> S;
void Pop(){
int x = S.top();
S.pop();
block[x / blockNum]--;
table[x]--;
printf("%d\n", x);
}
void Push(int x){
S.push(x);
block[x / blockNum]++;
table[x]++;
}
void PeekMedian(int mid){
int sum = 0;//目前计算时的实际个数
int index = 0;
while(sum + block[index] < mid){
sum += block[index++];
}
int num = index * blockNum;
while(sum + table[num] < mid){
sum += table[num++];
}
printf("%d\n", num);
//用for循环计算
// for (index = 0; index < sqrtN; ++index)
// {
// if(sum + block[index] < mid){
// sum += block[index];
// }else{
// break;
// }
// }
// for (int j = index * blockNum; j < (index + 1) * blockNum; ++j)
// {
// sum += table[j];
// if(sum >= mid){
// printf("%d\n", j);
// break;
// }
// }
}
int main(int argc, char const *argv[])
{
memset(table, 0, sizeof(table));
memset(block, 0, sizeof(block));
int n, x;
char cmd[20];
scanf("%d", &n);
while(n--){
scanf("%s", cmd);
if(strcmp(cmd, "Pop") == 0){
if(S.empty()== true){
printf("Invalid\n");
}else{
Pop();
}
}else if(strcmp(cmd, "Push") == 0){
scanf("%d", &x);
Push(x);
}else{
if(S.empty() == true){
printf("Invalid\n");
}else{
int mid = S.size();
if(mid % 2 == 0){
mid /= 2;
}else{
mid = (mid + 1) / 2;
}
PeekMedian(mid);
}
}
}
return 0;
}
3.2 思路二
#include
#include
#include
using std::stack;
const int MAXN = 100010;
#define lowbit(i) ((i) & (-i))
int c[MAXN];
stack<int> S;
void update(int x, int v){
for (int i = x; i < MAXN; i += lowbit(i))
{
c[i] += v;
}
}
int getSum(int x){
int sum = 0;
for (int i = x; i > 0; i -= lowbit(i))
{
sum += c[i];
}
return sum;
}
void PeekMedian(int K){
int l = 1, r = MAXN, mid;
while(l < r){
mid = (l + r) / 2;
if(getSum(mid) >= K){
r = mid;
}else{
l = mid + 1;
}
}
printf("%d\n", l);
}
int main(int argc, char const *argv[])
{
char str[12];
int n, x;
scanf("%d", &n);
while(n--){
scanf("%s", str);
if(strcmp(str,"Push") == 0){
scanf("%d", &x);
S.push(x);
update(x, 1);//将位置x加1
}else if(strcmp(str, "Pop") == 0){
if(S.empty() == true){
printf("Invalid\n");
}else{
printf("%d\n", S.top());
update(S.top(), -1);//将位置x减1
S.pop();
}
}else{
if(S.empty() == true){
printf("Invalid\n");
}else{
int K = S.size();
if(K % 2 == 0){
K /= 2;
}else{
K = (K + 1) / 2;
}
PeekMedian(K);
}
}
}
return 0;
}