栈和队列的理论基础
栈(stack):
特点:先进后出(LIFO)
java底层:
Java中的Stack<E>类继承了Vector<E>类
一般使用Deque<E>实现stack【Deque<Integer> stack = new ArrayDeque<Integer>();】
基本操作:
push() ---- 进栈
pop() ---- 出栈
peek() ---- 查看栈顶元素
empty() ---- 测试栈是否为空
实现:
使用链表实现栈
使用数组实现栈
使用队列实现栈
队列(queue):
特点:后进后出(FIFO)
Queue<E>的java底层:
Java中Queue<E>接口继承了Collection<E>接口
一般用LinkedList<E>类进行实现
Queue基本操作:
add()、offer() ---- 从队尾添加元素
remove()、poll() ---- 从队头删除元素
element()、peek() ---- 查看队头元素
Deque<E>的java底层:
Java中Deque<E>接口继承了Queue<E>接口
支持在两端插入和移除元素
deque <----> double ended queue 双端队列
Deque<E>可以等效Queue<E>
Deque<E>可以等效Stack<E>,优先使用此接口
Deque基本操作:
addFirst()、offerFirst() ---- 在队头添加元素
removeFirst()、pollFirst() ---- 从队头删除元素
getFirst()、peekFirst() ---- 查看队头元素
addLast()、offerLast() ---- 在队尾添加元素
removeLast()、pollLast() ---- 从队尾删除元素
getLast()、peekLast() ---- 查看队尾元素
实现:
使用链表实现队列
使用数组实现队列
使用栈实现队列
232.用栈实现队列
题目链接:力扣
思路
首先使用一个栈来模拟队列,入队相当于入栈,但是出队的话队头还压在栈底,要出来的话就需要将此栈中的元素全部弹出。
此时可以用另一个栈来保存弹出的元素,那么上一个栈中的栈底元素就在这个栈的栈顶元素,就是队列的队头,如果需要出队和查看队头就都可以进行操作了
用栈实现队列
第一步:定义一个入栈和一个出栈
第二步:实现push() --- 最好实现
第三步:实现pop() --- 出栈为出队
首先需要判断出栈是不是为空
如果出栈为空,要将入栈中的所有元素加入到出栈中,再从出栈中进行出队
如果不为空,说明出栈中还有元素,元素从出栈中进行出队
第四步:实现peek()
首先需要判断出栈是否为空
如果出栈为空,要将入栈中的所有元素加入到出栈中,再从出栈中返回队头
如果不为空,说明出栈中还有元素,从出栈中返回队头
第五步:实现isEmpty()
只有出栈和入栈中所有的元素为空,才算整个队列为空
class MyQueue { Stack<Integer> stackIn; Stack<Integer> stackOut; // 对队列进行初始化 public MyQueue() { stackIn = new Stack<>(); stackOut = new Stack<>(); } public void push(int x) { stackIn.push(x); } public int pop() { if (stackOut.isEmpty()) { while (!stackIn.isEmpty()) { stackOut.push(stackIn.pop()); } } return stackOut.pop(); } public int peek() { if (stackOut.isEmpty()) { while (!stackIn.isEmpty()) { stackOut.push(stackIn.pop()); } } else {} return stackOut.peek(); } public boolean empty() { return stackIn.isEmpty() && stackOut.isEmpty(); } }
225.用队列实现栈
题目链接:力扣
思路
因为有了上道题的经验,首先的思维就是用两个队列对栈进行实现,按照上道题目的思路是不可以的,因为把一个队列移动到另一个队列,出队的顺序并没有发生变化。所以需要转换思路。如果每次添加的数字都不出现在队尾,而是出现在队头就可以了。
那就每次添加数字的时候,先进q2,然后把q1已有的元素添加到q2,此时q2就是栈的顺序了,q1就为空了。但是我们得固定一个队列进行出栈操作,所以就把q1和q2进行交换。此时q1就是一个栈了,q2就为空,这样就实现了栈
那么如何用一个队列实现栈呢,压栈可以是入队,但是弹栈的时候需要队尾元素出来,怎么办呢,那就可以跟进队列的长度,把除队尾元素的元素全部出队再入队,这样队尾元素就到队头了,可以进行pop
那么top怎么实现呢,因为只查不删,所以直接返回队尾元素就可以了,java的Queue接口没有直接返回队尾元素的功能,所以我们要么使用Deque接口,要么就需要在添加元素的时候下功夫了,这样的队列才可以top和pop
随意每添加一个元素,就给队列中的元素重新排个序,保持队列中一直是压栈的顺序
如果使用Deque接口的话,就直接在每次删除的时候变换顺序就可以了,查询的话查询队尾就可以
用队列实现栈
用两个队列实现栈
第一步:创建两个队列
第二步:实现push() --- 压栈
先进q2进行待命
让q1中的元素进q2,实现了新进的元素都在队头(栈顶)
让q1和q2进行交换
第三步:实现pop()
q1进行出队
第四步:实现peek()
q1进行考察队头
第五步:实现empty()
q1是否为空
class MyStack { Queue<Integer> queue1; Queue<Integer> queue2; public MyStack() { queue1 = new LinkedList<>(); queue2 = new LinkedList<>(); } // 向栈中添加元素,这里queue2就是个备份队列 // 经过一番操作,queue1其实就是栈的顺序 public void push(int x) { queue2.offer(x); while (!queue1.isEmpty()) { queue2.offer(queue1.poll()); } // 到此处,queue2就有栈的顺序了,queue为空队列,已经被搬空了 // 然后再对queue1和queue2进行交换 Queue<Integer> queueTemp; queueTemp = queue1; queue1 = queue2; queue2 = queueTemp; } public int pop() { return queue1.poll(); } public int top() { return queue1.peek(); } public boolean empty() { return queue2.isEmpty(); } }
用一个队列实现栈
第一步:创建队列
第二步:实现push()
为了让top和pop更加方便,避免这两个操作临近操作时出错,在添加的时候就将顺序排成栈的顺序
每入队一个元素,就将前面的元素出队再入队,这样保证了队头是栈顶元素
第三步:实现pop()
排好队的队列出队
第四步:实现top()
排好队的队列查队头
第五步:实现empty()
队列是否为空
class MyStack { Queue<Integer> queue; public MyStack() { queue = new LinkedList<>(); } public void push(int x) { queue.offer(x); int len = queue.size() - 1; while (len-- > 0) { queue.offer(queue.poll()); } } public int pop() { return queue.poll(); } public int top() { return queue.peek(); } public boolean empty() { return queue.isEmpty(); } }