理论基础
1、java中Stack 是容器么?
是容器。java.util.Stack<E>是Vector<E>集合的子类,底层物理结构是动态数组,线程安全。Stack 比 Vector 多了几个方法:
(1) peek():查看栈顶元素,不弹出;
(2)pop():弹出栈;
(3)push():压入栈,即添加到链表的头。
2、栈里面的元素在内存中是连续分布的么?
栈是容器适配器,使用不同的底层容器,会影响到栈内数据在内存中的分布。缺省情况下的默认底层容器是deque,在内存中的数据分布不连续。
3、栈的实现:
Deque stack=new ArrayDeque();
Deque stack = new LinkedList();
双端队列 Deque 可用作 LIFO(后进先出)堆栈。应优先使用此接口而非遗留 Stack 类。在将Deque 用作 Stack 时,元素被推入 Deque 的开头并从 Deque 开头弹出。Stack 方法完全等效于 Deque 方法,如下表所示:
堆栈方法 | 等效 Deque 方法 |
---|---|
push(e) | addFirst(e) |
pop() | removeFirst() |
peek() | peekFirst() |
4、队列的实现:
Queue queue = new LinkedList();
Queue 除了基本的 Collection 操作外,还提供其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null / false)。
抛出异常 | 返回特殊值 | |
---|---|---|
插入 | add(e) | offer(e) |
移除 | remove() | poll() |
检查 | element() | peek() |
Deque 接口扩展了 Queue 接口。在将 Deque 用作队列时,将得到 FIFO(先进先出)行为。Deque 接口提供插入、移除和检查元素的方法。
第一个元素(头部) | 最后一个元素(尾部) | |||
---|---|---|---|---|
抛出异常 | 特殊值 | 抛出异常 | 特殊值 | |
插入 | addFirst(e) | offerFirst(e) | addLast(e) | offerLast(e) |
移除 | removeFirst() | pollFirst() | removeLast() | pollLast() |
检查 | getFirst() | peekFirst() | getLast() | peekLast() |
此外,从 Queue 接口继承的方法完全等效于 Deque 方法。
Queue 方法 | 等效 Deque 方法 |
---|---|
add(e) | addLast(e) |
offer(e) | offerLast(e) |
remove() | removeFirst() |
poll() | pollFirst() |
element() | getFirst() |
peek() | peekFirst() |
232.用栈实现队列
思路:
1. 输入栈+输出栈,实现队列。
2. push 操作:将数据放入输入栈in。pop操作:若输出栈out为空,则导入in的所有数据,弹出;若非空,直接弹出。
3. isEmpty判断:当in和out均为空时,队列为空。
4. peek 操作:若out为空,则导入in的所有数据,out.peek();若非空,直接peek()。
class MyQueue {
Stack<Integer> in;
Stack<Integer> out;
public MyQueue() {
in = new Stack<>();
out = new Stack<>();
}
public void push(int x) {
in.push(x);
}
public int pop() {
dumpStackIn();
return out.pop();
}
public int peek() {
dumpStackIn();
return out.peek();
}
public boolean empty() {
return in.isEmpty() && out.isEmpty();
}
private void dumpStackIn(){//如果out为空,就将in的元素全部加入out中
if(!out.isEmpty()) return;
while(!in.isEmpty()) out.push(in.pop());
}
}
/**
* 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. 用队列实现栈
思路:
1. 队列 queue1 用来实现操作,队列 queue2 用来在操作过程中暂时保存其他元素。
2. push 操作:queue1.push()。pop 操作:当 queue2 为空时,先把 queue1 的所有元素倒着存到 queue2,然后 queue2.pop(),最后将元素放回 queue1。
//两个Queue
class MyStack {
Queue<Integer> que1;
Queue<Integer> que2;
public MyStack() {
que1 = new LinkedList<>();
que2 = new LinkedList<>();
}
public void push(int x) {
que2.offer(x); // 先放在辅助队列中
while (!que1.isEmpty()){
que2.offer(que1.poll());
}
Queue<Integer> temp = que1;
que1 = que2;
que2 = temp; // 交换que1和que2,将元素都放到que1中
}
public int pop() {
return que1.poll();
}
public int top() {
return que1.peek();
}
public boolean empty() {
return que1.isEmpty();
}
}
/**
* 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();
*/
//两个Deque
class MyStack {
Queue<Integer> que1;
Queue<Integer> que2;
public MyStack() {
que1 = new ArrayDeque<>();
que2 = new ArrayDeque<>();
}
public void push(int x) {
que1.add(x);
}
public int pop() {
dumpQue();
int i = que1.remove();
makeQue();
return i;
}
public int top() {
dumpQue();
int i = que1.peek();
makeQue();
return i;
}
public boolean empty() {
return que1.isEmpty() && que2.isEmpty();
}
private void dumpQue(){
if(!que2.isEmpty()) return;
while(que1.size()!=1) que2.add(que1.remove());
}
private void makeQue(){
while(!que1.isEmpty()) que2.add(que1.remove());
while(!que2.isEmpty()) que1.add(que2.remove());
}
}
优化:
1. 一个队列在模拟栈弹出元素的时,只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了。
class MyStack {
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
//每offer一个数进来,都重新排列,把这个数放到队列的队首
public void push(int x) {
queue.offer(x);
int size = queue.size();
while (size-- > 1)
queue.offer(queue.poll());
}
public int pop() {
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}