PAT A 1057. Stack (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 (<= 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

 

即对堆栈增加一个返回中间数的功能。

需要考虑PeakMedian的时间代价。如果单纯每次进行排序然后输出,代价极限为n^2*log(n)肯定不行。

代码的思路是保存1、中间的数2、中间的数是从小到大的第几个数3、各个数出现的次数,每次维护这些数据。代码中需要调节中间的数的时候采用了效率比较低的方法,使得整体的极限代价为n^2,如果通过一个set来记录出现过的数,并通过这个set来查找,并维护这个set,可以把整体代价降低到n*log(n)

测试漏掉了一种情况:弹出了中间数,且中间数不唯一。如2,3,3,3,3,3,4,5,6多次弹出3后中间数是会变的!!!代码没有考虑这个问题时,也能通过

 

代码:

#include <iostream>
#include <cstdio>
#include <stack>
#include <cstring>
using namespace std;

int main()
{
	int n;
	stack<int> s;	//堆栈
	int *flag=new int[100000];	//记录各个数出现的次数
	int i;
	for(i=0;i<100000;i++)
		flag[i]=0;

	cin>>n;
	char c[12];	//指令

	int mid;	//中间的数
	int mid_id;	//中间的数是从小到大排序的第几个数
	int temp;
	for(i=0;i<n;i++)
	{
		scanf("%s",c);
		if(strcmp(c,"Push")==0)	//PUSH
		{
			scanf("%d",&temp);
			if(s.empty())	//堆栈为空
			{
				s.push(temp);
				mid=temp;
				mid_id=1;
				flag[mid]=1;
			}
			else	//堆栈非空
			{
				s.push(temp);
				flag[temp]++;
				if(temp<mid)	//压入的数小于中间的数
				{
					mid_id++;
					if(mid_id>(s.size()+1)/2)
					{
						mid--;
						while(flag[mid]==0)
							mid--;
						mid_id-=flag[mid];
					}
				}
				else if(temp>mid)	//压入的数大于中间的数
				{
					if(mid_id+flag[mid]-1<(s.size()+1)/2)
					{
						mid_id+=flag[mid];
						mid++;
						while(flag[mid]==0)
							mid++;
					}
				}
			}
		}
		else if(strcmp(c,"Pop")==0)	//POP
		{
			if(s.empty())	//堆栈为空
				printf("Invalid\n");
			else	//堆栈非空
			{
				temp=s.top();
				flag[temp]--;
				s.pop();
				printf("%d\n",temp);
				if(temp<mid)	//弹出的数小于中间的数
				{
					mid_id--;
					if(mid_id+flag[mid]-1<(s.size()+1)/2)
					{
						mid_id+=flag[mid];
						mid++;
						while(flag[mid]==0)
							mid++;
					}
				}
				else if(temp>mid)	//弹出的数大于中间的数
				{
					if(mid_id>(s.size()+1)/2)
					{
						mid--;
						while(flag[mid]==0)
							mid--;
						mid_id-=flag[mid];
					}
				}
				else if(s.empty())	//堆栈为空
					continue;
				else if(flag[mid]==0)	//弹出了中间的数,且中间的数是唯一的
				{
					if(mid_id<=(s.size()+1)/2)
					{
						mid++;
						while(flag[mid]==0)
							mid++;
					}
					else if(mid_id>(s.size()+1)/2)
					{
						
						mid--;
						while(flag[mid]==0)
							mid--;
						mid_id-=flag[mid];
					}
				}
				else	//弹出了和中间数,且中间数不唯一。如2,3,3,3,3,3,4,5,6多次弹出3后中间数是会变的
				{		//!!!测试代码有漏洞,漏掉了这种情况。一开始代码没有加这部分,也能通过。
					if(mid_id+flag[mid]-1<(s.size()+1)/2)
					{
						mid_id+=flag[mid];
						mid++;
						while(flag[mid]==0)
							mid++;
					}
					else if(mid_id>(s.size()+1)/2)
					{
						mid--;
						while(flag[mid]==0)
							mid--;
						mid_id-=flag[mid];
					}
				}
			}
		}
		else if(strcmp(c,"PeekMedian")==0)	//输出中间数
		{
			if(s.empty())
				printf("Invalid\n");
			else
				printf("%d\n",mid);
		}
	}

	delete [] flag;

	return 0;
}


 

 

 

 

你可能感兴趣的:(C++,pat)