目录
1,题目描述
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
题目大意
给出一系列栈的操作,输出对应操作的标志(入栈:不输出;出栈:输出弹出的值,若栈空,则输出Invalid;PeekMedian:输出当前栈内数据的中位数)
2,思路
这一题的解法主要有两种:分块法和树状数组法。
这里主要介绍树状数组的解法,不了解树状数组的同学可以参考这些大神的文章:
初步了解树状数组:@半根毛线code【树状数组彻底入门 】;
进一步总结:@和山米兰【树状数组】;
树状数组与本题的关系:@ 没想好呢111 【PAT 甲级 1057 Stack】
若想进一步了解树状数组,可以阅读算法笔记(晴神笔记)第十三章 专题拓展,里面有对树状数组的详细讲解。
补充:根据晴神笔记的内容,我也整理出了一篇讲解树状数组的博客,感兴趣的同学可以戳这里:树状数组【算法笔记/晴神笔记】,很详细哟!
数据结构
- 全局变量int c[maxn]:下标为key(入栈/出栈的值)。c[i]表示在i的辖区范围(lowBit(i))内,小于i的key的个数;
函数讲解
1,void update(int x, int v):
当Push x时,辖区范围(lowBit(i))内包括x的全部需要更新(加一。表示对x后面的每个数来说,比它小的元素的数目加一),即update(x, i);当Pop时,正好相反,需要减一,即update(x, -1)。
2,int getsum(int x):
求出当前栈中,所有小于等于x的元素的数目。
比如,当x=7时:ans += (c[7] + c[6] + c[4]);(7,6,4即for(int i = x; i >= 1; i -= lowbit(i))得出的)
即c[7]范围内小于等于7的元素数目,加上c[6]范围内小于等于6的元素数目,加上c[4]范围内小于等于4的元素数目.。
3,void PeekMedian():
主体为二分查找。
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);
}
这里没有用if(getsum(mid) == k) break; printf("%d\n",mid),是因为,可能不存在正好 getsum(mid) == k的mid值,所以if(getsum(mid) >= k)时,right = mid;正好可以解决这个问题。
3,解题历程
第一搏:
原先想着,每插入一个就sort一次可能有点浪费。。。(快排时间复杂度平均为O(nlogn)),于时就想着自定义插入删除函数(原本是有序的,只需要删除指定元素即可,类似于数组删除元素(时间复杂度为O(n))。然而有三个测试点超时。。。
vector<int> stack, temp;
void insert1(int key){
temp.push_back(key);
for(int i = temp.size() - 1; i > 0; i--){
if(temp[i] < temp[i-1]){
swap(temp[i], temp[i-1]);
}else break;
}
}
void delete1(int key){
int i;
for(i = 0; i < temp.size(); i++){
if(temp[i] == key) break;
}
temp.erase(temp.begin() + i);
}
字符串的处理方式
string cmd;
for(int i = 0; i < N; i++){
cin>>cmd;
第二搏:
修改了字符串的处理方式:
char cmd[11];
int key, out;
for(int i = 0; i < N; i++){
scanf("%s", cmd);
if(strcmp(cmd, "Pop") == 0){//字符串比较大小
然而还是超时。。。(甚至比cin还要慢一点,,,可能是偶然,因为同一代码几次运行的结果也可能不一样)
第三搏
我仍不死心,于时想用map试一试,key为入栈的元素,value为key出现的次数,每次求中位数时sum从小到大累加value值,若sum>=(stack.size() + 1) / 2) 时,key即为所求。
#include<iostream>
#include<vector>
#include<map>
#include<string.h>
#include<algorithm>
using namespace std;
vector<int> stack;
map<int, int> temp; //利用map自动对key排序的特性
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif
int N;//操作次数
cin>>N;
char cmd[11];
int key, out;
int num = 0, sum = 0;
for(int i = 0; i < N; i++){
scanf("%s", cmd);
if(strcmp(cmd, "Pop") == 0){
if(stack.size() == 0){
printf("Invalid\n");
}else{
out = stack[stack.size()-1];
printf("%d\n", out);
stack.pop_back();
temp[out]--; //out对应的值 出现次数减一
}
}else if(strcmp(cmd, "PeekMedian") == 0){
if(stack.size() == 0){
printf("Invalid\n");
}else{
for(auto it : temp){
sum += it.second;
if(sum >= (stack.size() + 1) / 2){//寻找中位数
printf("%d\n", it.first);
break;
}
}
sum = 0;
}
}else if(strcmp(cmd, "Push") == 0){
scanf("%d", &key);
stack.push_back(key);
temp[key]++; //out对应的值 出现次数加一
}
}
return 0;
}
第四搏:
没招了。
大神的做法基本都是树状数组之类的。
仿照柳神的代码敲了一遍。。。(柳神666,太不容易了)
再一次感受到了算法的魅力!!!
4,代码
第一搏:
#include<iostream>
#include<vector>
#include<map>
#include<string.h>
#include<algorithm>
using namespace std;
vector<int> stack, temp;
void insert1(int key){
temp.push_back(key);
for(int i = temp.size() - 1; i > 0; i--){
if(temp[i] < temp[i-1]){
swap(temp[i], temp[i-1]);
}else break;
}
}
void delete1(int key){
int i;
for(i = 0; i < temp.size(); i++){
if(temp[i] == key) break;
}
temp.erase(temp.begin() + i);
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif
int N;//操作次数
cin>>N;
char cmd[11];
int key, out;
for(int i = 0; i < N; i++){
scanf("%s", cmd);
if(strcmp(cmd, "Pop") == 0){//字符串比较大小
if(stack.size() == 0){
printf("Invalid\n");
}else{
out = stack[stack.size()-1];
printf("%d\n", out);
stack.pop_back();
delete1(out);
}
}else if(strcmp(cmd, "PeekMedian") == 0){
if(stack.size() == 0){
printf("Invalid\n");
}else{
out = temp[(temp.size() + 1) / 2 - 1];
printf("%d\n", out);
}
}else if(strcmp(cmd, "Push") == 0){
scanf("%d", &key);
stack.push_back(key);
insert1(key);
}
//cout<<"YES"<<endl;
}
return 0;
}
AC代码(柳神)
#include <iostream>
#include <stack>
#define lowbit(i) ((i) & (-i))
const int maxn = 100010;
using namespace std;
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 >= 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() {
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif
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;
}