用2個棧實現隊列

下面這段代碼是我定義的Stack類模板,接下來介紹幾種用2個該Stack類實現隊列Queue的幾種方法。算法

template<class T, int DEFAULT_CAPACITY = 0>
class Stack
{
public:
	Stack();
	Stack(const Stack<T> &st);
	Stack &operator=(const Stack<T> &st);
	~Stack();
public:
	void Push(const T &data);
	void Pop();
	T &Top();
	T &End();
	bool Empty();
	size_t Size();
	void Print();
	
protected:
	void CheckCapacity();
protected:
	T *_arr;
	size_t _top;
	size_t _capacity;
};


聲明:爲了實現除「入隊」「出隊」以外更多的功能,好比「打印」等,我將上面那個已造好的「輪子」Stack作了擴展,增長了一些成員方法。而若是你關注的重點是push和pop的算法,那麼其實並不須要在乎我造的下面這個「輪子」。能夠直接跳過下面的代碼,並把全部我使用的Stack類型看成庫裏的stack便可.ide


擴展後的Stack:優化

template<class T, int DEFAULT_CAPACITY = 0>
class Stack
{
public:
	Stack()
		:_arr(NULL)
		, _top(0)
		, _capacity(0)
	{}
	Stack(const Stack<T> &st)
		:_arr(new T[st._capacity])
		, _top(st._top)
		, _capacity(st._capacity)
	{
		for (size_t i = 0; i < _capacity; i++)
		{
			_arr[i] = st._arr[i];
		}
	}
	Stack &operator=(const Stack<T> &st)
	{
		if (st._arr != _arr)
		{
			delete[] _arr;
			_arr = new T[st._capacity];
			for (size_t i = 0; i < st._capacity; i++)
			{
				_arr[i] = st._arr[i];
			}
			_top = st._top;
			_capacity = st._capacity;
		}
		return *this;
	}
	~Stack()
	{
		if (_arr != NULL)
		{
			delete[] _arr;
		}
	}
public:
	void Push(const T &data)
	{
		CheckCapacity();
		_arr[_top] = data;
		++_top;
	}
	void Pop()
	{
		--_top;
	}
	T &Top()
	{
		return _arr[_top - 1];
	}
	T &End()
	{
		return _arr[0];
	}
	bool Empty()
	{
		if (0 == _top)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	size_t Size()
	{
		return _top;
	}

	void Print()
	{
		for (size_t i = 0; i < _top; i++)
		{
			cout << _arr[i] << " ";
		}
		cout << endl;
	}
	void RePrint()
	{
		if (0 == _top)
		{
			return;
		}
		for (int i = _top - 1; i >= 0; i--)
		{
			cout << _arr[i] << " ";
		}
		cout << endl;
	}
protected:
	void CheckCapacity()
	{
		if (_top == _capacity)
		{
			_capacity = _capacity + 3;
			T *tmp = new T[_capacity];
			for (size_t i = 0; i < _top; i++)
			{
				tmp[i] = _arr[i];
			}
			delete[] _arr;
			_arr = tmp;
		}
	}
protected:
	T *_arr;
	size_t _top;
	size_t _capacity;
};



--------------------------------------------------------------------------------this

1、普通版本spa


棧的特色是「先入後出」,而隊列的特色是「先入先出」。
3d

因此能夠定義一個類Queue,包含2個成員對象:對象

一個棧_stack1存放數據,另外一個棧_stack2用來臨時存放數據,經過一些壓棧出棧的成員方法就能夠實現對隊列的入隊、出隊操做。blog

實現的2個棧組成的隊列以下圖所示,如今要將一組數據【1 2 3 4 5】放入隊列中:隊列

wKioL1cIqF_Abf47AAAPcW2JOQk670.png


先將這組數依次壓入_stack1中,而後再將_stack1中的元素依次出棧壓入_stack2中:ci

wKioL1cIqrPhTBySAAAgrnAvqas709.png

這時候,_stack2中的元素依次出棧,就至關於隊列的出隊操做了。

用代碼實現:

定義一個類模板Queue:

template<class T>
class Queue
{
        Queue()
        :_size(0)
        {}
	void Push(const T &data)            //入隊
	{
		_stack1.Push(data);
		++_size;
	}
	void Pop()                          //出隊
	{
		Converse(_stack2, _stack1);
		_stack2.Pop();
		Converse(_stack1, _stack2);    
		--_size;
	}
protected:	
	void Converse(Stack<T> &dst, Stack<T> &src)    //src->dst
	{
		while (size--)
		{
			dst.Push(src.Top());
			src.Pop();
		}
	}
protected:
	Stack<T> _stack1;
	Stack<T> _stack2;
	size_t _size;
};

其中,

成員方法Converse():做用是將棧src中的內容依次出棧,壓入棧dst中。

成員方法Push()  :入隊操做,每次將元素data存入成員對象_stack1中。

成員方法Pop()     :出隊操做,彈出第一個送入的元素。其中,第二個Converse的做用是還原。

能夠看出,這種入隊、出隊的算法,須要保證元素始終在_stack1中維護,而只有在出棧的時候用到_stack2臨時存放數據。



採用這種方式實現的隊列,能夠實現正常的入隊、出隊操做,但應該注意到,其中出隊操做須要進行兩次壓棧,咱們能夠對一個細節稍做優化,進一步提升出隊操做的執行效率。

下圖爲優化後的出隊操做:

wKioL1cIrk2hxlcBAAAfA_FxTW8587.png

區別在於,在出隊操做時,將_stack1中的(_size - 1)個元素彈出並壓入_stack2中。

彈出後,也不須要將_stack2的元素「倒回」_stack1中。



2、代碼優化

具體的實現步驟爲:

出隊操做時:

而是在每次執行出隊的時候進行一次判斷:

若_stack2爲空,則將_stack1中的(_size - 1)個元素彈出並壓入_stack2中,並彈出_stack中剩下的那個元素(就是我上面說的那個步驟);

若_stack2不爲空,則彈出_stack2中最頂層的元素。



在入隊操做時,判斷_stack1是否爲空:

若爲空,則先將_stack2中的元素依次彈出並壓入_stack1中,而後再將入棧元素壓入_stack1中(左圖)

不然,直接將入棧元素壓入_stack1中


優化後的方案用代碼實現以下:

template<class t>
class queue
{
public:
	queue()
		:_size(0)
	{}
	queue(const queue &que)
	{
		_stack1 = que._stack1;
		_size = que._size;
	}
public:
	void Push(const t &data)
	{
		if (_stack1.Empty() && !_stack2.Empty())
		{
			Converse(_stack1, _stack2);
		}
		_stack1.Push(data);
		++_size;
	}
	void Pop()
	{
		if (_stack2.Empty())
		{
			if (_stack1.Empty())
			{
				return;
			}
			RemainConverse(_stack2, _stack1);
			_stack1.Pop();
		}
		else
		{
			_stack2.Pop();
		}
		--_size;
	}
	void Print()			
	{
		_stack1.Print();
		_stack2.RePrint();
	}
	bool Empty()
	{
		return (0 == _size);
	}
	t& Front()			
	{
		if (_stack1.empty())
		{
			return _stack2.top();
		}
		else
		{
			return _stack1.end();
		}
	}
	t& Back()			
	{
		if (_stack1.Empty())
		{
			return _stack2.End();
		}
		else
		{
			return _stack1.Top();
		}
	}
	size_t Size()
	{
		return _size;
	}
protected:
	void RemainConverse(Stack<t> &dst, Stack<t> &src)
	{
		size_t count = src.Size() - 1;
		while (count--)
		{
			dst.Push(src.Top());
			src.Pop();
		}
	}
	void Converse(Stack<t> &dst, Stack<t> &src)    //src->dst
	{
		while (!src.Empty())
		{
			dst.Push(src.Top());
			src.Pop();
		}
	}
protected:
	Stack<t> _stack1;
	Stack<t> _stack2;
	size_t _size;
};

int main()
{
	queue<int> que1;
	que1.Push(1);
	que1.Push(2);
	que1.Push(3);
	que1.Push(4);
	que1.Print();
	que1.Pop();
	que1.Print();
	que1.Push(5);
	que1.Print();
	return 0;
}


到目前咱們已經實現了2種不一樣的方式實現這個隊列。

這兩種方法相比,第一種方法每次進行出隊操做都要移動2次棧中的所有數據

而對於第二種方法實現的隊列,若是連續進行入隊或者出隊操做,則不須要移動2個棧中的數據,能必定程度上提升效率。



3、進一步優化

能夠看出,_stack1和_stack2中所有元素(或者說,所有元素-1)轉移的次數越少,程序的執行效率就越高。

還有一種方法能夠進一步減小_stack1和_stack2中所有元素交換的次數:

wKioL1cIyevjgVFdAAAgrnAvqas158.png出隊:

檢測_stack2是否爲空:

若爲空,則將_stack1中的元素依次彈出並壓入_stack2中,

若不爲空,則彈出_stack2中棧頂的元素


入隊:

將元素壓入_stack。


能夠看出,這種實現方式入隊永遠是從_stack2中彈出元素,出隊永遠是向_stack1中壓入元素

而只有當入棧時檢測到_stack2爲空時,才執行2個棧之間所有元素的轉移。

用以下的圖能更形象地表示:

wKiom1cIzHXCjzOOAAAQFi6FFvQ951.png


實現代碼以下:

template<class T>
class Queue
{
public:
	Queue()
		:_size(0)
	{}
	Queue(const Queue &que)
	{
		_stack1 = que._stack1;
		_size = que._size;
	}
public:
	void Push(const T &data)
	{
		_stack1.Push(data);
		++_size;
	}
	void Pop()
	{
		if (_size == 0)   //異常
		{
			return;
		}
		if (_stack2.Empty())
		{
			Converse(_stack2, _stack1);
		}
		_stack2.Pop();
		--_size;
	}
	void Print()			
	{
		_stack2.RePrint();
		_stack1.Print();
	}
	bool Empty()
	{
		return (0 == _size);
	}
	T& Front()		
	{
		if (_stack2.Empty())
		{
			return _stack2.End();
		}
		else
		{
			return _stack2.Top();
		}
	}
	T& Back()			
	{
		return _stack1.Top();
	}
	size_t Size()
	{
		return _size;
	}
protected:
	void Converse(Stack<T> &dst, Stack<T> &src) 
	{
		while (!src.Empty())
		{
			dst.Push(src.Top());
			src.Pop();
		}
	}
protected:
	Stack<T> _stack1;
	Stack<T> _stack2;
	size_t _size;
};


4、總結

這裏一共提供了3種方法:

wKiom1cJF9aAf8wEAADstDPNQOo096.png

相關文章
相關標籤/搜索