PAT A1057 Stack

PAT A1057 Stack

在这里插入图片描述

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
  • 思路1:
    push和pop很好实现,主要是peekmedian操作,如果对栈中所有元素排序会超时,所以用块状数组法(数组标记)

  • code1:自己实现栈

#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 100010;
int stk[maxn];
int top = 0;	//top == 当前栈中元素个数 
int N;
int block[316] = {0}, table[maxn] = {0};	//存储块内元素的个数,i的个数 
void findK(int k){
	int sum = 0;
	int nbb = 0;	//记录所在块号 
	while(sum + block[nbb] < k){
		sum += block[nbb++];
	}
	//第nbb块内的第 k - sum1 个元素
	int num = 316*nbb;
	while(sum + table[num] < k){
		sum += table[num++];
	}
	cout << num <<endl;
} 
void push(int v){
	stk[top] = v;
	top++;
	int nb = v / 316;
	block[nb]++;
	table[v]++;	
}

void pop(){
	if(top > 0){
		top--;
		printf("%d\n", stk[top]);
		int x = stk[top];
		int x1 = x / 316;
		block[x1]--;
		table[x]--;	
	}
	else printf("Invalid\n");
}
void peekmedian(){
	//找第top/2 (分奇偶)的元素 
	if(top == 0) printf("Invalid\n");
	else if(top%2==0){
	//top为偶数,找第top/2大元素 
		findK(top/2);
	} 
	else{
	//top为奇数,找第top+1/2大元素
	 	findK((top+1)/2);
	} 
}

void deal(string s, int v){
	char c = s[1]; 
	switch(c){
		case 'u':
			push(v);
			break;
		case 'o':
			pop();
			break;
		case 'e':
			peekmedian();
			break;
	}
}

int main(){
	scanf("%d", &N);
	string cmd;
	int value;
	for(int i = 0; i < N; ++i){
		cin >> cmd;
		if(cmd == "Push") scanf("%d", &value);
		deal(cmd, value);
	} 
}

  • code2: 使用stl栈
#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
#include <stack>
using namespace std;
const int maxn = 100010;
const int sqr = 316;
int N;
int block[sqr] = {0};
int table[maxn] = {0}; 
stack<int> st; 

void findK(int k){
	int sum = 0;
	int id = 0;
	while(sum + block[id] < k){
		sum += block[id++];
	}
	int num = sqr * id;
	while(sum + table[num] < k){
		sum += table[num++];
	}
	printf("%d\n", num);
}
void push(int v){
	st.push(v);
	block[v / sqr]++;
	table[v]++;
}
void pop(){
	int x = st.top();
	st.pop();
	block[x / sqr]--;
	table[x]--;
}

int main(){
	scanf("%d", &N);
	string cmd;
	int value;
	for(int i = 0; i < N; ++i){
		cin >> cmd;
		if(cmd == "Push"){
			scanf("%d", &value);
			push(value);	
		} 
		else if(cmd == "Pop"){
			if(st.empty()) printf("Invalid\n");
			else{
				int x = st.top();
				pop();
				printf("%d\n", x);
			} 
		}
		else{
		//PeekMedian
			int n = st.size();	
			if(n == 0) printf("Invalid\n");
			else if(n % 2 == 1) findK((n + 1) / 2);
			else findK(n/2); 
		}
	} 
	return 0;
}


  • T2 code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010, sqr = sqrt(100010.0);
int block[sqr], table[maxn];
stack<int> st;

bool GetMed(int n){
	int sum = 0;
	n = ceil(1.0 * n / 2);
	if(n == 0) return false;
	for(int i = 0; i < sqr; ++i){
		if(sum + block[i] < n){
			sum += block[i];
		}else{
			for(int j = i * sqr; j < (i + 1) * sqr; ++j){
				if(sum + table[j] < n){
					sum += table[j];
				}else{
					printf("%d\n", j);
					return true;
				}
			}
		}
	}
	return false;
}

int main(){
	int n, tmp;
	scanf("%d", &n);
	for(int i = 0; i < n; ++i){
		string com;
		cin >> com;
		if(com[1] == 'u'){	//push
			scanf("%d", &tmp);
			st.push(tmp);
			table[tmp]++;
			block[tmp / sqr]++;
		}else if(com[1] == 'o'){	//pop
			if(!st.empty() && table[st.top()] > 0){
				tmp = st.top();
				printf("%d\n", tmp);
				st.pop();
				table[tmp]--;
				block[tmp / sqr]--;
			}else{
				printf("Invalid\n");
			}
		}else{	//med
			if(GetMed(st.size()) == false){
				printf("Invalid\n");
			}
		}
	}
	return 0;
}
  • 思路 2:树状数组法

  • push: 每入栈一个元素x,更新树状数组 下标[x] +1;

  • pop: 每出栈一个元素,更新树状数组 下标[x] -1;

  • GetMed:树状数组中GetSum(x) 可以得到位置x前(包括x),所有元素的和(因为每次+1,和即为个数);
    GetSum(x) 为栈中小于等于x 的个数:GetSum(maxn) = st.size(); (因为栈中元素都比maxn小) ;
    GetSum(x) 函数单调不减,故可用二分法找到mid,使得:GetSum(mid) >= st.size() / 2; ,说明栈中小于等于mid的元素个数至少有 st.size() / 2 个,mid就是所求median(栈中第med大的元素)

  • code:

#include <bits/stdc++.h>
#define lowbit(x) (x)&(-x)
using namespace std;
const int maxn = 100010;
int C[maxn];
stack<int> st;
int GetSum(int x){	// <=x 的个数 
	int sum = 0;
	for(int i = x; i > 0; i -= lowbit(i)){
		sum += C[i];
	}
	return sum;
}
void Update(int x, int v){
	for(int i = x; i <= maxn; i += lowbit(i)){
		C[i] += v;
	}
}
int FindMedian(int l, int r, int x){
	while(l < r){
		int mid = (l + r) / 2;
		int mid_sum = GetSum(mid);
		if(mid_sum < x){
			l = mid + 1;
		}else{
			r = mid;
		}
	}
	return l;
} 
int main(){
	int n, tmp;
	scanf("%d", &n);
	for(int i = 0; i < n; ++i){
		string com;	cin >> com;
		if(com[1] == 'u'){
			scanf("%d", &tmp);
			st.push(tmp);
			Update(tmp, 1);
		}else if(com[1] == 'o'){
			if(!st.empty()){
				tmp = st.top();
				printf("%d\n", tmp);
				st.pop();
				Update(tmp, -1);
			}else{
				printf("Invalid\n");
			}
		}else{
			int num = ceil(1.0 * st.size() / 2);
			if(num == 0){
				printf("Invalid\n");
			}else{
				printf("%d\n", FindMedian(1, maxn, num));
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值