【引言】stack和queue的题目不太多,而且几道题目大概思路要么是构造辅助stack或者queue,要么是根据stack的FILO和queue的FIFO特性下手有一定的技巧性
【题目7】用两个队列实现栈
用两个栈实现队列。队列声明如下,请实现它的两个函数append_tail和delete_head在队列的队尾添加数据和删除队头
- <span style="background-color: rgb(255, 255, 255);">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;
- };</span>
分析: 队列的特性是先进后出,比如对于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就行了。代码实现如下
【代码】
- <span style="background-color: rgb(255, 255, 255);">#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);
- }</span>
【测试】
- <span style="background-color: rgb(255, 255, 255);">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
- }</span>
【扩展】使用两个队列实现一个栈
这个栈的声明如下:
- 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月份的时候
有过类似的题目,现在找出来。
待续。