<剑指offer>之栈和队列所有题目

【引言】stack和queue的题目不太多,而且几道题目大概思路要么是构造辅助stack或者queue,要么是根据stack的FILO和queue的FIFO特性下手有一定的技巧性

【题目7】用两个队列实现栈

用两个栈实现队列。队列声明如下,请实现它的两个函数append_tail和delete_head在队列的队尾添加数据和删除队头
template<typename T>
class Queue_by_stack
{
public:
	Queue_by_stack(){};
	~Queue_by_stack(){};

	void append_tail(const T& node);
	T delete_head();
protected:
private:
	stack<T> stack1;
	stack<T> stack2;
};

分析:队列的特性是先进后出,比如对于1,2,3,4序列来说,首先将序列压入栈(stack1和stack2随便选一个),要实现delete_head的话,不能对stack1进行pop,因为这时stack1的top值是4,我们要删除的是1,注意这时候stack2还没有利用,如果我将stack1里的数据都逐个pop,push进去stack2,对于stack2;来说,那不就正好相当于原序列吗?实现delete_head的话只需对stack2进行pop就行了,append_tail的话只需往空的stack1里面push就行了。代码实现如下
【代码】
#include <iostream>
#include <stack>
using namespace std;

template<typename T>
class Queue_by_stack
{
public:
	Queue_by_stack(){};
	~Queue_by_stack(){};

	void append_tail(const T& node);
	T delete_head();
protected:
private:
	stack<T> stack1;
	stack<T> stack2;
};


template<typename T>
T Queue_by_stack<T>::delete_head()
{
	T tmp;
	if (stack2.empty())
	{
		while (!stack1.empty())
		{
			tmp = stack1.top();
			stack2.push(tmp);
			stack1.pop();
		}
	}
	if (stack2.empty())
	{
		return -1;
	}
	tmp = stack2.top();
	stack2.pop();
	return tmp;		
}

template<typename T>
void Queue_by_stack<T>::append_tail( const T& node )
{
	stack1.push(node);
}
【测试】
int main()
{
#if 0
	Queue_by_stack<char> my_queue;
	my_queue.append_tail('a');
	my_queue.append_tail('b');
	my_queue.append_tail('c');

	my_queue.delete_head();
	my_queue.delete_head();
	my_queue.delete_head();
	my_queue.delete_head();
	my_queue.append_tail('d');
	my_queue.append_tail('e');
	my_queue.delete_head();
#endif
}

【扩展】使用两个队列实现一个栈

这个栈的声明如下:
template<typename T>
class Stack_by_queue
{
public:
	Stack_by_queue(){};
	~Stack_by_queue(){};

	void append_tail(const T& node);
	T delete_head();
protected:
private:
	queue<T> queue1;
	queue<T> queue2;
};


分析:栈的特性是先进后出,举一个序列1,2,3,4来说,我们试着往一个queue1里面push进去,这时候queue1的队头是1,队尾是4,这时候要实现delete_head的话,对应的栈应该删除4,对于queue1的队尾,前面的1,2,3,都是不需要的。
实现dalete_head解决方法就是,依次弹出1,2,3并且压入queue2中,queue1里面只保存4,这时候要delete_head的话,对queue1进行pop操作就行了,然后queue1为空(注意这个状态),然后我要继续delete_head,这个时候也是按照上面的思路,将queue2的1,2依次弹出,压入queue1里面,知道剩下最后一个队尾元素3,将它pop掉就行了!这时候的状态是queue1不为空,queue2为空。
实现append_tail的话也容易。注意上面的删除头以后的两种状态其实可以可以归结为一种,那就是其中一个queue为空,另一个可以为空(这个时候模拟的stack就是空),或者不为空,append_tail来说,只需往非空的queue里面添到队尾就行了,若是都为空,随便选一个即可
【实现】
#include <queue>
using namespace std;
template<typename T>
class Stack_by_queue
{
public:
	Stack_by_queue(){};
	~Stack_by_queue(){};

	void append_tail(const T& node);
	T delete_head();
protected:
private:
	queue<T> queue1;
	queue<T> queue2;
};

//保证在所有的过程中,至少队列有一个是空的
template<typename T>
T Stack_by_queue<T>::delete_head()
{
	T tmp;
	if(queue1.empty() && !queue2.empty())
	{
		//2->1
		
		if (queue2.size() < 1)
		{
			return -1;
		}
		while(queue2.size() != 1)
		{
			tmp = queue2.front();
			queue2.pop();
			queue1.push(tmp);
		}
		tmp = queue2.front();
		queue2.pop();
		return tmp;
	}
	if (!queue1.empty() && queue2.empty())
	{
		//1->2
		T tmp;
		if (queue1.size() < 1)
		{
			return -1;
		}
		while(queue1.size() != 1)
		{
			tmp = queue1.front();
			queue1.pop();
			queue2.push(tmp);
		}
		tmp = queue1.front();
		queue1.pop();
		return tmp;

	}
	else
		return -1;	
}

template<typename T>
void Stack_by_queue<T>::append_tail( const T& node )
{
	//保证有一队列为空,若全为空,则队空,任选一个队列就行
	if (queue1.empty() && !queue2.empty())
		queue2.push(node);
	if (!queue1.empty() && queue2.empty())
		queue1.push(node);
	if (queue1.empty() && queue2.empty())
		queue1.push(node);
	else 
		return;
}

【测试】
int main()
{
#if 1


	Stack_by_queue<char> my_stack;
	my_stack.append_tail('a');
	my_stack.append_tail('b');
	my_stack.append_tail('c');
	my_stack.delete_head();
	my_stack.delete_head();
	my_stack.delete_head();
	my_stack.delete_head();
	my_stack.append_tail('d');
	my_stack.append_tail('e');
	my_stack.delete_head();
	my_stack.append_tail('f');
#endif
}

【题目21】包含min函数的栈

描述:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的函数。在该栈中,调用min,push,pop的时间复杂度都是O(1)
思路:首先是一个栈,这个栈已经是最常见的那种,我们需要改造它使它能够在O(1)时间内返回最小元素,刚开始的思路是往栈里面添加一个元素放在stack的顶部,表示当前栈里面的最小元素,如果压入的元素比它小,那么更新它,并且将它pop出来,放在一个临时值里面,将刚才要压入的序列元素push进去栈,再将最小值压进去,看上去可行。但这样有问题,比如说我对栈pop操作,我需要将栈顶的最小元素弹出,存起来,然后将真正的栈顶元素弹出,这个时候我要是再把刚才保存的栈顶元素压入原栈,就不一定对了,因为删除一个元素以后,最小元素也要更新,如何得到次小元素的值呢?明显我们需要保存次小,再次小。。。的值,为此我们使用两个栈,一个是普通的数据栈,data_stack,和另外一个auxi_stack辅助栈,首先在空的时候都压入两个栈,然后对于压入的序列,比较这个元素和辅助栈栈顶元素,如果小于这个栈顶那么在压入数据栈的时候也压入辅助站,否则,将辅助栈的顶部元素再次压入一次,这样保证了两个栈的大小相等,这样才能在pop的时候同步,否则我在调用min以后更新候判断就比较麻烦。
这样需要的空间就是两个相等的栈
【代码】
template <typename T>
class  min_stack
{
public:
	min_stack(){};
	~min_stack(){};

	T my_min();
	void my_push(const T& data);
	void my_pop();
protected:
private:
	stack<T> data_stack;
	stack<T> auxi_stack;
};

template <typename T>
void min_stack<T>::my_pop()
{
	if (auxi_stack.empty() && data_stack.empty())
		return;
	else
	{
		auxi_stack.pop();
		data_stack.pop();
	}
}

template <typename T>
void min_stack<T>::my_push(const T& data)
{
	data_stack.push(data);
	if(auxi_stack.empty() || data < auxi_stack.top())
		auxi_stack.push(data);
	else
		auxi_stack.push(auxi_stack.top());

}

template <typename T>
T min_stack<T>::my_min()
{
	if(!auxi_stack.empty())
		return auxi_stack.top();
	else
		return -1;
}
测试】
int main()
{
	min_stack<int> my_min_stack;
	my_min_stack.my_push(3);
	my_min_stack.my_push(2);
	my_min_stack.my_push(1);
	my_min_stack.my_push(8);
	my_min_stack.my_min();
	my_min_stack.my_pop();
	my_min_stack.my_min();
	my_min_stack.my_push(2);
	my_min_stack.my_push(1);
	my_min_stack.my_min();


}

【题目22】判断压入弹出顺序问题

题目描述:
对于两个序列,第一个表示栈的压入顺序,第二个序列表示栈的弹出顺序,判断下第二个序列是否是第一个序列的弹出顺序。假设原数序列元素都不相等。比如对于原始序列1,2,3,4,5来说,序列4,5,1,2,3就是它的一个弹出序列,对原始序列执行的操作时push,push,push,push,pop,push,pop,pop,pop,pop
思路:对于这种问题需要慢慢分析,对于4,5,1,2,3来说,要首先弹出一个4,必须将原始序列中4之前的元素都压栈,然后弹出4,然后看第二个元素5,首先栈顶不是这个5,我们需要在上次压栈改变后原始序列中找,只剩第五个元素5,压栈,弹出,第三个元素3,栈顶就是,弹出,依次这样。。。如果碰见在原始序列中找不到并且栈顶也不是这个元素的情况,无论如何也不能是原始序列,这个元素肯定在栈里面,非栈顶。so,代码如下。。

【代码】
#include <stack>
using namespace std;

//思路:遍历第二个序列,对于序列的每个元素,首先看这个元素在不在栈顶,在的话弹出;
//不在的话遍历第一个序列,一边遍历一边将第一个序列元素压栈,如果第一个序列没有,则证明则第二个序列的这个元素已经压进去栈了
bool is_pop_order(const int* to_push, const int* to_pop, int len)
{
	if(to_push== NULL || to_pop== NULL || len < 1)
		return false;
	stack<int> tmp_stack;	
	int tmp;
	int i, j;
	for (i = 0, j = 0; i < len; i++)
	{
		tmp = to_pop[i];
		if (!tmp_stack.empty() && tmp == tmp_stack.top())
		{
			tmp_stack.pop();
		}
		else
		{
			while(j < len && tmp != to_push[j])
			{
				tmp_stack.push(to_push[j]);
				++j;
			}
			if (j == len)//第一个序列里面没有,失败
			{
				return false;
			}
			else//在第一个序列的j个位置
			{
				++j;
			}
		}
	}
	return true;
}
【测试】
int main()
{

	int a[] = {1,2,3,4,5};
	int	b[] = {4,5,3,2,1};
	int c[] = {4,3,5,1,2};
	int d[1] = {5};
	int e[1] = {5};
	bool res1 = is_pop_order(a, b, sizeof(a)/sizeof(a[0]));
	bool res2 = is_pop_order(a, c, sizeof(a)/sizeof(a[0]));
	bool res3 = is_pop_order(d,e,1);
}

【扩展】记得微软3月份的时候

有过类似的题目,现在找出来。



待续。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值