題目大意:要求實現一個棧,除了普通的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 110using namespace std;int stack[maxn],top,max=0;//max初始化為0,不妨設入棧的元素均大於0void 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;}