1. 程式人生 > >堆疊相關面試題(詳解)

堆疊相關面試題(詳解)

1. 實現一個棧,要求實現Push(出棧)、Pop(入棧)、Min(返回最小值的操作)時間複雜度為O(1)。

分析:設計兩個棧,一個棧用來push 、pop操作,另一個棧用來儲存當前最小值Min。當push元素小於等於Min棧頂元素時,將其壓入Min棧頂,當pop元素等於Min棧頂元素時,兩個棧均要pop出棧頂元素。下圖為各種情況的優缺點分析。

class StackWithMin
{
public:
	StackWithMin()
	{}

	~StackWithMin()
	{}

	void Push(const int& x)
	{
		s1.push(x);
		if(s2.empty() || x <= s2.top())
		{
			s2.push(x);
		}
	}

	void Pop()
	{
		assert(s1.size()>0 && s2.size()>0);
		if(s1.top() == s2.top())
		{
			s2.pop();
		}
		s1.pop();
	}

	int Min()
	{
		assert(s1.size()>0 && s2.size()>0);

		return s2.top();
	}
private:
	stack<int> s1;
	stack<int> s2; //用於儲存最下元素
};

2. 使用兩個棧實現一個佇列。

分析:一個棧用來push資料,一個棧用來pop資料。

圖解

實現

//使用兩個棧實現一個佇列。
class Queue
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty())
		{
			while(!stack1.empty())
			{
				stack2.push(stack1.top());
				stack1.pop();
			}
		}

		int top = stack2.top();
		stack2.pop();
		return top;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

3. 使用兩個佇列實現一個棧。

分析:使用兩個佇列不斷的倒換元素,實現一個棧。下面為圖解,以及程式碼的實現過程。

圖解

#include<iostream>
#include<queue>
using namespace std;

template <class T>
class QueueToStack
{
public:
	QueueToStack()
	{}

	~QueueToStack()
	{}

	void Push(T x)
	{
		if(q1.size()>0)//若q1不為NULL時,在q1中插入
		{
			q1.push(x);
		}
		else if(q2.size()>0)//若q2不為NULL時,在q1中插入
		{
			q2.push(x);
		}
		else
		{
			q1.push(x);//兩者均為NULL時,插入到q1
		}
	}

	void Pop()
	{
		if(q1.size()==0)//如果queue1為空
		{
			while(q2.size()>1)//保證queue2中有一個元素,將其餘元素儲存到queue1中
			{
				q1.push(q2.front());
				q2.pop();
			}
			q2.pop();

		}
		else//如果queue2為空
		{
			while(q1.size()>1)//保證queue2中有一個元素,將其餘元素儲存到queue1中
			{
				q2.push(q1.front());
				q1.pop();
			}
			q1.pop();

		}

	}

	T Top()
	{
		if(q1.size()==0)//如果queue1為空
		{
			while(q2.size()>1)//保證queue2中有一個元素,將其餘元素儲存到queue1中
			{
				q1.push(q2.front());
				q2.pop();
			}
			T& data=q2.front();
			q1.push(q2.front());
			q2.pop();
			return data;
		}
		else//如果queue2為空
		{
			while(q1.size()>1)//保證queue2中有一個元素,將其餘元素儲存到queue1中
			{
				q2.push(q1.front());
				q1.pop();
			}
			T& data=q1.front();
			q2.push(q1.front());

			q1.pop();
			return data;
		}
	}

	bool Empty()
	{
		return (q1.empty() && q2.empty());
	}

private:
	queue<T> q1;
	queue<T> q2;
};

4. 元素出棧、入棧順序的合法性。如入棧的序列(1,2,3,4,5),出棧序列為(4,5,3,2,1)。

分析:藉助輔助棧,如果下一個彈出的數字剛好是棧頂數字,那麼直接彈出。如果下一個彈出的數字不在棧頂,把壓棧序列中還沒有入棧的數字壓人輔助棧,直到把下一個需要彈出的數字壓入棧頂為止。如果所有的數字都壓入了仍然沒有找到下一個彈出的數字,那麼該序列不可能是一個彈出序列。

class Solution {
public:
   bool IsPopOrder(vector<int> pushV,vector<int> popV) 
	{
		if(pushV.size() == 0 || popV.size() == 0)
		{
			return false;
		}

        if(pushV.size() != popV.size())
		{
			return false;
		}

		stack<int> s; //藉助輔助棧
		int j=0;
		for(int i=0; i<popV.size(); ++i)
		{
			s.push(pushV[i]);

			//當棧頂元素和出棧序列元素相等時,出棧
			while(j<popV.size() && s.top() == popV[j])
			{
				s.pop();
				++j;
			}
		}

		return s.empty();
    }
};

5. 一個數組實現兩個棧。

   分析:一個數組實現兩個棧,分別從資料開始,和陣列末端實現。當兩個指標相當時,進行擴容。擴容時,需要將原陣列的資料寫入新陣列。

#include <iostream>
#include<assert.h>
using namespace std;

template<class T>
class TwoStack
{
public:
	//構造
	TwoStack()
		:_arr(NULL)
		,_top1(0)
		,_top2(0)
		,_capacity(0)
	{
		CheckCapacity();
	}

	//析構
	~TwoStack()
	{
		if(NULL != _arr) //_arr不為NULL時,釋放_arr
		{
			delete[] _arr;
		}
	}

	//拷貝構造
	TwoStack(const TwoStack<T>& ts)
		:_arr(new T[ts._capacity-ts._top2+ts._top1])
		,_top1(ts._top1)
		,_top2(_top1)
		,_capacity(ts._capacity-ts._top2+ts._top1)
	{
		//拷貝資料
		for(size_t i=0; i<_top1; i++)
		{
			_arr[i] = ts._arr[i];
		}

		size_t j = ts._capacity-1;
		for(size_t i=_capacity-1; i>_top2;i--,j--)
		{
			_arr[i] = ts._arr[j];
		}
	}

	//複製運算子過載
	TwoStack<T>& operator=(TwoStack<T> ts)
	{
		int * tmp = ts._arr;
		_top1 = ts._top1;
		_top2 = ts._top2;
		_capacity = ts._capacity;
		swap(_arr,tmp);
		return *this;
	}

	//push 、pop、top、size、empty
	void Push1(const int& x)
	{
		CheckCapacity();
		_arr[_top1++] = x;
	}

	void Push2(const int& x)
	{
		CheckCapacity();
		_arr[_top2--] = x;
	}

	void Pop1()
	{
		if(Size1() == 0)
		{
			return;
		}

		--_top1;
	}

	void Pop2()
	{
		if(Size2() == 0)
		{
			return;
		}

		--_top2;
	}

	int Top1()
	{
		assert(_top1 > 0);

		return _arr[_top1-1];
	}

	int Top2()
	{
		assert(_top2 < _capacity-1);

		return _arr[_top2+1];
	}

	size_t Size1()
	{
		return _top1;
	}

	size_t Size2()
	{
		return _capacity - _top2 - 1;
	}

	bool Empty1()
	{
		if(_top1 == 0)
		{
			return true;
		}
		return false;
	}

	bool Empty2()
	{
		if(_top2 == _capacity-1)
		{
			return true;
		}

		return false;
	}

protected:
	void CheckCapacity()
	{
		//1.開始時擴容  2._top1 == _top2時,擴容

		if(NULL == _arr)//開始時,當陣列為NULL時,給_arr分配空間
		{
			_capacity += 2;
			_arr = new int[_capacity];
			_top2 = _capacity - 1;

			return;
		}

		if(_top1 == _top2) //如果_top1==_top2時,應該進行擴容
		{
			size_t newcapacity = _capacity*2;
			int* tmp = new int[newcapacity];
			//拷貝資料
			for(size_t i=0; i<_top1; ++i)
			{
				tmp[i] = _arr[i];
			}
			size_t j = newcapacity - 1;
			for(size_t k=_capacity-1; k>_top2; --k)
			{
				tmp[j] = _arr[k];
				--j;
			}

			_top2 = j;
			_capacity = newcapacity;
			_arr = tmp;
		}
	}

private:
	T * _arr; //析構時,應銷燬陣列中的內容
	size_t _top1; //top1、top2分別表示棧的下標
	size_t _top2;
	size_t _capacity;
};