棧的定義--Stackide
棧只容許在末端進行插入和刪除的線性表。棧具備後進先出的特性(LIFO,Last In First Out)。測試
問題:url
實現一個棧,要求實現Push(出棧)、Pop(入棧)、Min(返回最小值的操做)的時間複雜度爲O(1)spa
問題分析:blog
根據棧的特性要實現入棧和出棧是比較容易的,只須要藉助系統給的stack便可。可是最難的是要知足Min(返回最小值的操做)ip
的時間複雜度爲O(1)get
咱們先來討論最通常的思路:it
思路1io
使用一個變量來保存最小的值_min,每次入棧時讓入棧的元素d和_min進行比較,若是d<_min則更新_min,不然不更新。ast
可是這存在一個很大的問題,若是最小值出棧了怎麼辦?
好比說有一個棧爲:8,10,6,2,9.
則_min的值爲2,9出棧的話那麼_min的值不受影響。可是若是2出棧的話,則_min的值就沒法獲得。
思路2
既然只用一個變量無法解決這個問題,那咱們就增長變量。_min 保存當前的最小值,_second 保存次小值。
可是在實現Pop的時候又出現了新的問題,若是_min和_second都出棧以後,_min和_second該如何更新?
假設有棧爲8,10,6,2,9
_min爲2,_second爲6.當2出棧以後_min更新爲_second,可是_second該如何更新?
思路3
既然須要不斷的更新最小值,那就把全部的最小值給保存起來。
解決方案一:
每次push()和pop()時將數據和最小值同時壓入。
假設有一個棧 8 7 10 6 2 2 9
則存儲的結構爲:
入棧時的操做:
1)若是棧爲空或者入棧的元素d比當前棧的最小元素_s.top()._min小,則_min=d;
2)若是入棧元素d比當前棧的元素大於或者等於則直接將當前棧的最小值_s.top()._min賦給_min。
出棧操做:直接出棧
返回Min則 :當棧不爲空時,返回當前棧頂元素的_min即_s.top()._min。
實現代碼:
//節點結構 template<class T> struct Node { public: T _data;//數據域 T _min;//當前最小值 }; //棧的實現 template<class T> class Stack { public: void Push(const T& d) { Node<T> Data; Data._data = d;//不能將d直接入棧,由於棧爲一個結構體棧 if (_s.empty() || d < _s.top()._min)//若是棧爲空或者入棧元素小於當前棧的最小值 { Data._min = d; } else//入棧元素大於等於當前棧的最小值 { Data._min = _s.top()._min; } _s.push(Data);//入棧 } void Pop() { _s.pop(); } T& Min() { if (_s.empty()) { throw exception("棧爲空"); } return _s.top()._min; } private: stack<Node<T>> _s; };
總結:代碼實現起來比較容易簡潔,好理解可讀性比較強,可是對於重複比較多的棧的元素的存取則比較浪費空間,
由於每次入棧和出棧時,都至關於入棧兩次。
例如:八、六、六、六、九、五、五、五、五、十、二、二、十一、二、2
解決方案二:
使用兩個棧來實現。s1實現棧的push和pop,s2棧用於存儲在push和pop過程當中的最小值
好比有一組數要入棧: 8,10,6,2,9
S1: 8,10,6,2,9
S2: 8 6 2
每次入棧時,都會和S2.top 比較,若是小於S2.top 則會同時入S1和S2棧。
即到棧頂9時,最小值是2, 若是S1棧9出,和S2.top比較,若是大於S2.top,則S2不作任何操做,若是等於S2.top, 則S2.pop();
好比S1的2出棧時,2 == S2.top(), 而後S2.pop();這樣就節省了不少空間。
可是上述方法還有問題:
好比數據
S1:8 7 10 6 2 2 2 9 這時
S2:8 7 6 2
當S1中2出棧時,S2中的2也會出棧,這樣從S2中讀取到的最小值就是6,可是明顯錯了。
解決方案:
既然棧中可能存在多個重複的最小值_min,解決方法有兩種:
方法一:S2中每一個元素添加一個計數器。這樣當連續3次2出棧,S2中的2纔出棧。
方法二:S2中入棧的時候將<=S2.top的元素通通入棧,即S2爲8,7,6,2,2,2.
實現代碼(計數器):
template<typename T> struct DataCount { public: DataCount(const T & d) :_data(d) ,_count(1) {} public: T _data; int _count; }; template<typename T> class Stack { //拷貝構造、賦值、均使用默認的便可,由於拷貝構造和賦值的時候調用的是stack的,不存在淺拷貝 public: void Push(const T &d) { _s.push(d);//d入數據棧 DataCount<T> tmp(d);//因爲_min爲DataCount的棧,因此d不能直接入棧 if (_min.empty()|| d < _min.top()._data)//若是存放最小的棧爲空或者小於 { _min.push(tmp); } else if (d == _min.top()._data)//入棧的元素等於_min棧的棧頂元素,計數器_count+1; { _min.top()._count++; } } void Pop() { if (_s.empty()) { return; //throw exception("棧爲空"); } //若是數據棧出棧的元素等於_min棧的棧頂元素 if (_s.top() == _min.top()._data) { if (_min.top()._count==1)//數據棧中最小的元素只有一個 { _min.pop(); } else//數據棧中存在多個相等的最小元素 { _min.top()._count--; } } //若是數據棧出棧的元素大於_min棧的棧頂元素 //因爲_min棧中存放的都是較小的元素,因此不可能比數據棧要出棧的元素更大 _s.pop(); } T& Min() { if (_min.empty()) { //return -1; throw exception("棧爲空"); } return _min.top()._data; } private: stack<T> _s; //存儲基本數據的棧 stack<DataCount<T>> _min; //存儲最小值的棧 };
實現代碼(重複元素進棧)
template<typename T> class Stack { public: void Push(const T &d) { _s.push(d);//d入數據棧 if (_min.empty()|| d <= _min.top())//若是存放最小的棧爲空或者入棧的元素小於等於_min棧的棧頂元素 { _min.push(d); } } void Pop() { if (_s.empty()) { return; //throw exception("棧爲空"); } //若是數據棧出棧的元素等於_min棧的棧頂元素 if (_s.top() == _min.top()) { _min.pop(); } //若是數據棧出棧的元素大於_min棧的棧頂元素 //因爲_min棧中存放的都是較小的元素,因此不可能比數據棧要出棧的元素更大 _s.pop(); } T& Min() { if (_min.empty()) { //return -1; throw exception("棧爲空"); } return _min.top(); } private: stack<T> _s; //存儲基本數據的棧 stack<T> _min; //存儲最小值的棧 };
比較:
方法一:
優勢:適用於大量的重複的較小值的存儲
缺點:在通常場景下空間浪費比較大,由於S2中每一個元素添加一個計數器。
例如:八、六、六、六、九、五、五、五、五、十、二、二、十一、二、2
使用計數器比較節省空間。
方法二:
優勢:在通常場下比較節省空間。
缺點:對於大量的重複的較小值得存儲比較浪費
例如:八、六、五、十二、九、二、十一、7
測試代碼:
void test() { Stack<int> s1; s1.Push(10); s1.Push(11); s1.Push(6); s1.Push(2); s1.Push(2); s1.Push(2); s1.Push(12); Stack<int> s3(s1); Stack<int> s2; s2 = s1; try { cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; s1.Pop(); cout << s1.Min() << endl; } catch (...) { cout << "棧爲空" << endl; } try { cout << s2.Min() << endl; s2.Pop(); cout << s2.Min() << endl; s2.Pop(); cout << s2.Min() << endl; s2.Pop(); cout << s2.Min() << endl; s2.Pop(); cout << s2.Min() << endl; s2.Pop(); cout << s2.Min() << endl; s2.Pop(); cout << s2.Min() << endl; s2.Pop(); cout << s2.Min() << endl; } catch (...) { cout << "棧爲空" << endl; } } int main() { test(); getchar(); return 0; }