pat 1057. Stack (30) 浙江大学复试上机最后一题

pat 1057. Stack (30)

时间限制
100 ms
内存限制
32000 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

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 (<= 105). 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 105.

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

这道题目是pat当中稍有的和特定数据结构有关的算法题,大多数人使用树状数组来解,而初次接触树状数组又比较难理解。并且这道题目用的不仅仅是树状数组,还用了其做二分查找,感觉没有经过竞赛训练的人,难以想出这个方法。
一个树状数组的解释如下。
http://blog.csdn.net/shahdza/article/details/6314818
一般看树状数组的基本理解,是无法做这道题目的。本题目当中,每一个树状数组上的值其意义是其管辖区域的数字所出现的次数。
对于代码当中的一些函数的意思,我都进行了注释,应该能够看懂。不懂可以进行讨论。

//18:45
#include <stdio.h>
#include <string.h>
#include <vector>

using namespace std;
#define MAX 120000
//树状数组大家看解释可以明白,是在log(n)的复杂度之内,对数组进行区域求和,修改某个值。
//看之前,大家要明白,树状数组的含义。即是,数组上的没一个数字,其含义并不是一数字,而是管辖着某一范围的数组
//他是他所管辖区域所有数组的和,这个被管辖的实际数组并不存在,只存在概念当中,当然,也可以用真是的数组表示
//这个数字所管辖的范围是 sn = a[n+1-2^k]...a[n].k代表n的二进制末尾零的个数。
//树状数组三个很重要的函数是 lowbit(),sum,update();
//每一个函数的意思是 lowbit(x) = x&(-x).lowbit(x)就是求x的2^k. x+lowbit(x)就到了管辖他的父节点。
//x-lowbit(x)就到了他所在区域的下一个区域。sum(x)求x之前,实际数组所有数字的和,实际数组也是那个概念当中的数组
//update(x)表示修改实际数组的某一个数字,但是其所有父节点都需要更改
//而本题树状数组,利用的是统计的特性。数组上的每一个数字代表该以该索引为值的数字出现过的次数,初始时没有数字
//所以全部都是零,每插入一个数字,那么以这个数字为索引的值便加一。没删除一个数字,该索引上的值便减一。
//由于树状数组的特性,管辖这个数字的所有父节点.也都要+1,这样就可以保证数字是有序的。
//对于查找中位数,利用的并非纯粹的二分查找。只有当当前值大于现在所求的和,才进行向右查找。小于或者等于的情况,都是
//向左查找。最后找到一个临界值,把l输出便是结果。
struct Tree
{
	vector<int> tree;
	Tree()
	{
		tree = vector<int>(MAX,0);
	}
	//求2^k,k为x的末位连续0的个数
	int lowerBit(int x)
	{
		return x&(-x);
	}
	//更新实际某个数组的值,在这道题当中。1代表num增加多了一个,然后其父节点都得统计+1
	//-1代表num少了一个,然后其父节点都得统计-1;
	void update(int num,int val)
	{
		while(num<MAX)
		{
			tree[num] += val;
			num += lowerBit(num);
		}
		return ;
	}
	//统计num之前总共有多少个数字,而中位数就是get(num)==n/2的那个,
	int getSum(int num)
	{
		int sum = 0;
		while(num>0)
		{
			sum += tree[num];
			num -= lowerBit(num);
		}
		return sum;
	}
	//二分查找,在index大于sum的情况下,向右移动。index小于或者等于sum,都向左移动。
	//最后根据二分查找的原理,能够找到恰好和是index的那个索引
	int find(int index)
	{
		int l = 1,h=MAX-1,tmp=0,mid;
		while(l<=h)
		{
			mid = (l+h)/2;
			tmp = getSum(mid);
			// can only judge the minimal one
			if(tmp<index)
			{
				l = mid+1;
			}else
			{
				h = mid-1;
			}
		}
		return l;
	}
};
Tree t;
int main()
{
	int n,tmp;
	int i,j;
	char ope[13];
	vector<int> sta;
	//freopen("pat1057engTest.txt","r",stdin);
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		scanf("%s",ope);
		if(strcmp(ope,"Pop")==0)
		{
			if(sta.size()==0)
			{
				printf("Invalid\n");
			}else
			{
				tmp = sta.back();
				sta.pop_back();
				t.update(tmp,-1);
				printf("%d\n",tmp);
			}
		}else if(strcmp(ope,"PeekMedian")==0)
		{
			if(sta.size()==0)
			{
				printf("Invalid\n");
			}else
			{
				tmp = t.find(1+(sta.size()-1)/2);
				printf("%d\n",tmp);
			}
		}else if(strcmp(ope,"Push")==0)
		{
			scanf("%d",&tmp);
			sta.push_back(tmp);
			t.update(tmp,1);

		}else
		{
			printf("Invalid\n");
		}
	}
	return 0;
}


你可能感兴趣的:(数据结构,二分查找,算法)