题目大意:要求实现一个栈,除了普通的pop()函数和push()函数外,还有一个max()函数,功能是返回栈中最大值,要求时间复杂度和空间复杂度尽量低。
首先,很容易想到时间复杂度为O(1)的算法,我们设一个数组max[],max[i]表示从栈底到位置i的区间上的最大值,则我们插入的是后,设当前插入的位置是po,则我们比较max[po-1]和插入元素x的大小,较大值更新max[po],然后插入x,pop()的时候,照常弹出即可,计算getmax()函数时,我们只要返回max[top]即可(top为栈顶位置)。这个算法的时间复杂度为O(1),空间复杂度为O(n)。
一个显然易见的优化就是,我们没有必要维护所有位置到栈底的最大值,容易发现,只有插入的值比当前栈中所有元素都大时,才需要更新max,所以我们将max数组替换成一个链表,链表指向上一次更新max值的位置。在插入时比较插入元素与当前最大值比较,若小于什么也不做,若大于则则加一个节点为当前节点,pop()时,看弹出的元素的位置是否是当前节点所指向的位置,若是的话,则将当前节点更新为下一个指向的节点(若没有返回错误码),getmax()函数的话,返回当前节点所代表的位置上的值即可。这样的话在平均情况下,时间复杂度和上述算法一样,但是空间复杂度有比较大的改进,但是在极端情况下,比如元素是按升序插入的话,空间复杂度还是O(n)。
下面介绍时间复杂度和空间复杂度都是O(1)的算法,我们除了原有的栈stack[]数组外,只需两个额外变量top,表示栈顶元素位置,max表示当前栈中的最大值。我们在插入元素x时,插入的不是x,而是max-x,这时候,如果max-x小于0,说明插入的元素大于当前栈中的最大值,这是需要更新max,在弹出时,若弹出的元素x大于0,表示这个元素小于栈中最大值,则我们返回max-x,(因为前面我们存的是max-x,这时max-(max-x)==x,能很容易求出栈中到底村的是什么值),否则说明当前要弹出的数就是栈中的最大值,则我们将max弹出后,还要更新max=max+x(x<0),这时max依然为当前栈(去掉x后)中的最大值(想一想为什么),getmax()函数就直接返回max即可。这样一来,我们就可以在O(1)的时间复杂度和O(1)的空间复杂度完成题目所要求的功能了,详细实现请看以下代码。
#include <stdio.h> #define maxn 110 using namespace std; int stack[maxn],top,max=0;//max初始化为0,不妨设入栈的元素均大于0 void push(int x) { stack[top++]=max-x; if(x>max) max=x; } int pop() { if(top==0)//栈空 return -1;//错误码 int x; if(stack[top-1]<0)//表示要出栈的数是当前栈中的最大值,这时候要更新max { x=max; max+=stack[--top]; } else { x=max-stack[--top]; } return x; } int getmax() { if(top==0)//栈空 return -1;//错误码 return max; } int main() { //freopen("dd.txt","r",stdin); while(1) { int a,b; scanf("%d",&a); if(a==1)//执行push(x)语句 { scanf("%d",&b); push(b); } else if(a==2)//执行pop语句 printf("%d\n",pop()); else if(a==3)//执行getmax()语句 printf("%d\n",getmax()); else//退出 break; } return 0; }