今天學習的時間雖然挺多的,但是總覺效率不高。其實今天沒有按照計划進行EPI題目的瀏覽,白天去看了其他的書籍。准備找工作可能需要的狀態是一定量經典的書,偶爾溫習才可。書是看不完的,知識點也是固定的。所以從把手頭的幾本書在瀏覽完畢之后就要着手復習之前的知識啦。C++的知識,leetcode的題目,操作系統,數據庫,網絡的學習筆記~。
1. 實現一個棧,支持返回當前棧中最大值的操作。要求,返回最大值的操作時間復雜度O(1)。可以使用額外的O(n)的空間復雜度。
題目之前見到過,所以思路一下子就有了,利用額外的棧存儲當前棧中最大值即可。
簡單的思路,在元素入棧的時候,輔助棧同時壓入當前棧的最大值,元素出棧的時候,彈出輔助棧的元素。取得最大值直接從輔助棧棧頂獲取即可。
另外,可以減少一下壓入輔助棧中的元素的數量,只有壓入當前棧的元素大於等於當前輔助棧棧頂元素,才將該至壓入輔助棧棧頂,出棧的時候,只有出棧元素等於輔助棧棧頂元素時,才將輔助棧棧頂彈出。
另外,能在常數項更優化的一個方法是,輔助棧中壓入一個數對,數對的意義是當前的最大值,以及當前最大值的數量,push的時候如果相等,則增加數對的第二個值,pop的時候如果相等,減去數對中的第二個值,如果減至0,則pop輔助棧即可。
擴展問題,實現一個隊列,支持返回當前最大值的操作,能否優化至O(1)的時間復雜度。
這個題目還是比較復雜的,需要結合兩個知識來把時間復雜度均攤為O(1),及利用兩個已經實現最大值操作的堆棧來模擬隊列,可以達到均攤O(1)復雜度的返回最大值的隊列。
其實利用deque也能夠達到O(1)的均攤成本,但是時間復雜度分析比較復雜。
均攤的理解是每個元素最多進入D和離開D一次,不會第二次進入和離開D。所以入隊的均攤O(1)。
2.實現逆波蘭表達式的計算並返回計算結果。
利用棧不斷的壓入數字,遇到運算符號,從數字棧中取出兩個數字進行計算,結果繼續壓入數字棧,直到計算完畢,數字棧中存儲的運算結果。
3.迭代方法實現BST的中序遍歷。
利用棧的迭代實現不是很復雜,而且可以完成O(1)空間復雜度的morris中序遍歷。
而且迭代實現二叉樹的遍歷方法中,后序遍歷的實現是難度最大的。morris的后序遍歷實現起來難度也很大。
4.針對隨機鏈表,即除了next域之外,增加一個random指針域的鏈表,迭代的方法進行random-first序的遍歷。
遞歸的方法比較容易實現,這里讓進行迭代即利用stack來模擬遞歸的方法即可。
template <typename T> void search_postings_list(const shared_ptr<node_t<T> > &L) { stack<shared_ptr<node_t<T> > > s; int order = 0; s.emplace(L); while(!s.empty()) { shared_ptr<node_t<T> > curr = s.top(); s.pop(); if( curr && curr->order == -1 ) { curr->order = order++; s.emplace(curr->next); s.emplace(curr->jump); } } }
5.利用棧記錄漢諾塔的移動過程,模擬。
void transfer(const int &n, array<stack<int>,3> &pegs, const int &from, const int & to, const int &use) { if(n > 0) { transfer(n-1,pegs,from,use,to); pegs[to].push(pegs[from].top()); pegs[from].pop(); cout<<"Move from peg"<<from<<"to peg"<<to<<endl; transfer(n-1,pegs,use,to,from); } } void move_tower_hanoi(const int &n) { array<stack<int>,3> pegs; for(int i = n;i >= 1;--i) { pegs[0].push(i); } transfer(n,pegs,0,1,2); }
6.一排樓房所有的窗戶都面朝西,太陽落下的時候如果一個樓房的西面不存在比該樓房高的樓房,則該樓房可以看到陽光,從東至西計算出所有可以看到陽光的樓房。
擴展問題:從西至東計算出所有可以看到陽光的樓房。
問題的根本思路是利用棧,從東至西維護一個遞減的序列。從西至東則需要維護一個遞增的序列。其中序列的內容及為可看到陽光的樓房。這種利用棧維護單調序列的方法在求最大直方圖面積中應用可以減少時間復雜度至O(n)。
從東至西的遞減序列code
template <typename T> vector<pair<int,T>> examine_buildings_with_sunset(istringstream &sin) { int idx = 0;//building's index T height; //Stores(buiding_idx,building_height) pair with sunset views vector<pair<int,T> > buildings_with_sunset; while(sin >> height) { while(buildings_with_sunset.empty() == false && height >= buildings_with_sunset.back().second) { buildings_with_sunset.pop_back(); } buildings_with_sunset.emplace_back(idx++,height); } return buildings_with_sunset; }
7.設計一個排序堆棧的算法僅僅利用push,pop,empty,top操作,並且不顯示的開辟額外空間。
如果不顯示的開辟額外空間,就想想利用函數遞歸調用的堆棧存儲信息,這樣就能夠進行排序。比如插入排序的遞歸實現方法。
template <typename T> void sort(stack<T> &S) { if(!S.emtpy()) { T e = S.top(); S.pop(); sort(S); insert(S,e); } } template <typename T> void insert(stack<T> &S,const T &e) { if(S.empty() || S.top() <= e) { S.push(e); }else { T f = S.top(); S.pop(); insert(S,e); S.push(f); } }
8.將包含..和.的文件路徑名簡化至最短路徑。
思路就是保持一個棧,當遇到..的時候講棧頂的目錄名彈出,遇到.則忽略。這樣就可以得到最短路徑了。該題目邊界情況較多,屬於細節題。
9.層序遍歷BST的方法。
層序遍歷,即廣度優先搜索,需要借助隊列來完成功能。
10.利用兩個整數實現一個隊列的功能。隊列中能夠加入的元素為[0,9]。
首先看到這個題目想到的是將整數看成位數組,4位可以表示[0,15]之間的數字,所以可以當成數組。
后來參考答案看到按照十進制的位來表示[0,9]計算起來更加方便。剛好十進制的每位都是[0,9],一個數字作為數組,另外一個數字作為隊列的長度。注意全為0的邊界情況。
擴展問題:如果只有一個整數,可以使用其中一位保存長度。
11.利用兩個棧實現一個隊列,使得pop和push的均攤成本O(1)。
一個棧負責進入隊列,另外一個棧負責彈出隊列。
12.O(1)返回隊列max值的隊列的應用。
應用場景是現在有一個數組,數組是一個數對,第一個元素表示一個時間戳,第二個元素表示該事件的流量,數組已經根據時間戳有序的,現在需要計算每個時間戳至時間戳+w這段時間內的最大流量是多少。
如果直接解法則需要遍歷當前時間戳的流量至當前時間戳+w這段時間內所有流量,時間復雜度O(nw)。
如果利用優先隊列的話因為是最小堆實現的,所以可以優化至O(nlogw)的時間復雜度。
如果利用O(1)返回最大值的隊列,則優化至了O(n)的時間復雜度。