【引言】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月份的时候
有过类似的题目,现在找出来。
待续。