L3-002. 堆栈 (线段树求区域第K大值 或 树状数组+二分查找法)

L3-002. 堆栈

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

大家都知道“堆栈”是一种“先进后出”的线性结构,基本操作有“入栈”(将新元素插入栈顶)和“出栈”(将栈顶元素的值返回并从堆栈中将其删除)。现请你实现一种特殊的堆栈,它多了一种操作叫“查中值”,即返回堆栈中所有元素的中值。对于N个元素,若N是偶数,则中值定义为第N/2个最小元;若N是奇数,则中值定义为第(N+1)/2个最小元。

输入格式:

输入第一行给出正整数N(<= 105)。随后N行,每行给出一个操作指令,为下列3种指令之一:

Push key
Pop
PeekMedian

其中Push表示入栈,key是不超过105的正整数;Pop表示出栈;PeekMedian表示查中值。

输出格式:

对每个入栈指令,将key入栈,并不输出任何信息。对每个出栈或查中值的指令,在一行中打印相应的返回结果。若指令非法,就打印“Invalid”。

输入样例:
17
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
PeekMedian
Pop
Pop
Pop
Pop
输出样例:
Invalid
Invalid
3
2
2
1
2
4
4
5
3
Invalid
// 线段树求区域第K大值
#include <iostream> 
#include <stack>
#include <string>
using namespace std;
struct node {
	int left, right, value;
}tree[4 * 100005];

// i为当前操作的树节点的坐标,left,right为区间的范围。
void build(int i, int left , int right) {
	tree[i].left = left;
	tree[i].right = right;
	tree[i].value = 0;
	if (tree[i].left == tree[i].right)
		return;
	int mid = (tree[i].left + tree[i].right) / 2;
	build(i * 2, left, mid);
	build(i * 2 + 1, mid + 1, right);
}
// i为当前操作的树节点的坐标,left,rgiht为区间的范围,pos为出栈的值,这里的value为1就是push,-1为pop;
void update(int i, int left, int right, int pos, int value) {
	if (tree[i].left == tree[i].right && tree[i].left == pos) {
		tree[i].value += value;
		return;
	}
	int mid = (tree[i].left + tree[i].right) / 2;
	if (mid >= pos)
		update(i * 2, left, mid, pos,value);
	else
		update(i * 2 + 1, mid + 1, right, pos,value);
	tree[i].value = tree[i * 2].value + tree[i * 2 + 1].value;
}
// i为当前操作的树节点的坐标,left,rgiht为区间的范围,pos为出栈的值
// 这里查询第(N/2)或(N+1)/2 大的值
int query(int i, int left, int right, int pos) {
	if (tree[i].left == tree[i].right)
		return tree[i].left;
	int mid = (tree[i].left + tree[i].right) / 2;
	if (tree[i * 2].value >= pos)
		query(i * 2, left, mid, pos);
	else
		// 如果查询的范围大于现在的范围了,需要把现在右边的范围里的值减掉
		query(i * 2 + 1, mid + 1, right, pos - tree[i * 2].value);
}
int main()
{
	//freopen("data.txt", "r", stdin);
	stack<int> sta;
	int N;
	string str;
	cin >> N;
	// 先将线段树初始化
	build(1, 0, 100005);
	for (int i = 0; i < N; i++) {
		int num;
		cin >> str;
		if (str == "Push") {
			cin >> num;
			sta.push(num);
			update(1, 0, 100005, num, 1);
		}
		else if (str == "Pop")
			if (sta.empty())
				cout << "Invalid" << endl;
			else {
				cout << sta.top() << endl;
				update(1, 0, 100005, sta.top(), -1);
				sta.pop();
			}
		else {
			if (sta.empty())
				cout << "Invalid" << endl;
			else {
				int n = sta.size();
				if (n % 2 == 0)
					n /= 2;
				else
					n = (n + 1) / 2;
				cout << query(1, 0, 100005, n) << endl;
			}
		}
	}
	return 0;
}


// 树状数组+二分查找
// getsum(x)的含义就是栈中不超过x的数有多少个
#include <iostream> 
#include <stack>
#include <string>
#define max 100005
using namespace std;
int C[max] = { 0 };

// 树状数组的标准模版
int lowbit(int t) {
	return t & (-t);
}

void update(int pos, int value) {
	for (int i = pos; i < max; i += lowbit(i))
		C[i] += value;
}

int getsum(int x) {
	int sum = 0;
	for (int i = x; i > 0; i -= lowbit(i))
		sum += C[i];
	return sum;
}

int main()
{
	//freopen("data.txt", "r", stdin);
	stack<int> sta;
	int N;
	string str;
	cin >> N;
	for (int i = 0; i < N; i++) {
		int num;
		cin >> str;
		if (str == "Push") {
			cin >> num;
			sta.push(num);
			update(num, 1);
		}
		else if (str == "Pop")
			if (sta.empty())
				cout << "Invalid" << endl;
			else {
				cout << sta.top() << endl;
				update(sta.top(), -1);
				sta.pop();
			}
		else {
			if (sta.empty())
				cout << "Invalid" << endl;
			else {
				int n = sta.size();
				if (n % 2 == 0)
					n /= 2;
				else
					n = (n + 1) / 2;
				// 这里用二分查找
				int left = 0, right = max;
				while (left < right) {
					int mid = (right + left) / 2;
					if (getsum(mid) >= n)
						right = mid;
					else
						left = mid + 1;
				}
				cout << left << endl;
			}
		}
	}
	return 0;
}




发布了314 篇原创文章 · 获赞 27 · 访问量 8万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览