1. 程式人生 > >劍指offer--包含mini函式的棧

劍指offer--包含mini函式的棧

題目描述

定義棧的資料結構,請在該型別中實現一個能夠得到棧中所含最小元素的min函式(時間複雜度應為O(1))。

 這個題目的意思就是我們的函式裡邊有一個min函式,當我們呼叫這個函式的時候,不需要去遍歷我們的棧,直接呼叫這個函式就可以返回我們棧裡邊的最小元素。

  那我們就一步一步的來分析一下這個問題。

第一步:設定一個min變數

      這是很好理解的,我們現在定義一個變數,int型別的min,然後開始操作我們的棧,當棧是空的時候, 入棧操作的時候就把min也給賦值了。

   

class Solution {
public:
	void push(int value) {
		if (s.empty()||value<mini)
		{
			s.push(value);
			mini = value;
		}
		else{
			s.push(value);
		}
	}
	void pop() {
		s.pop();
		//這時候就很複雜了,可能最小的值被pop出去了
		//這時候就需要遍歷整個棧來找出目前的最小值	
	}
	int top() {
		return s.top();
	}
	int min() {
		return mini;
	}
private:
	int mini;
	stack<int> s;
};

 這裡通過一段簡單的程式碼來實現一下,這裡pop的時間複雜度就是O(n)了,因為每次pop都有可能把最小值給pop出去了。但是每次獲取mini的時間仍然是O(1)。當然這自然不是最好的辦法。

第二步:通過一個輔助棧來實現

        這個方法是大多數人都瞭解的方法,就是我們建立一個新的棧,這個棧專門用來儲存最小值,這樣的話即使pop也無所謂了。所有操作的時間複雜度都是O(1),不過這種方法佔用的空間就大了一些些是O(n)了。

 

class Solution {
public:
	void push(int value) {
		if (mini.empty()||value<=mini.top())
		{
			mini.push(value);
			s1.push(value);
		}
		else
		{
			s1.push(value);
			mini.push(mini.top());
		}
	}
	void pop() {
		if (s1.empty())
			return ;
		s1.pop();
		mini.pop();
	}
	int top() {
		if (s1.empty())
			return -1;
		return s1.top();
	}
	int min() {
		if (s1.empty())
			return -1;
		return mini.top();
	}
private:
	stack<int> s1;
	stack<int> mini;
};

  這也是我提交的程式碼,通過了牛客網的所有測試,但是這裡的程式碼並不是很好。首先就是我們沒有判斷當棧是空的時候仍然進行pop、top、min等一系列操作應該怎麼辦。這裡我之後進行了一點點的改進。加了

if (s1.empty())

return -1;

兩句話來判斷,如果我們的棧是空的話,就返回-1.

第三步語法優化:

     這樣自然還是會出現問題,如果我們的資料裡邊曾經push過-1,怎麼辦?那豈不是出現了烏龍,我明明是返回一個值,你缺判斷成了錯誤碼。

這裡我們既然學習了C++的異常,我們就要學會使用,異常是C++裡出現的很好的一個機制。

Throw exception(1,”棧為空”);這樣的話就不存在你的特殊返回值在push階段操作過了。

第四步演算法優化:

     說了我們程式碼上的問題,那這個輔助棧是不是最好的?有沒有優化的可能?自然是有!雖然不能在時間複雜度上進行優化,因為時間複雜度已經是O(1)了再優化就不需要執行直接獲得結果了!那這裡我們可以對我們的空間複雜度進行一個小小的優化。

     假如我們的元素插入的是2,1,9,9,9,8,7,6,7這樣的話我們的最小棧裡邊存的元素不就是2,1,1,1,1,1,1,1,1後邊全都是重複的1.大量的重複,那我們是不是可以在這個上邊做一點文章。

   那我們是不是可以這樣,當我們進行插入操作的時候我們進行判斷,如果我們現在插入的元素比我們的最小值要小我們就把他插入進去,如果說比我們的最小值大,那我們的最小棧就不操作

當我們插入元素的時候就和最小棧頂部元素進行比較如果說比他小的話再進最小棧,

這時候再來一個2,2比我們最小棧的棧頂元素小,那這個2就不進入我們的最小棧。這樣的話當我們執行pop操作的時候也需要對比一下。

當進行出棧操作的時候,我們判斷一下,現在出的這個元素和我們最小棧的棧頂元素相等不,如果不相等那最小棧不出,相等的話才會出。

當我們的正常棧pop現在棧頂元素1的時候,發現現在出的元素和我棧頂的元素相等,那最小棧也要pop。

  但是這時候就又來了一個問題,假如說你現在入的元素和最小棧棧頂元素相等的時候你入不如最小棧。

  這是肯定要入的不然就會出很大的問題

這時候我們pop一個元素,發現1和最小棧棧頂元素是一樣大小的,那最小棧的元素也會出去,這時候最小棧裡邊剩下的元素就是2了,但是正常棧裡邊還有那麼多1,所以相等肯定是要入棧的。

那我們入棧的元素特殊的話是不是又成了這樣,我們的空間還是有浪費,我們這裡還是可以再繼續優化的。

class Solution {
public:
	void push(int value) {
		if (mini.empty()||value<mini.top())
		{
			s.push(value);
			mini.push(s.size());
		}
		else
		{
			s.push(value);
		}
	}
	void pop() {
		if (mini.top() == s.size())
		{
			mini.pop();
		}
		s.pop();
	}
	int top() {
		return s.top();
	}
	int min() {
		//這裡的索引需要stack通過陣列來實現
		//或者在java中存在get函式介面但是在c++的stl中
		//並沒有提供獲取棧指定位置的介面這裡就沒實現
	}
private:
	stack<int> s;
	stack<int> mini;
}

這裡我們儲存的是索引可以理解成下標,然後通過這個下標來訪問棧的某一特定位置的元素。因為C++ STL中使用deque也就是雙端佇列來實現的,所以這裡並沒有提供函式介面,不過如果我們自己通過陣列來實現一個棧的話還是可以實現我們上邊的思想的,這裡我就不實現了。這種編寫程式的思想還是要有的。