PAT 1057 Stack [难][树状数组]
1057 Stack (30)(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 (<= 10^5^). 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 10^5^.
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
题目大意:栈原本有push和pop的操作,现在加入了PeekMedian 即输出中值元素的操作,输入命令,输出相应的结果。
//关键就是怎么求中值不会导致超时。
//其实就是实现一种数据结构,能够查询头,弹出头,求中值。
代码来自:https://www.liuchuo.net/archives/2265
#include <iostream> #include <stack> #define lowbit(i) ((i) & (-i)) const int maxn = 100010; using namespace std; int c[maxn];//这个是用来计数每个元素出现的次数,比如push 3,那么就是3多了一个。 //表示≤当前下标的有几个。 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 >= 1; i -= lowbit(i)) sum += c[i]; return sum; } void PeekMedian() { int left = 1, right = maxn, mid, k = (s.size() + 1) / 2; while(left < right) {//使用二分法查找。 mid = (left + right) / 2; if(getsum(mid) >= k) right = mid; else left = mid + 1; } printf("%d\n", left); } int main() { int n, temp; scanf("%d", &n); char str[15]; for(int i = 0; i < n; i++) { scanf("%s", str); if(str[1] == 'u') {//这样判断比较快 scanf("%d", &temp); s.push(temp); update(temp, 1);//更新 } else if(str[1] == 'o') { if(!s.empty()) { update(s.top(), -1); printf("%d\n", s.top()); s.pop(); } else { printf("Invalid\n"); } } else { if(!s.empty()) PeekMedian(); else printf("Invalid\n"); } } return 0; }
//这真的相当厉害,叹为观止。太厉害了。
1.取中值并没有采取对数据进行排序的思想,而是使用树状数组存储下标,下标就是数据的值。c[i]便表示≤当前值的数的个数。
2.当push时对其进行更新,更新的参数为1,即当前数+1.为了getSum函数,所以对其他包含当前的数也进行更新。
3.当pop时操作的是栈顶的元素,更新的参数为-1。