目录
一、栈与队列理论基础
1.1栈
1.1.1java中给出的栈的基本操作:
1.1.2 使用单链表实现栈:头进头出
1.1.3使用双链表来实现栈:头进头出或尾进尾出,以LinkedList为例(底层为双链表)
也可以自己手写双链表来实现上述方法;
1.2队列
1.2.1Java中给出的队列的基本操作:
1.2.2可手写使用单链表实现队列:注意要尾(tail)进头(head)出,如果是头进尾出,那么遍历到tail的前一个节点需要O(n)的操作,十分麻烦。
1.2.3可手写使用双链表实现队列:可头进尾出,也可尾进头出。
1.2.4循环队列:用数组实现的队列
为什么使用循环?
一个存满的数组,先出队一个,如果再进队尾rear下标就越界了,但数组中还有空间没有利用 =》 对于这种情况所以使用了循环;
共定义三个变量:
(1)front:指向队头即将要出队的元素下标,初始化为0
(2)tail:指向队尾,下一个元素进队后应该存放的位置下标,初始化为0
(3)size:记录当前队列中元素的个数,以此来标记该队列(数组)是否已满(关键)
无论是在操作入队还是出队,都要先判断size的大小,若出队时size=0,那么报错,若入队时,size==数组的length,那么报错。当front或tail < 数组.length-1时,入队或出队后指针++即可,否则置为0,以此来实现循环队列。
二、力扣232.用栈实现队列
题目:
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
思路:
用两个栈来实现队列(画图模拟一下,更清晰)。
在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
最后如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。
代码:
class MyQueue {
//成员变量,两个标准的栈
Stack<Integer> pushStack;
Stack<Integer> popStack;
//空构造器
public MyQueue() {
pushStack = new Stack<>();
popStack = new Stack<>();
}
public void push(int x) {
pushStack.push(x);
}
public int pop() {
//如果popStack为空,那么需要将pushStack的数据倒入其中,否则直接pop
if(popStack.isEmpty()){
while(!pushStack.isEmpty()){
popStack.push(pushStack.pop());
}
return popStack.pop();
}else{
return popStack.pop();
}
}
public int peek() {
if(popStack.isEmpty()){
while(!pushStack.isEmpty()){
popStack.push(pushStack.pop());
}
return popStack.peek();
}else{
return popStack.peek();
}
}
//判断队列是否为空
public boolean empty() {
return pushStack.isEmpty() && popStack.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
三、力扣225.用队列实现栈
题目:
使用队列实现栈的下列操作:
- push(x) – 元素 x 入栈
- pop() – 移除栈顶元素
- top() – 获取栈顶元素
- empty() – 返回栈是否为空
注意: - 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
- 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
- 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
思路:
(1)用两个队列来模拟栈:
用两个队列que1和que2实现栈的功能,其中一个queue其实完全就是一个备份的作用,比如把que1最后面的元素以外的元素都添加到que2,然后弹出queue最后面的元素,即为pop;
注意:哪个queue不为空,哪个queue就是主队列,另个队列为辅助队列,实际上是来回切换的,在操作数据之前先判空来确定哪个是主队列哪个是辅助队列,这很重要。
(2)用一个队列来模拟栈(推荐):
其实这道题目就是用一个队列就够了。
一个队列在模拟栈弹出元素的时候只要将 队列的元素(除了最后一个元素外) 重新添加到队列尾部,使得刚刚加入进来的数据永远置于队首,此时再去弹出元素就是栈的顺序了。
代码:
(1)两个队列来模拟栈:
class MyStack {
//用两个队列来实现栈
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
public void push(int x) {
//哪个队列不为空,就往哪个里面加
if(!queue1.isEmpty()){
queue1.offer(x);
}else{
queue2.offer(x);
}
}
public int pop() {
if(!queue1.isEmpty()){
queueToQueue(queue1,queue2);
return queue1.poll();
}else{
queueToQueue(queue2,queue1);
return queue2.poll();
}
}
public int top() {
if(!queue1.isEmpty()){
queueToQueue(queue1,queue2);
int res = queue1.peek();
queue2.offer(queue1.poll());
return res;
}else{
queueToQueue(queue2,queue1);
int res = queue2.peek();
queue1.offer(queue2.poll());
return res;
}
}
public boolean empty() {
return queue1.isEmpty() && queue2.isEmpty();
}
public void queueToQueue(Queue queueOut,Queue queueIn){
while(queueOut.size() > 1){
queueIn.offer(queueOut.poll());
}
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
(2)一个队列来模拟栈(推荐):
class MyStack {
//用一个队列来实现栈
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
public void push(int x) {
queue.offer(x);
}
public int pop() {
lastInHead();
return queue.poll();
}
public int top() {
lastInHead();
int res = queue.peek();
queue.offer(queue.poll());
return res;
}
public boolean empty() {
return queue.isEmpty();
}
//将刚刚加入的元素置于队头
public void lastInHead(){
int loop = queue.size();
//除了最后一个元素,其他的都依次队头出队尾进
while(loop > 1){
queue.offer(queue.poll());
loop--;
}
}
}