基本思路:
队列有针对队头的操作,也就是说需要操作栈底的元素,直观的想到另外开辟一个栈,然后将前面的元素倒入新开辟的栈中,这样就可以操作栈的元素了,也就实现了对队头的操作
大体思路基本一致,但是实现过程有小的不同,请开下面的详解:
方法一:
思路:有两个栈,s1作为存储空间,s2z作为临时缓存区,
(1)入队时,将元素压入s1
(2)出队时,将s1的元素逐个“倒入”(弹出并压入)s2,将s2的顶元素弹出作为出队元素,之后再将s2剩下的元素逐个“倒回”s1
具体实现如下:
#include<iostream>
#include<stack>
#include<assert.h>
using namespace std;
template<class T>
class TwoStackOfQueue
{
public:
TwoStackOfQueue(){};
~TwoStackOfQueue(){};
public:
void Push(const T& x);
void Pop();
T& Front();//<span style="color:#ff0000;">注意写法</span>
const T& Back();
bool Empty()const;
size_t Size()const;
void Print();
protected:
void PushToPop();
void PopToPush();
private:
stack<T> stackPush;
stack<T> stackPop;
};
template<class T>
void TwoStackOfQueue<T>::PushToPop()
{
while (!stackPush.empty())
{
stackPop.push(stackPush.top());
stackPush.pop();
}
}
template<class T>
void TwoStackOfQueue<T>::PopToPush()
{
while (!stackPop.empty())
{
stackPush.push(stackPop.top());
stackPop.pop();
}
}
template<class T>
void TwoStackOfQueue<T>::Push(const T& x)
{
stackPush.push(x);
}
template<class T>
void TwoStackOfQueue<T>::Pop()
{
assert(!stackPush.empty());
PushToPop();
stackPop.pop();
PopToPush();
}
template<class T>
T& TwoStackOfQueue<T>::Front()
{
assert(!stackPush.empty());
//PushToPop();
//T& ret = stackPop.top();//<span style="color:#ff0000;">注意,这种写法是错误的,引用的底层相当于指针,倒回去的时候,Front又回到了栈底,
//之所以不崩溃,大概系统栈的底层实现是因为通过数组实现的,而不是通过链式结构</span>
while (stackPush.size() != 1)
{
stackPop.push(stackPush.top());
stackPush.pop();
}
T& ret = stackPush.top();//要加引用,要不然不可以改变其中的值
PopToPush();
return ret;
}
template<class T>
const T& TwoStackOfQueue<T>::Back()
{
assert(!stackPush.empty());
return stackPush.top();
}
template<class T>
bool TwoStackOfQueue<T>::Empty()const
{
return stackPush.empty();
}
template<class T>
size_t TwoStackOfQueue<T>::Size()const
{
return stackPush.size();
}
template<class T>
void TwoStackOfQueue<T>::Print()
{
PushToPop();
while (!stackPop.empty())
{
cout << stackPop.top()<<" ";
stackPush.push(stackPop.top());
stackPop.pop();
}
cout << endl;
}
其实有一个细节是可以优化一下的。即:在出队时,将s1的元素逐个“倒入”s2时,原在s1栈底的元素,不用“倒入”s2(即只“倒”s1.Count()-1个),可直接弹出作为出队元素返回。这样可以减少一次压栈的操作。
方法二:
思路:这其实是上面犯法的一种优化,上述实现的队列需要多次连续的Pop()的话就需要频繁的 将两个栈的元素来回倒,其实可以不用倒的
(1)入队时,先判断s1是否为空,如不为空,说明所有元素都在s1,此时将入队元素直接压入s1;如为空,要将s2的元素逐个“倒回”s1,再压入入队元素。
(2)出队时,先判断s2是否为空,如不为空,直接弹出s2的顶元素并出队;如为空,将s1的元素逐个“倒入”s2,把最后一个元素弹出并出队
具体实现如下:
#include<iostream>
#include<stack>
#include<assert.h>
using namespace std;
template<class T>
class TwoStackOfQueue
{
public:
TwoStackOfQueue(){};
~TwoStackOfQueue(){};
public:
void Push(const T& x);
void Pop();
T& Front();//注意写法
const T& Back();
bool Empty()const;
size_t Size()const;
public:
void PushToPop();
void PopToPush();
private:
stack<T> stackPush;
stack<T> stackPop;
};
template<class T>
void TwoStackOfQueue<T>::PushToPop()
{
while (!stackPush.empty())
{
stackPop.push(stackPush.top());
stackPush.pop();
}
}
template<class T>
void TwoStackOfQueue<T>::PopToPush()
{
while (!stackPop.empty())
{
stackPush.push(stackPop.top());
stackPop.pop();
}
}
template<class T>
void TwoStackOfQueue<T>::Push(const T& x)
{
if (stackPush.empty())
{
PopToPush();
}
stackPush.push(x);
}
template<class T>
void TwoStackOfQueue<T>::Pop()
{
if (stackPop.empty())
{
PushToPop();
}
stackPop.pop();
}
template<class T>
T& TwoStackOfQueue<T>::Front()
{
if (stackPop.empty())
{
assert(!stackPush.empty());
PushToPop();
}
return stackPop.top();
}
template<class T>
const T& const TwoStackOfQueue<T>::Back()
{
if (stackPush.empty())
{
assert(!stackPop.empty());
PopToPush();
}
return stackPush.top();
}
template<class T>
bool TwoStackOfQueue<T>::Empty()const
{
if (stackPush.empty())
{
return stackPop.empty();
}
return stackPush.empty();
}
template<class T>
size_t TwoStackOfQueue<T>::Size()const
{
if (stackPush.empty())
{
return stackPop.size();
}
return stackPush.size();
}
这种写法解决了,需要多次连续删除的情况,但是还可以更加简便一点,请看方法三
方法三:
思路:其实 两个栈来回倒,仔细思考便会发现,其实可以想象一下,本身两个栈一个栈顶元素是对应的队头,而一个栈顶元素是对应的队尾,那么只要保证两个栈中间的元素顺序永不打乱,每次对栈顶的操作,就可以模拟对队头和队尾的操作
(1)入队时,将元素压入s1。
(2)出队时,判断s2是否为空,如不为空,则直接弹出顶元素;如为空,则将s1的元素逐个“倒入”s2,把最后一个元素弹出并出队。
具体实现如下:
#include<iostream>
#include<stack>
#include<assert.h>
using namespace std;
template<class T>
class TwoStackOfQueue
{
public:
TwoStackOfQueue(){};
~TwoStackOfQueue(){};
public:
void Push(const T& x);
void Pop();
T& Front();
const T& Back();
bool Empty()const;
size_t Size()const;
protected:
void PushToPop();
void PopToPush();
private:
stack<T> stackPush;
stack<T> stackPop;
};
template<class T>
void TwoStackOfQueue<T>::PushToPop()
{
while (!stackPush.empty())
{
stackPop.push(stackPush.top());
stackPush.pop();
}
}
template<class T>
void TwoStackOfQueue<T>::PopToPush()
{
while (!stackPop.empty())
{
stackPush.push(stackPop.top());
stackPop.pop();
}
}
template<class T>
void TwoStackOfQueue<T>::Push(const T& x)
{
stackPush.push(x);
}
template<class T>
void TwoStackOfQueue<T>::Pop()
{
if (stackPop.empty())
{
PushToPop();
}
assert(!stackPop.empty());
stackPop.pop();
}
template<class T>
T& TwoStackOfQueue<T>::Front()
{
if (stackPop.empty())
{
PushToPop();
}
assert(!stackPop.empty());
return stackPop.top();
}
template<class T>
const T& TwoStackOfQueue<T>::Back()
{
if (stackPush.empty())
{
PopToPush();
}
return stackPush.top();
}
template<class T>
bool TwoStackOfQueue<T>::Empty()const
{
return stackPush.empty() || stackPop.empty();
}
template<class T>
size_t TwoStackOfQueue<T>::Size()const
{
return stackPush.size() + stackPop.size();
}
这种方法需要满足栈的中间元素顺序永远不改变的原则,这就需要保证一下两个条件:
1、如果stackPush要往stackPop中压入数据,那么必须一次性把stackPush中的数据全部压入
2。如果stackPop不为空,stackPush绝对不能向stackPop中压入数据