首页 > 试题广场 >

Stack (30)

[编程题]Stack (30)
  • 热度指数:5385 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
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.

输入描述:
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.


输出描述:
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.
示例1

输入

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
推荐

 

以上内容来自我的博客http://blog.csdn.net/sinat_29278271,博客的名字和牛客网ID相同,都是Uncle_Sugar,这是我的第一篇博文,希望大家喜欢
     题目的大意是维护一个能随时返回中位数的栈,这个问题其实可以直接简化为维护一个能返回中位数同时支持插入 和删除的数据结构。本身解法比较多样,网上能找到的解法有三种,个人感觉都很有启发性,所以加上我的解法在此 做个总结。

   因为题目中说明每个数据都在[1,100000]之间,所以很朴素的一种解法就是设立一个Count[100005]。插入n的

时候Count[n]++,删除的时候Count[n]--,查找的时候遍历数组,寻找前缀和为(size+1)/2的下标。但是10^5

本身是个比较大的数字,在多次查找之后果断超时了,怎么办呢?当然要做优化。

1.树状数组+二分查找

      树状数组(Binary Indexed Tree(BIT))是一种能高效查找前缀和的数据结构,具体实现原理见鄙人还没写好的拙作 《树状数组的复习》。使用树状数组是为了能进行二分查找,原先遍历Count数组,最多的时候能遍历10^5次,运 用二分查找可以将查找次数优化为lg(10^5)/lg(2)  < 15

下面是代码

<span style="font-family:Comic Sans MS;font-size:14px;">
# include <cstdio>
# include <stack>
using namespace std;
class BIT
{
private:
    int *Elem;
    int Size;
    int lowbit(int n)
    {
	    return n&(-n);
	}
public:
    BIT(int size):Size(size+1)  /*想想看还是+1好了,要不申请了100的空间只能用到99感觉太奇怪了*/
    { 
	    Elem = new int[Size];
	    for (int i=0;i<Size;i++)/*还没试过用memset初始化,下次试试*/
	        Elem[i] = 0;
	}
	int GetSum(int right)/*[0,right]*/
	{
	    int sum = 0;
	    while (right)
	    {
		    sum += Elem[right];
		    right -= lowbit(right);
		}
		return sum;
	}
	int GetSum(int left,int right)/*[left,right]*/
	{
	    return GetSum(left-1) - GetSum(right);
	}
    void Add(int value,int index)
    {
	    while (index < Size)
		{
		    Elem[index] += value;
		    index += lowbit(index);
		} 
	}
    ~BIT()
    {
      delete[] Elem;
	}
};
BIT bit(100000);
int getmid(int size)
{
	int index = (size+1)/2;
	int left = 1,right = 100000,mid;
    while(left<right)
	{
        mid = (left+right)/2;
        if(bit.GetSum(mid)<index)
            left = mid+1;
        else
            right = mid;
    }
    return left;
}
int main()
{
  int n,tmp;
  scanf("%d",&n);
  stack<int> s;
  char str[10];
  while (n--)
  {
      scanf("%s",str);
      switch(str[1])
      {
	      case 'e':
		      {
			  if (s.empty()) 
			      printf("Invalid\n");
			  else 
			      printf("%d\n",getmid(s.size()));
			  break;
			  }
	      case 'o':
		      {
    	      if (s.empty())
    	          printf("Invalid\n");
	          else 
                  {
          	      tmp = s.top();s.pop();
          	      printf("%d\n",tmp);
                  bit.Add(-1,tmp);
				  }
			  break;
			  }
	      case 'u':
		  	  {
	              scanf("%d",&tmp);s.push(tmp);
                  bit.Add(1,tmp);
			  }
			  break;
	  }
  }
  return 0;
}</span>

 

2.分桶法(分治,分层HASH,平方分割)本人快乐

的原创

     分桶法的基本思路是分治,在一开始的暴力解法中,我们可以认为Count数组是一个大的桶,这个大的桶里有 5*10^5个小桶,每个小桶能装一个数,在分桶法中,我们建立多个大桶,每个桶中又有小桶,比如,我们建立多个 500个大桶,每个桶的容量是100,同时记录每个大桶中存放的数据的个数,在查找的时候我们可以通过每个大桶中 元素的快速定位到放置中位数的那个小桶。当然你可以认为这是一种HASH,hash(key) = key/10。
     设每个大桶中含有k个小桶,共有m个大桶,m*k = n为定值。则一开始我们需要遍历大小为m的大桶数组,后来要 遍历大小为k单个大桶,时间复杂度为O(max(k,m))在n*k为定值的情况下,易知m = k = (m*k)^(1/2)的时 候效率最高为n^(1/2)。

   本题中为了方便,采用分层hash的策略,将值为key的元素放入bucke[k/100][k%100]中。
# include <cstdio>
# include <stack>
using namespace std;

const int _size = 100000;
const int capi  = 500;
int bucket[_size/capi][capi];
int count[_size/capi];
int getmid(int size)
{
	int ind = (size+1)/2,cnt=0,i,j;
    for (i=0;i<_size/capi;i++)
        {
		if (cnt + count[i]>=ind)
            break;
		cnt += count[i];
		}
	for (j=0;j<capi;j++)
	    {
		cnt += bucket[i][j];
	    if (cnt>=ind)
	        return j+i*capi;
		}
    
}
char str[10];
int main()
{
  int n,tmp;
  scanf("%d",&n);
  stack<int> s;
  while (n--)
  {
      scanf("%s",str);
      switch(str[1])
      {
	      case 'e':
		      {
			  if (s.empty()) 
			      printf("Invalid\n");
			  else 
			      printf("%d\n",getmid(s.size())+1);
			  break;
			  }
	      case 'o':
		      {
    	      if (s.empty())
    	          printf("Invalid\n");
	          else 
                  {
          	      tmp = s.top();s.pop();
          	      printf("%d\n",tmp);
          	      tmp--;
			      bucket[tmp/capi][tmp%capi]--;
			      count[tmp/capi]--;
				  }
			  break;
			  }
	      case 'u':
		  	  {
	              scanf("%d",&tmp);s.push(tmp);
                  tmp--;
                  bucket[tmp/capi][tmp%capi]++;
			      count[tmp/capi]++;
			  }
			  break;
	  }
  }
  return 0;
}  

 

最后我想说的就是,

1. 这个方法和树状数组 + 二分的方法并无矛盾,你同样可以用树状数组优化大桶元素的前缀和。

2. 还有就是如果你乐意你完全可以多分几个层玩 , 比如 key 放在 bucket[...][...][...], 分层分多了以后,你会发现这个桶变成了一棵树,如果你分层的依据是二分法,你还会发现,你分出了一棵线段树。

3. 如果数据范围增大,你可以修改 hash 使其映射到更小的空间,同时将每个大桶改为 vector<int> 数组,查询是对每个 vector<int> 中的元素排序,个人感觉不会很慢   

3.线段树(分治)有种杀鸡用牛刀的感觉

           线段树是个霸气的数据结构,基本上包含了分桶法和树状数组的全部功能。线段树的基础思想是分治,但是时间
复杂度上比分桶法更加高效,能将时间优化到O(lgn)然而在PAT的小数据之下,普通的线段树因为常数上的差距花
费的时间更长。具体的树的创建我就不说了,这里采用一点zkw线段树的思想,直接找到树的叶子自底向上走到树根,每个节点维护一个Cnt记录经过这里的路径的个数,查找中位数的时候根据每个节点的Cnt进入合适的子树进行查找。
 
# include <cstdio>
# include <stack>
using namespace std;

typedef int Node;
class zkw_segtree
{
private:
    Node *T;
	int size;
public:
    zkw_segtree(int range)
	{
	    for (size = 1;size < range+2;size<<=1);
	    T = new Node[2*size];
	    for (int i=1;i<size+size;i++)
		    T[i] = 0;    
	} 
	void Add(int value,int index)
	{
		index += size;
	    for (T[index]+=value,index>>=1;index>0;index>>=1)
            T[index] = T[index<<1]  + T[(index<<1) + 1];
	}
	int Query(int s,int t)
	{
	    int ret = 0;
	    for (s+=size-1,t+=size-1;s^t^1;s>>=1,t>>=1)
	        {
			  if (~s^1) ret += T[s^1];
			  if (t^1)  ret += T[t^1]; 
			}
	    return ret;
	}
	int Find_Kth(int k,int root = 1)
	{
	    while (root<<1 < size<<1)
		{
		  if (T[root<<1]>=k)  root = root<<1;
          else 
		    {
			  k -= T[root<<1];
			  root = (root<<1) + 1;
		    }
		}		    
	    return root - size;
	}
	~zkw_segtree()
	{
	    delete[] T;
	}
};
zkw_segtree segtree(100000);
int main()
{
  int n,tmp;
  scanf("%d",&n);
  stack<int> s;
  char str[10];
  while (n--)
  {
      scanf("%s",str);
      switch(str[1])
      {
	      case 'e':
		      {
			  if (s.empty()) 
			      printf("Invalid\n");
			  else 
			      printf("%d\n",segtree.Find_Kth((s.size()+1)/2));
			  break;
			  }
	      case 'o':
		      {
    	      if (s.empty())
    	          printf("Invalid\n");
	          else 
                  {
          	      tmp = s.top();s.pop();
          	      printf("%d\n",tmp);
                  segtree.Add(-1,tmp);
				  }
			  break;
			  }
	      case 'u':
		  	  {
	              scanf("%d",&tmp);s.push(tmp);
                  segtree.Add(1,tmp);
			  }
			  break;
	  }
  }
  return 0;
}

    
      测试完发现是最快的,不愧是zkw大神的杰作

3.Prioriry Queue On Multiset(红黑树是支持插入与删除的堆)真正的牛刀

        讲了那么多终于讲到最终的解法了,前三种方法归根结底是利用了输入数据范围有限这一条件,要想完美解决这一问题,我们不得不借助插入查找删除效率都为O(lgn)的高级搜索树。
      有一种利用堆查找第K个元素的算法,维持一个大顶堆,一个小顶堆,将K个元素压入小顶堆,其余压入大顶堆,随后如果大顶堆的堆顶元素大于小顶堆的堆顶元素,就将将两个堆得元素弹出并压入另一个堆中,直到大顶堆的堆顶元素小于小顶堆的堆顶元素。这样小顶堆的堆顶元素就是第K个元素。
       然而对于这道题,这种算法并没有什么用处,这道题要求随时删除某一个元素,然而堆并不具备这种功能 废话,能随意删除某个元素还叫堆吗 )。
       犹记得当初学数据结构的时候,是先教的高级搜索树,后教的优先队列。老师在教优先队列的时候,为了引出堆,说,其实想要实现优先队列,用高级搜索树是完全可以的,但高级搜索树实在是太强大了,实现也比较复杂,我们需要一种更简单高效的数据结构去实现优先队列。
    于是传说中的堆诞生了,因为堆在结构上的特点,我们也无法做到随意删除其中的某个元素。
    然而此时我们需要一个能删除任意元素的优先队列,OK,是时候启动最终形态了,优先级队列超进化,红黑优先级队列。以上文字过于中二,请谨慎阅读。
    从时间复杂度上分析,插入查找和删除都是O(lgn)这里的n是指总元素的个数。
    然后,估计大家都知道要怎么做了,下面是代码,为了简单起见,并没有扩展出寻找第K个数的功能,而是直接寻找中位数。
# include <cstdio>
# include <stack>
# include <set>
using namespace std;

const int debug = 1;
typedef int T;
class Find_Median
{
private:
    multiset<T,greater<T> > maxheap;
	multiset<T,less<T> > minheap;
public: 
    void Push(T data)
	{
	    if (maxheap.size() < minheap.size()) 
            maxheap.insert(data);
        else 
		    minheap.insert(data);
	}
	bool Erase(T data)
	{
		multiset<T>::iterator it;
        if ((it=maxheap.find(data))!=maxheap.end())  
		    maxheap.erase(it);
		else if ((it=minheap.find(data))!=minheap.end()) 
	 	    minheap.erase(it);
        else 
            return false;
        return true;
	}
	T Query()
	{
		while (maxheap.size() < minheap.size())  
        {
            maxheap.insert(*minheap.begin());
            minheap.erase(minheap.begin());
		}
		while (minheap.size() < maxheap.si敏感词heap.insert(*maxheap.begin());
			maxheap.erase(maxheap.begin());   
		}
		
		if (maxheap.size()==0) return *minheap.begin();
		if (minheap.size()==0) return *maxheap.begin();
		
        multiset<T>::iterator maxtop = maxheap.begin();     	
	    multiset<T>::iterator mintop = minheap.begin(); 
	    while (*maxtop > *mintop)
	    {
            maxheap.insert(*mintop);
			minheap.insert(*maxtop);
            maxheap.erase(maxtop); 					
		    minheap.erase(mintop);
		    maxtop = maxheap.begin(); 
		    mintop = minheap.begin(); 
		}
        return *(maxheap.size() >= minheap.size()?maxtop:mintop);
	}   
};
Find_Median FM;
int main()
{
  int n,tmp;
  scanf("%d",&n);
  stack<int> s;
  char str[10];
  while (n--)
  {
      scanf("%s",str);
      switch(str[1])
      {
	      case 'e':
		      {
			  if (s.empty())  printf("Invalid\n");
			  else 
                  printf("%d\n",FM.Query());
			  break;
			  }
	      case 'o':
		      {
    	      if (s.empty())  printf("Invalid\n");
	          else 
                  {
          	      tmp = s.top();s.pop();
          	      printf("%d\n",tmp);
                  FM.Erase(tmp);
				  }
			  break;
			  }
	      case 'u':
		  	  {
	              scanf("%d",&tmp);s.push(tmp);
                  FM.Push(tmp);
			  }
			  break;
	  }
  }
  return 0;
}

看起来是最慢的,一半怪stl太慢,一半怪我的实现太渣
    
编辑于 2015-08-18 21:40:33 回复(6)
import java.util.Scanner;
import java.util.Stack;
public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		Stack<Integer> stack = new Stack<Integer>();
		int[] count = new int[100001];
        int n = in.nextInt();
		for (int i = 0; i < n; i++) {
			String command = in.next();
			if (command.charAt(1)=='u') {
				int num = in.nextInt();
				stack.add(num);
				count[num]++;
			} else if (command.charAt(1)=='o') {
				if (stack.isEmpty())
					System.out.println("Invalid");
				else {
					int m = stack.pop();
					count[m]--;
					System.out.println(m);
				}
			} else {
				if (stack.isEmpty())
					System.out.println("Invalid");
				else
					System.out.println(getMid(count,(stack.size()+1)/2));
			}
		}
	}
	public static int getMid(int[] count, int mid) {
		int sum = 0;
		for (int i = 0; i < count.length; i++) {
			sum += count[i];
			if (sum >= mid)
				return i;
		}
		return 0;
	}
}
试了用解题报告的方法解,结果超时了。。
import java.util.Scanner;
import java.util.Stack;
import java.util.TreeSet;
public class Main {
	private static TreeSet<Int> s1 = new TreeSet<Int>();
	private static TreeSet<Int> s2 = new TreeSet<Int>();
	private static Stack<Integer> stack = new Stack<Integer>();
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		for(int i = 0;i<n;i++){
			String command = in.next();
			if(command.charAt(1)=='o'){
				if(stack.isEmpty())
					System.out.println("Invalid");
				else{
					int val = stack.pop();
					remove(new Int(val,true));
					System.out.println(val);
				}
			}else if(command.charAt(1)=='e'){
				if(stack.isEmpty())
					System.out.println("Invalid");
				else
					System.out.println(PeekMedian());
			}else{
				int val = in.nextInt();
				stack.add(val);
				add(new Int(val));
			}
		}
	}
	public static void add(Int x) {
	    if (s1.size() == s2.size()) {
	        s2.add(x);
	        s1.add(s2.pollFirst());
	    }
	    else {
	        s1.add(x);
	        s2.add(s1.pollLast());
	    }
	    
	}
	private static void remove(Int val){
		boolean InS2 = s2.contains(val);
		if(s1.size()==s2.size()){
			if(InS2){
				s2.remove(val);
			}else{
				s1.remove(val);
	            s1.add(s2.pollFirst());
			}
		}else if (!InS2) {
	        s1.remove(val);
	    }else{
	    	s2.remove(val);
	        s2.add(s1.pollLast());
	    }
	}
	private static int PeekMedian() {
		return s1.last().val;
	}
	
	private static class Int implements Comparable<Int>{
		int val;
		boolean isRemove = false;
		public Int(int val) {
			this.val = val;
		}
		public Int(int val, boolean isRemove) {
			this.val = val;
			this.isRemove = isRemove;
		}
		@Override
		public int compareTo(Int o) {
			if(this.val>o.val)
				return 1;
			else if(this.val==o.val){
				if(this.isRemove)
					return 0;
			}
			return -1;
		}
	}
}




编辑于 2016-07-17 20:56:39 回复(0)
//就比赛而言肯定首选multiset啊
//关键坑:如果multiset的erase()参数为值,则会删除所有这个值的元素
//正确姿势:应该是找到一个要删除的值的元素,用迭代器作为erase()参数
#include <iostream>
#include <vector>
#include <set>
#include <string>
using namespace std;

#define MAX 150000
int N; vector<int> S; multiset<int> m;
int res[MAX];

int main()
{
	cin >> N; string op; int e, n;
	fill(res, res + N + 100, -1);
	for (int i = 0; i < N; ++i) {
		cin >> op;
		if (op == "Push") {
			cin >> e; S.push_back(e);
			m.insert(e);
		}
		else if (S.empty())
			res[i] = -2;
		else if (op == "Pop") {
			res[i] = e = S.back();
			S.pop_back();
			m.erase(m.find(e));
		}
		else if (op == "PeekMedian") {
			n = m.size(); 
			if (n % 2 == 0) n /= 2;
			else n = (n + 1) / 2;
			auto it = m.begin();
			for (int j = 0; j < n - 1; ++j)++it;
			res[i] = *it;
		}
	}
	for (int i = 0; i < N; ++i) {
		if (res[i] != -1) {
			if (res[i] != -2)cout << res[i];
			else cout << "Invalid";
			if (i < N - 1)cout << endl;
		}
	}
	return 0;
}


发表于 2021-01-19 01:29:18 回复(3)
去年用纯c做过今年用c++重做的时候却不会做了
看了看去年的思路很巧妙,这里没人用过,贴一下。
(已用c++重写)
#include<iostream>
#include<vector>
using namespace std;

int i,j,N,num[262144]={0};//262144=2^18,num[0]舍弃不要,num[1]作为堆的根
//用堆维护栈中数字的数量,num[i]表示以结点i为根的堆中数字数量
//叶节点131072~231072分别代表真实数字0~100000,叶节点231072~262144闲置
//插入新数字时,从叶节点开始向上向堆根维护
vector<int>v;

int main()
{
	cin>>N;
	for(string c;N--;)
	{	cin>>c;
		if(c=="Push"){
			cin>>i;
			v.push_back(i);
			for(i+=131072;i;i/=2)num[i]++;
		}
		else if(!num[1])cout<<"Invalid\n";
			else if(c=="Pop"){
					i=*(v.end()-1);
					v.pop_back();
					cout<<i<<endl;
					for(i+=131072;i;i/=2)num[i]--;
				}
				else{
					for(i=1,j=(num[1]+1)/2;(i*=2)<262144;)if(num[i]<j)j-=num[i++];
					cout<<i/2-131072<<endl;
				}
	}
}


发表于 2020-01-10 15:38:35 回复(0)
本题的关键是找中值(第k大)。
这是 树状数组(Binary indexed tree)或 线段树 的一个经典应用。
cnt[i] i 在栈中的个数,prf[i]cnt[i] 的前缀和 ,那么最先满足 prf[i] >= k  的 i 就是第k大了。
直接维护 cnt 数组是O(n),显然会超时。。
而使用上面两个数据结构便可以完成O(logn) 维护与查询。
线段树网上已经很多优秀资料了。
树状数组推荐这篇 (Topcoder那篇经典教程的译文

弱贴一个线段树的。。

#include <bits/stdc++.h>
using namespace std;
typedeflonglongLL;
#define rep(i, s, t)for(int(i)=(s);(i)<=(t);++(i))
#define Mod1000000007
 
constintN = 1e5;
 
#define lc o<<1
#define rc o<<1|1
 
struct node {
    intl, r, v;
};
 
node tree[N*4+5];
 
voidseg_build(into,intl,intr) {
    tree[o] = {l, r,0};
    if( l != r ) {
        intm = (l+r)>>1;
        seg_build(lc, l, m);
        seg_build(rc, m+1, r);
    }
}
 
voidseg_modify(into,intx,intv) {
    intl = tree[o].l, r = tree[o].r;
    if( l == r ) {
        tree[o].v += v;
        return;
    }
    intm = (l+r)>>1;
    if( x <= m )
        seg_modify(lc, x, v);
    else
        seg_modify(rc, x, v);
    tree[o].v = tree[lc].v + tree[rc].v;
}
 
intseg_query(into,intk) {
    if( k > tree[o].v )
        return-1;
    if( tree[o].l == tree[o].r )returntree[o].l;
    if( k <= tree[lc].v )
        returnseg_query(lc, k);
    else
        returnseg_query(rc, k-tree[lc].v);
}
 
intmain() {
    //freopen("input.in", "r", stdin);
    intt; cin >> t;
    string op;
    stack<int> stk;
    seg_build(1,1, N);
    intx;
    while( t-- ) {
        cin >> op;
        if( op =="Pop") {
            if( stk.empty() ) {
                cout <<"Invalid\n";
                continue;
            }
            x = stk.top(); stk.pop();
            cout << x <<'\n';
            seg_modify(1, x, -1);
        }elseif( op =="Push") {
            cin >> x;
            //cout << "Push " << x << endl;
            stk.push(x);
            seg_modify(1, x,1);
        }else{
            if( stk.empty() ) {
                cout <<"Invalid\n";
                continue;
            }
            intm = ( stk.size() +1) >>1;
            cout << seg_query(1, m) <<'\n';
        }
    }
 
    return0;
}


编辑于 2015-07-05 20:13:27 回复(0)
/*单点更新,区间查询*/
#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5 + 66 ;
int c[AX] ; 
int lowbit( int x ){
	return x & (-x) ; 
}
void update( int x , int v ){
	while( x <= AX ){
		c[x] += v ; 
		x += lowbit(x);
	}
}
int querry( int x ){
	int ans = 0 ;
	while( x ){
		ans += c[x] ;
		x -= lowbit(x) ; 
	}
	return ans ; 
}

int main() {
	int n ;
	char op[20] ;
	scanf("%d",&n) ;
	stack<int>s ;
	int x ;
	for( int i = 0 ; i < n ; i++ ) {
		scanf("%s",op);
		if( op[1] == 'u' ) { //push
			scanf("%d",&x);
			s.push(x);
			update( x , 1 ) ;
		}
		int len = s.size() ;
		if( !len ) {
			printf("Invalid\n");
			continue ;
		}
		if( op[1] == 'o' ) { //pop
			printf("%d\n",s.top());
			update( s.top() , -1 );
			s.pop();
		}
		if( op[1] == 'e' ) { //peekMedian
			int l = 0 , r = AX ;
			int id = ( s.size() + 1 ) >> 1 ; 
			while( l <= r ){
				int mid = ( l + r ) >> 1 ; 
				if( querry(mid) >= id ) r = mid - 1 ;
				else l = mid + 1 ;
			}
			printf("%d\n",l) ;
		}
	}
	return 0 ;
}

发表于 2020-02-19 15:44:27 回复(0)
看完觉得是树状数组+二分,但树状数组忘了怎么写了。
试图排序水过去,未果,但混了两个测试点。
突发奇想用multiset瞎搞,试了下不行,应该是迭代器相关的原因。
于是去猜树状数组的写法,猜了半天猜出来了,原来这么好写的。
WA的同学注意一下这样的例子:
4
Push 2
Push 2
Push 2
PeekMedian
即二分的时候只要<=mid的数量大于half就可以记录答案。
#include <iostream>
#include <vector>
// #include <set>
#include <algorithm>
#define dbg(x) cout << #x ": " << (x) << endl
using namespace std;
const int MAXN = 112345;

int bit[MAXN];

int lowbit(int x){
    return x & (~x + 1);
}

void update(int key, int value){
    while(key <= MAXN){
        bit[key] += value;
        key += lowbit(key);
    }
}

int get(int key){
    int res = 0;
    while(key){
        res += bit[key];
        key -= lowbit(key);
    }
    return res;
}


int main(){
    int N;
    cin >> N;
    string cmd;
    int value;
    vector<int> stack;
    for(int i = 0; i < N; i++){
        cin >> cmd;
        if(cmd == "Push"){
            cin >> value;
            stack.push_back(value);
            update(value, 1);
        }
        else if(cmd == "Pop"){
            if(stack.empty()){
                cout << "Invalid" << endl;
            }
            else{
                value = stack.back();
                cout << value << endl;
                stack.pop_back();
                update(value, -1);
            }
        }
        else if(cmd == "PeekMedian"){
            if(stack.empty()){
                cout << "Invalid" << endl;
                continue;
            }
            int n = stack.size();
            int half = (n + 1) / 2;
            int l = 0, r = MAXN, mid;
            int ans = -1;
            while(l <= r){
                mid = (l + r) / 2;
                int cnt = get(mid);
                // dbg(l);
                // dbg(r);
                // dbg(mid);
                // dbg(cnt);
                if(cnt >= half){
                    ans = mid;
                    r = mid - 1;
                }
                else{
                    l = mid + 1;
                }
            }
            cout << ans << endl;
        }
    }

}


发表于 2019-08-27 13:26:05 回复(0)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>

using namespace std;

const int MAXN = 100010,sqr=316;
stack<int> S;
int block[sqr];
int table[MAXN];

void Push() 
{     int x;     cin>>x;     table[x]++;     block[x/sqr]++;     S.push(x);
}

void Pop()
{     int x;     if(S.empty())         cout<<"Invalid"<<endl;     else{         x = S.top();         table[x]--;         block[x/sqr]--;         S.pop();         cout<<x<<endl;     }
}

void PeekMedian()
{     if(S.empty())         cout<<"Invalid"<<endl;     else{         int sum=0,index=0;         int len = S.size();         int mid = (len&1)?(len+1)/2:len/2;         while(sum+block[index] < mid)             sum += block[index++];         int num = index*sqr;         while(sum+table[num] < mid)             sum += table[num++];         cout<<num<<endl;     }
}

int main()
{     int n;     cin>>n;     while(n--)     {         string s;         cin>>s;         if(s[1]=='o')             Pop();         else if(s[1]=='u')             Push();         else if(s[1]=='e')             PeekMedian();     }     return 0;
}

发表于 2018-02-23 01:35:54 回复(0)
#include <bits/extc++.h>

using namespace std;
using namespace __gnu_pbds;

constexpr int MAXN = 20;
char s[MAXN];
unordered_map<int, int> vis;

struct Compare {
    bool operator()(const pair<int, int> &lhs, const pair<int, int> &rhs)
    {
        if (lhs.first == rhs.first) {
            return (lhs.second < rhs.second);
        }
        return (lhs.first < rhs.first);
    }
};

tree <pair<int, int> ,null_type, Compare, rb_tree_tag,
        tree_order_statistics_node_update> Rbtree;
vector <pair<int, int> > A;

int main() {
    
    int N;
    scanf("%d", &N);
    int len = 0;
    pair<int, int> temp;

    while (N--) {
        scanf("%s", s);
        if (strcmp(s, "Pop") == 0) {
            if (len == 0) {
                printf("Invalid\n");
            } else {
                temp = A.at(len - 1);
                printf("%d\n", temp.first);
                A.pop_back();
                --len;
                Rbtree.erase(temp);
            }
        } else if (strcmp(s, "PeekMedian") == 0) {
            if (len == 0) {
                printf("Invalid\n");
                continue;
            }
            if (len % 2 == 0) {
                printf("%d\n", Rbtree.find_by_order(len / 2 - 1)->first);
            } else {
                printf("%d\n", Rbtree.find_by_order((len - 1) / 2)->first);
            }
        } else {
            scanf("%d", &temp.first);
            temp.second = vis[temp.first];
            ++vis[temp.first];
            ++len;
            A.push_back(temp);
            Rbtree.insert(temp);
        }
    }
    return 0;
}

红黑树就完事儿了
编辑于 2020-01-24 20:12:27 回复(0)

总览

借鉴对顶堆的方法,map红黑树加指针移动,数据范围可以到10^9,时间复杂度平均,空间最差。允许更大的数据范围(10^9),同时保证与题目相同的时间空间复杂度。

思路和复杂度分析

思路很简单,记录当前中值nowp,和大于以及小于中值的数的个数,L和R。
然后下次进行操作时,如果操作数小于当前中值,对L进行操作(push 则加L,pop则减L),大于时同理。操作后,检查L和R,如果不满足nowp为中值,则变更nowp,如果L多了,则将nowp左移(map的树上中找nowp的前驱),左移时间复杂度为直到L满足条件,因为L最多变1,因此找到一个不为0的树上点就可以了,如果使用erase操作,可以保证只左移一次就可以找到新的中值nowp;R的操作同理。
总时间复杂度为。空间复杂度为map中同时存在的不同键值的数目,也就是。这个方法对map的键值范围没有要求,因此数据范围可以到10^9甚至longlong。

算法对比

与线段树时间复杂度相同,都比树状数组二分的好。
在给定值范围内,空间是逐渐增长的,而不是一开始就开满空间,因此空间比树状数组和线段树都好常数级。
特别,如果值扩展到10^9,本方法是在线算法,不需要存储之前的指令;而其他两种都需要使用离线算法,先存储所有指令对值做离散化。

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
map msave;
int K;
int T,L,R,M;
int nowp;
int main()
{
    int K;
    cin>>K;
    char testc[128];
    stack qs;
    T=0;
    L=R=M=0;
    nowp=-1;
    int pn;
    for(int i=0;i<K;++i)
    {
        scanf("%s",testc);
        if(testc[1]=='o')
        {
            if(!qs.empty())
            {
                pn=qs.top();
                qs.pop();
                --msave[pn];
                --T;
                if(pn<nowp) --L;
                else if(pn>nowp) --R;
                printf("%d\n",pn);
            }
            else printf("Invalid\n");
        }
        else if(testc[1]=='e')
        {
            if(T==0) printf("Invalid\n");
            else printf("%d\n",nowp);
        }
        else
        {
            scanf(" %d",&pn);
            qs.push(pn);
            if(msave.find(pn)!=msave.end()) ++msave[pn];
            else msave[pn]=1;
            ++T;
            if(T==1) nowp=pn;
            else if(pn<nowp) ++L;
            else if(pn>nowp) ++R;
        }
        if(T>0)
        {
            auto it=msave.find(nowp);
            for(;L>=((T+1)/2);)
            {
                R+=it->second;
                --it;
                L-=it->second;
            }
            for(;R>(T/2);)
            {
                L+=it->second;
                ++it;
                R-=it->second;
            }
            nowp=it->first;
        }
    }
    return 0;
}
发表于 2022-08-09 17:51:06 回复(0)


更多PAT甲级题解--acking-you.gtihub.io

最易理解的Stack实现方式,没有树状数组,没有堆!

题目


OJ平台

题目分析

  • 关键就是要我们实现以下这个操作:

    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.
    作用是返回栈中第 N/2 小的元素。

  • 我们可能很快就会想到排序,然后用STL中的栈来实现,一旦需要排序的时候(PeekMedian操作)就直接sort,然后提交后你会发现----它超时了!😂

好吧,我不偷懒了,自己想想怎么实现这一一个栈,我是用的最朴素的方式实现,通过在栈中多加一个数组实现对元素的排序(这个过程由于是每次入栈和出栈进行排序,所以完全可以用二分找到位置进行插入和删除某个元素),后面看到很多大佬,不是用堆就是用树状数组,太强了,我还达不到这样的高度啊🤦‍♂️

  • 以下是我实现的效率(根大佬们的树状数组还是没得比🤣

代码详解

栈的数据结构实现

这里使用的二分是STL自带的,当然你也可以自己写个二分,这样的话,代码长度会变长🤦‍♂️

//@实现一个栈,用两个数组实现。
struct Stack {
    int *nums;                 //用作栈空间
    int *sort_nums;            //将栈元素排序后的情况
    int length;                //栈的长度
    int capcity;               //栈的最大容量

    Stack(int cap) : nums(nullptr), length(0), capcity(cap), sort_nums(nullptr) {
        nums = new int[capcity];
        sort_nums = new int[capcity];
    }

//@关键在于push和pop操作的处理
    void push(int val) {
        if (!isEmpty()) {
            int pos = upper_bound(sort_nums, sort_nums + length, val) - sort_nums;
            for (int i = length; i > pos; i--) {
                sort_nums[i] = sort_nums[i - 1];
            }
            sort_nums[pos] = val;
        } else {
            sort_nums[length] = val;
        }
        nums[length] = val;
        length++;;
    }

    void pop() {
        if (!isEmpty()) {
            int pos = upper_bound(sort_nums, sort_nums + length, nums[length - 1]) - sort_nums;
            for (int i = pos - 1; i < length; i++) {
                sort_nums[i] = sort_nums[i + 1];
            }
        } else {
            cout << "Invalid" << endl;
            return;
        }
        length--;
        cout << nums[length] << endl;
        return;
    }

    void peekMed() {
        if (isEmpty()) {
            cout << "Invalid" << endl;
        } else {
            cout << sort_nums[(length - 1) / 2] << endl;
        }
    }

    bool isEmpty() {
        return length == 0;
    }
} St(100002);

输入数据和问题解决处理

void solve(const string &s) {//根据字符串的第二个字符进行分类操作
    if (s[1] == 'u') {
        int v;
        cin >> v;
        St.push(v);
    } else if (s[1] == 'o') {
        St.pop();
    } else {
        St.peekMed();
    }
}

void Input() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N;
    int n = N;
    string s;
    while (n--) {
        cin >> s;
        solve(s);
    }
}

整合代码进行提交

应该是最易理解的实现方式了吧!

#include <bits/stdc++.h>

using namespace std;
int N;                         //输入的操作数
//@实现一个栈,用两个数组实现。
struct Stack {
    int *nums;                 //用作栈空间
    int *sort_nums;            //将栈元素排序后的情况
    int length;                //栈的长度
    int capcity;               //栈的最大容量

    Stack(int cap) : nums(nullptr), length(0), capcity(cap), sort_nums(nullptr) {
        nums = new int[capcity];
        sort_nums = new int[capcity];
    }

//@关键在于push和pop操作的处理
    void push(int val) {
        if (!isEmpty()) {
            int pos = upper_bound(sort_nums, sort_nums + length, val) - sort_nums;
            for (int i = length; i > pos; i--) {
                sort_nums[i] = sort_nums[i - 1];
            }
            sort_nums[pos] = val;
        } else {
            sort_nums[length] = val;
        }
        nums[length] = val;
        length++;;
    }

    void pop() {
        if (!isEmpty()) {
            int pos = upper_bound(sort_nums, sort_nums + length, nums[length - 1]) - sort_nums;
            for (int i = pos - 1; i < length; i++) {
                sort_nums[i] = sort_nums[i + 1];
            }
        } else {
            cout << "Invalid" << endl;
            return;
        }
        length--;
        cout << nums[length] << endl;
        return;
    }

    void peekMed() {
        if (isEmpty()) {
            cout << "Invalid" << endl;
        } else {
            cout << sort_nums[(length - 1) / 2] << endl;
        }
    }

    bool isEmpty() {
        return length == 0;
    }
} St(100002);


void solve(const string &s) {//根据字符串的第二个字符进行分类操作
    if (s[1] == 'u') {
        int v;
        cin >> v;
        St.push(v);
    } else if (s[1] == 'o') {
        St.pop();
    } else {
        St.peekMed();
    }
}

void Input() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N;
    int n = N;
    string s;
    while (n--) {
        cin >> s;
        solve(s);
    }
}

int main() {
    Input();
    return 0;
}
发表于 2021-10-01 21:04:33 回复(0)
牛客全过,官网超时了3个,只有一半的分
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
using namespace std;
string temp1;
string temp2;
vector<int> stk;
vector<string> op;
multiset<int> temp3;
int nop;
int main(int argc, const char * argv[]) {
    cin>>nop;
    getchar();
    for (int i=0; i<nop; i++) {
        getline(cin,temp1);
        op.push_back(temp1);
    }
    for (int i=0; i<nop; i++) {
        if (op[i][1]=='o') {
            if (stk.size()>0) {
                cout<<stk.back();
                cout<<endl;
                temp3.erase(temp3.find(stk.back()));
                stk.pop_back();
            }else{
                cout<<"Invalid";
                cout<<endl;
            }
        }else if(op[i][1]=='u'){
            op[i].replace(0, 5, "");
            stk.push_back(stoi(op[i]));
            temp3.insert(stoi(op[i]));
        }else if(op[i][1]=='e'){
            if (stk.size()>0) {
                auto it=temp3.begin();
                for (int i=0; i<(stk.size()+1)/2-1; i++) {
                    it++;
                }
                cout<<*it;
                cout<<endl;
            }else{
                cout<<"Invalid";
                cout<<endl;
            }
        }
    }
    return 0;
}




编辑于 2021-02-21 21:24:10 回复(0)
学习了树状数组,然后也学习了二分查找,以前以为二分查找很简单,这个二分查找返回了left,还是想了很久。记录一下。
#include<iostream>
(720)#include<vector>
#include<algorithm>
(831)#include<stack>
using namespace std;
stack<int>myStack;
int a[100001];
int lowbit(int x) {
	return x & (-x);
}
void add(int val, int index) {
	while (index < 100001) {
		a[index] += val;
		index += lowbit(index);
	}
}
int getSum(int index) {//求下标为1到index的元素的和
	int sum = 0;
	while (index > 0) {//不能为0
		sum += a[index];
		index -= lowbit(index);
	}
	return sum;
}
int PeekMedian(int num) {
	num = (num + 1) / 2;
	int left = 1, right = 100001;
	int mid;
	while (left <= right) {
		mid = (left + right) / 2;
		int tmp = getSum(mid);//这里tmp是前mid项的和,不能用tmp==num来作为出口,因为 若前面只有a[3]=1,其余均为0,则前100和1000的结果都为1。
		if (tmp < num) {      //其出口应该是left<right
			left = mid+1;
		}
		else {
			right = mid-1;
		}
	}
	return left;
}
int main() {
	//freopen("C:\\Users\\47\\Desktop\\1.txt","r",stdin);
	fill(a, a + 100001, 0);
	int n, x;
	string s;
	cin >> n;
	while (n--) {
		cin >> s;
		if (s == "Push") {
			cin >> x;
			myStack.push(x);
			add(1, x);
		}
		if (s == "Pop") {
			if (myStack.empty())printf("Invalid\n");
			else {
				printf("%d\n", myStack.top());
				add(-1, myStack.top());
				myStack.pop();
			}
		}
		if (s == "PeekMedian") {
			if (myStack.empty())printf("Invalid\n");
			else {
				printf("%d\n", PeekMedian(myStack.size()));
			}
		}
	}
}


发表于 2020-02-27 00:27:41 回复(0)
这题还有点意思,目前用了三种解法:
1.树状数组
2.二分法(有点意思)
3.直接用set排序然后取中值,加上桶思想优化,再尝试关闭同步流,A了
前两种解法网上比较多,这里就只贴出我想出来的第三种:
#include<bits/stdc++.h>
using namespace std;
int vis[100001];
int main()
{     ios::sync_with_stdio(false);//关闭同步流
    cin.tie(nullptr);     memset(vis,0,sizeof(vis));             stack<int> s;         set<int> od;     int n;cin>>n;         for(int i=0;i<n;i++)     {         string t;cin>>t;             if(t=="Pop")         {             if(s.empty())                 cout<<"Invalid"<<endl;             else              {                     int tmp=s.top();                             if(vis[tmp]>1)                     vis[tmp]--;                 else                  {                     od.erase(od.find(tmp));                                     vis[tmp]--;                 }                                     cout<<tmp<<endl,s.pop();             }         }         if(t=="PeekMedian")         {             if(s.empty())                 cout<<"Invalid"<<endl;             else              {                 vector<int> tmp;//将出现的元素统统放入数组中                 for(auto it=od.begin();it!=od.end();it++)                 {                     for(int i=0;i<vis[*it];i++)                         tmp.push_back(*it);                                     }                 if(tmp.size()%2==0)                 {                     cout<<min(tmp[tmp.size()/2],tmp[tmp.size()/2-1])<<endl;                 }                 else                 {                     cout<<tmp[(tmp.size()+1)/2-1]<<endl;                 }             }         }         if(t=="Push")         {             int x;cin>>x;                         s.push(x);                         if(vis[x]==0)                 od.insert(x);             vis[x]++;         }     }     return 0;
}

发表于 2019-07-30 17:19:50 回复(0)
/*
使用大小堆来找到中位数。使用一个大堆和一个小堆,维持大顶堆的数都小于等于小顶堆的数,且两者的个数相等或差1。平均数就在两个堆顶的数之中。
需要使用可以删除任意数的堆结构 struct Heap
*/
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
struct Heapl{//可以让堆删除任意变量
    priority_queue<int, vector<int>, less<int> > q1,q2;//大堆,堆顶应该是最大的数
    inline void push(int x){q1.push(x);}
    inline void erase(int x){q2.push(x);}
    inline void pop(){for(;q2.size()&&q1.top()==q2.top();q1.pop(),q2.pop());if(q1.size())q1.pop();}
    inline int top(){for(;q2.size()&&q1.top()==q2.top();q1.pop(),q2.pop());return q1.top();}
    inline int top2(){int val,ret; val=top(),pop(),ret=top(),push(val); return ret;}
    inline int size(){return q1.size()-q2.size();}
};  //这个地方应该可以优化,Heap可以传入参数?建立大堆/小堆的Heap
struct Heaps{
    priority_queue<int, vector<int>, greater<int> > q1,q2;//小堆,top为最小值
    inline void push(int x){q1.push(x);}
    inline void erase(int x){q2.push(x);}
    inline void pop(){for(;q2.size()&&q1.top()==q2.top();q1.pop(),q2.pop());if(q1.size())q1.pop();}
    inline int top(){for(;q2.size()&&q1.top()==q2.top();q1.pop(),q2.pop());return q1.top();}
    inline int top2(){int val,ret; val=top(),pop(),ret=top(),push(val); return ret;}
    inline int size(){return q1.size()-q2.size();}
};
//维持大顶堆的数都小于等于小顶堆的数,且两者的个数相等或差1。平均数/中位数就在两个堆顶的数之中。
Heapl p;//大堆
Heaps q;//top为最小值
stack<int> s1;
void pop(){
 if(s1.size()==0) cout<<"Invalid"<<endl;
 else {
    int y=s1.top();
    s1.pop();
    int a1=p.top();
    if(y<=a1)       p.erase(y);  //如果要删除的值小于大堆的堆顶,那么这个数在大堆里
    else  q.erase(y); 
   cout<<y<<endl;
        while(p.size()>(q.size()+1))
        {
            int yy=p.top(); p.pop();  q.push(yy);
        }
        while(q.size()>(p.size()+1))
        {
            int yy=q.top();  q.pop(); p.push(yy);
        }
 }
}
void PeekMedian(){
    if(s1.size()==0) cout<<"Invalid"<<endl;
 else {
    if(p.size() == q.size()) cout<<p.top()<<endl;
    else   cout<<( p.size() > q.size() ? p.top() : q.top())<<endl;
 }
}
void Push(){
int num;cin>>num;
s1.push(num);
//如果num小于大堆的堆顶,那么放在大堆里。如果num大于小堆的堆顶,那么放小堆里
        if(p.size()!=0 && num<=p.top())  // !!!注意不能是p.size()==0 || num<=p.top()。因为可能在pop的过程中出现了p变为空的情况。这个时候要看q是不是0。如果两个都是0,那么就放在p里。
            p.push(num);
         else if(q.size()!=0 && num>q.top())
            q.push(num);
        else{
            if(p.size()<=q.size())  p.push(num);
            else   q.push(num);
        }
        while(p.size()>(q.size()+1))
        {
            int y=p.top();  p.pop(); q.push(y);
        }
        while(q.size()>(p.size()+1))
        {
            int y=q.top();  q.pop();   p.push(y);
        }
}
int main()
{
   // freopen("a.txt","r",stdin);
    int num;cin>>num;
    for(int i=0;i<num;i++)
    {
        string a;cin>>a;
        if(a=="Pop") pop();
        else if(a=="PeekMedian") PeekMedian();
        else if(a=="Push") Push();
        }
    return 0;
}
 
发表于 2019-07-26 16:13:54 回复(0)
#include <iostream>
#include<stack>
#include<string>
using namespace std;
#define lowbit(i) (i&-i)
#define maxn 100010

int c[maxn] = { 0 }, n, tem;
stack<int>s;
string st;
int getsum(int x) {     int res = 0;     for (int i = x; i >= 1; i -= lowbit(i))          res += c[i];     return res;
}

void add(int x, int v) {     for (int i = x; i < maxn; i += lowbit(i))         c[i] += v;
}

void peekmedian() {     int lef = 1, r = maxn, aim = (s.size()+1) / 2,mid;     while (lef < r) {         mid = (lef + r) / 2;         if (getsum(mid)>= aim) {             r = mid;         }         else lef = mid+1;     }     cout << lef << endl;
}


int main() {     cin >> n;     for (int i = 0; i < n; i++) {         cin >> st;         switch (st[1])         {         case'u':cin >> tem;             s.push(tem);             add(tem, 1);             break;         case'o':if (s.empty())cout << "Invalid\n" ;                 else {                     add(s.top(), -1);                     cout << s.top() << endl;                     s.pop();                 }                 break;         case'e':if (s.empty())cout << "Invalid\n";                 else {                     peekmedian();                 }         }              }     return 0;
}

发表于 2018-12-26 21:38:03 回复(1)
def getMid(cnt,mid):
    suma = 0
    for i in range(0,len(cnt)):
        suma += count[i]
        if suma>= mid:
            return i
    return 0

n = input()
n = int(n)
stack = []
count = [0 for i in range(0,100000)]
for i in range(0,n):
    line = input()
    if line == 'Pop':
        if len(stack)>0:
            m = stack.pop()
            count[m]-=1
            print(m)
        else:
            print("Invalid")
    elif line == 'PeekMedian':
        if len(stack)>0:
            print(getMid(count,((len(stack)+1)//2)))
        else:
            print("Invalid")
    else:
        lst = list(map(str,line.split()))
        m = int(lst[1])
        stack.append(m)
        count[m]+=1

发表于 2018-11-24 14:43:21 回复(1)
思路: 折半查找法 & 折半插入排序;
#include <iostream>
#include <vector>
#include <stack>
#include <string>
#include <deque>
#include <fstream>
using namespace std;

#ifndef debug
ifstream ifile("case.txt");
#define cin ifile
#endif

int BinaryFind(deque<int> & a,int& x)
{
    int low = 0;
    int high = a.size() - 1;
    while (low <= high)
    {
        int mid = (low + high) / 2;
        if (a[mid] > x)
        {
            high = mid - 1;
        }
        else if (a[mid] < x)
        {
            low = mid + 1;
        }
        else
        {
            return mid;
        }
    }
    x = low;
    return -1;
}

void BinaryErase(deque<int> & a, int x)
{
    int tmp = BinaryFind(a, x);
    if (tmp != -1)
    {
        a.erase(a.begin() + tmp);
    }
}

void BinaryInsert(deque<int> & a, int x)
{
    int tmp = x;
    int rlt = BinaryFind(a, tmp);
    if (rlt == -1) {
        a.insert(a.begin() + tmp, x);
    }
    else
        a.insert(a.begin() + rlt, x);
}



int main()
{
    int N;
    string str;
    int tmp;
    int index = 0;
    while (cin >> N) {
        stack<int> s;
        deque<int> media;
        for (int i = 0; i < N; i++)
        {
            cin >> str;
            switch (str[1]) {
            case 'o':// pop
                if (!s.empty()) {
                    tmp = s.top();
                    cout << tmp << endl;
                    s.pop();
                    BinaryErase(media, tmp);
                }
                else {
                    cout << "Invalid" << endl;
                }
                break;
            case 'u':// push
                cin >> tmp;
                s.push(tmp);
                BinaryInsert(media, tmp);
                break;
            case 'e':
                if (!media.empty())
                    cout << media[(media.size() - 1) / 2] << endl;
                else
                    cout << "Invalid" << endl;
                break;
            }
        }
    }
    system("pause");
}

发表于 2018-09-03 14:37:59 回复(0)
//用vector模拟栈,求PeekMedian即中位数,用map(某个数和对应出现的次数的映射)维护即可。
//可以避免树状数组和线段树的写法。 #include <bits/stdc++.h>
using namespace std;
int n;char str[25];
vector<int> vec;
map<int,int> mp;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%s",str+1);
        if(str[2]=='o'){
            if(vec.empty()) printf("Invalid\n");
            else --mp[vec.back()],printf("%d\n",vec.back()),vec.pop_back();
            continue;
        }if(str[2]=='e'){
            if(vec.empty()) printf("Invalid\n");
            else{
                int sz=vec.size();
                if(sz%2==0){
                    int cnt=0;sz/=2;
                    for(auto &ch:mp){
                        if((cnt+=ch.second)>=sz){
                            printf("%d\n",ch.first);break;
                        }
                    }
                }else{
                    int cnt=0;sz=(sz+1)/2;
                    for(auto &ch:mp){
                        if((cnt+=ch.second)>=sz){
                            printf("%d\n",ch.first);break;
                        }
                    }
                }
            }
            continue;
        }if(str[2]=='u'){
            int x;scanf("%d",&x);
            vec.push_back(x);++mp[x];
            continue;
        }
    }    
    return 0;
} 

发表于 2018-03-04 23:15:48 回复(1)
分块hash或树状数组
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<string>
#include<cstring>
#include<stack>
using namespace std;

#define lowbit(i) ((i)&(-i))

const int maxn = 100010;
int c[maxn];
stack<int> st;

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>0; i -= lowbit(i))
        sum += c[i];
    return sum;
}

void Push(int num){
    st.push(num);
    update(num, 1);
}

int peekMidian(int k){
    int l = 1, r = maxn;
    int mid;
    while (l < r){
        mid = (l + r) / 2;
        if (getSum(mid) >= k)
            r = mid;
        else
            l = mid + 1;
    }
    return l;
}

int Pop(){
    int x = st.top();
    st.pop();
    update(x, -1);
    return x;
}

int main(){
    int n;
    scanf("%d", &n);
    memset(c, 0, sizeof(c));
    char op[20];
    for (int i = 0; i < n; i++){
        scanf("%s", op);
        switch (op[1]){
        case 'o':
            if (st.empty())
                printf("Invalid\n");
            else
            {
                printf("%d\n", Pop());
            }
            break;
        case 'e':
            if (st.empty()){
                printf("Invalid\n");
            }
            else{
                int k = st.size() % 2 ? (st.size() + 1) / 2 : st.size() / 2;
                printf("%d\n", peekMidian(k));
            }
            break;
        case 'u':
            int x;
            scanf("%d", &x);
            Push(x);
            break;

        }
    }

    return 0;
}





发表于 2018-02-21 12:57:45 回复(0)
#include <stack>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 10;
const int sqrN = 316;
stack<int> s;
int block[sqrN];
int table[maxn];

void Pop(){
	int elem;
	if(s.empty())
		cout << "Invalid" << endl;
	else{
		elem = s.top();
		table[elem] --;
		block[elem/sqrN] --;
		s.pop();
		cout << elem << endl;
	}
}

void Push(){
	int elem;
	cin >> elem;
	table[elem] ++;
	block[elem/sqrN] ++;
	s.push(elem);
}

void PeekMedian(){
	if(s.empty())
		cout << "Invalid" << endl;
	else{
		int sum_ = 0;
		int idx = 0;
		int len = s.size();
		int mid = (len % 2 == 0) ? (len / 2) : ((len + 1) / 2);
		while(sum_ + block[idx] < mid)
			sum_ += block[idx++];
		int num = idx * sqrN;
		while(sum_ + table[num] < mid)
			sum_ += table[num++];
		printf("%d\n", num);
	}
}

int main(){
	int n;
	cin >> n;
	char op[15];
	while(n--){
		scanf("%s", op);
		if(op[1] == 'o')
			Pop();
		else if(op[1] == 'u')
			Push();
		else if(op[1] == 'e')
			PeekMedian();
	}
	return 0;
}
分块思想!利用hash表
发表于 2017-08-27 23:33:19 回复(0)

问题信息

难度:
22条回答 15130浏览

热门推荐

通过挑战的用户