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 -th smallest element if N is even, or -th if N is odd.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer . 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 .
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
Ω
本以为是一个模拟栈的小题,没想到外加PeekMedian功能;本以为只需输出栈中间元素即可,没想到是所有元素排序后的中间元素。
本来想用multiset容器来实现元素自动排序的,不过中间元素的迭代器并不好求。不想超时还是只能用线性容器vector+二分搜索。
思想非常纯朴,一个stack来模拟,一个vector来排序。vector的插入和删除都使用二分搜索来寻找位置,勉勉强强不会超时。
🐎
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int main()
{
int n, m, left, right, mid;
cin >> n;
stack<int> stk;
vector<int> order;
string s;
for (int i = 0; i < n; ++i)
{
cin >> s;
if (s == "Push")
{
cin >> m;
left = 0, right = stk.size(), mid = (left + right) / 2;
while (left < right - 1)
{
(order[mid] > m) ? (right = mid) : (left = mid);
mid = (left + right) / 2;
}
int fix = (!order.empty() && order[left] < m) ? 1 : 0;
order.insert(order.begin() + left + fix, m);
stk.push(m);
}
else if (!stk.empty())
{
if (s == "Pop")
{
int t = stk.top();
cout << t << endl;
left = 0, right = stk.size(), mid = (left + right) / 2;
while (left < right)
{
if (order[mid] != t)
(order[mid] > t) ? (right = mid) : (left = mid);
else break;
mid = (left + right) / 2;
}
stk.pop();
order.erase(order.begin() + mid);
}
else
cout << order[(stk.size() - 1) / 2] << endl;
}
else
cout << "Invalid" << endl;
}
}
Tips
两次二分搜索还是有点差异的,Pop时你可以确定存在该元素,只要order[mid]==value
即可退出循环,而Push时我们寻找的是插入该元素的位置,退出循环的时候right和lleft最多差1(left < right - 1为防止right=left+1 && mid==left时而无限循环),此时order[mid]既有可能==m,也有可能<m,后者不能直接插入,因此引入一个fix对位置进行修正。