1. 簡述
假設有這樣乙個擁有3個操作的佇列:
1. enqueue(v):將v加入佇列中
2. dequeue:使佇列中的對首元素刪除並返回此元素
3. maxelement:返回佇列中的最大元素
請設計乙個資料結構和演算法,讓maxelement操作的時間複雜度盡可能的低。
2. 思路
起初沒仔細看,還以為與此前的自定義棧-pop-push-min-時間複雜度都為o(1)
是一樣的,後來才發現不是一回事,有差別的。對於棧來說,我們可以的入棧和出棧不會影響輔助陣列內的情況,假設當前n個元素,(為了說明簡單,下標從1開始),輔助空間的f[1]記錄的是a[1,1]內的最值位置,f[2]記錄的是a[1,2]內的最值位置,···,f[n]記錄的是a[1,n]內的最值位置。在插入f[k+1]=a[f[k]]與a[k+1]兩者最值的位置,插入複雜度為o(1),在刪除第k+1個節點時,刪除a[k+1]和f[k+1],這並不影響f[1]-f[k],因此刪除的複雜度為o(1),取最值,假設當前n個元素,即返回a[f[n]]。
對於佇列來說,如果套用棧的輔助陣列方法,假設當前有n個元素,下標從1開始,那麼f[1]記錄的是a[1,n]中的最值,f[2]記錄的是a[2,n]中的最值,···,f[n]記錄的是a[n,n]中的最值。當a[k+1]入隊時,需要更新f[1]到f[k],f[k+1]=k+1,因此插入的複雜度為o(n),當f[k]出隊時,刪除f[k]即可(每次出隊的都是第乙個元素,實際上此時f[1]-f[k-1]已經出隊完畢了),因此刪除的複雜度為o(1)。取最值的複雜度是o(1)。
佇列與棧的區別很清楚了。程式設計之美上給了兩個答案,乙個是構建最大堆,另乙個是用兩個棧來實現。
最大堆的方法:
佇列本身要麼順序結構要麼鏈結結構,還那麼存。另外對於佇列每個元素構建乙個節點(包含在佇列中的位置),這些節點構成乙個最大堆,因此插入和刪除操作都要維護這個最大堆,時間複雜度都是o(logn),取最大值的複雜度為 o(1)。
兩個棧的方法:
a棧,b棧,這兩個棧都是前面提到的pop-push-min複雜度都為o(1)的空間換時間的實現。
取最值:返回a棧的最值和b棧的最值相比後的最值。複雜度o(1)。
入隊操作:直接入到b棧中。複雜度o(1)。
出隊操作:如果a棧不為空,直接a棧出棧,複雜度為o(1),如果a棧為空,那麼將b棧內容逐個出棧並且逐個入棧到a中,然後a棧出棧,複雜度o(n),實際上是b棧的長度。
對於這種方法,如果對列的操作時,一連串的入棧,然後是一連串的出棧,那麼就是首先不停向b入棧,然後第乙個出棧,b棧元素全壓入a棧,a出棧乙個,這一步是n的複雜度,但是此後是不停的從a出棧,這都是o (1)的複雜度。還不錯呢。而且借助了棧的**,方便實現。對於這樣的情景,就是只有第乙個出棧的時候,要o(n),複雜度不是很均勻。對於每個元素來說,要麼入b棧,入a棧,從a棧彈出,即總體是3n,平均下來基本上是o(3),要不最大堆的o(logn)是快了不少呢。
3. 參考
程式設計之美,3.7,佇列中取最大值操作問題
程式設計之美 佇列中取最大值操作問題
思想就是入隊操作時,對最大值進行記錄。一種方法直接建立佇列,二種方法是利用2個棧實現佇列功能 詳見前面的文章 此處給出第一種方法 maxqueue.cpp 定義控制台應用程式的入口點。include stdafx.h includetypedef int type using namespace s...
程式設計之美(三)佇列中取最大運算元的問題
include include include includeusing namespace std define maxn 100 class stack 判斷當前堆疊是否為空 bool isempty 判斷當前堆疊是否滿 bool isfull 向堆疊中push元素,void push int ...
59 2 佇列的最大值
實現乙個函式max能夠得到佇列裡的最大值,並且時間複雜度為o 1 很熟悉,以前做過兩個棧實現乙個棧的min,max函式的,這道題變成了佇列 所以,這裡也能夠有相似的思想,我們能不能兩個雙端佇列實現這個的max函式呢?答案是可以的。不過,deque的一些方法我實現是有點記混了,所以在做這道題時想不起來...