栈与队列的理论基础
1、java中stack 是容器么?
是容器,stack是继承自Vector,底层使用数组存储、用来模拟栈的一个java集合。同时也是线程安全的。
2、栈里面的元素在内存中是连续分布的么?
这个问题有两个陷阱:
- 陷阱1:栈是容器适配器,底层容器使用不同的容器,导致栈内数据在内存中是不是连续分布。
- 陷阱2:缺省情况下,默认底层容器是deque,那么deque的在内存中的数据分布是什么样的呢? 答案是:不连续的,下文也会提到deque。
3、栈的实现:
一个是用java本身的集合类型Stack类型;另一个是借用LinkedList来间接实现Stack。
Deque stack=new ArrayDeque();
Deque stack = new LinkedList();
栈方法 等效方法
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst() isEmpty() //判断是否为空
4、队列的实现:
Queue queue = new LinkedList();
队列方法 等效方法
offer(e) offer(e)/offerLast(e) //进队列,将元素加入队列末尾
poll() poll()/pollFirst() //获取队列头的元素并移除
peek() peek()/peekFirst() //获取队列头的元素 isEmpty() //判断是否为空
5、栈在系统中的应用
如果还记得编译原理的话,编译器在 词法分析的过程中处理括号、花括号等这个符号的逻辑,就是使用了栈这种数据结构。
再举个例子,linux系统中,cd这个进入目录的命令我们应该再熟悉不过了。
cd a/b/c/../../
这个命令最后进入a目录,系统是如何知道进入了a目录呢 ,这就是栈的应用。这在leetcode上也是一道题目,编号:71. 简化路径,大家有空可以做一下。
递归的实现是栈:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
所以栈在计算机领域中应用是非常广泛的。
栈经典题目
232-用栈实现队列 Deque push pop
使用栈实现队列的下列操作:
- push(x) – 将一个元素放入队列的尾部。
- pop() – 从队列首部移除元素。
- peek() – 返回队列首部的元素。
- empty() – 返回队列是否为空。
/**
* 队列是一种 先进先出(first in - first out, FIFO)的数据结构
* 栈是一种 后进先出(last in - first out, LIFO)的数据结构
*
* 方法一:双栈
* 思路
* 将一个栈当作输入栈,用于压入 push 传入的数据;另一个栈当作输出栈,用于 pop 和 peek 操作。
* 每次 pop 或 peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。
*
*/
class MyQueue {
Deque<Integer> inStack;
Deque<Integer> outStack;
/** Initialize your data structure here. */
public MyQueue() {
inStack = new LinkedList<Integer>();
outStack = new LinkedList<Integer>();
}
/** Push element x to the back of queue. */
public void push(int x) {
inStack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if (outStack.isEmpty()){
in2out();
}
return outStack.pop();
}
/** Get the front element. */
public int peek() {
if (outStack.isEmpty()){
in2out();
}
return outStack.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}
private void in2out(){
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
}
复杂度分析
时间复杂度:push 和 empty 为 O(1),pop 和peek 为均摊 O(1)。对于每个元素,至多入栈和出栈各两次,故均摊复杂度为 O(1)。
空间复杂度:O(n)。其中 n 是操作总数。对于有 n 次 push 操作的情况,队列中会有 n 个元素,故空间复杂度为 O(n)
225-用队列实现栈 Queue offer poll
使用队列实现栈的下列操作:
- push(x) – 元素 x 入栈
- pop() – 移除栈顶元素
- top() – 获取栈顶元素
- empty() – 返回栈是否为空
一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。
/**
* 方法一:两个队列
* queue1用于存储栈内的元素,queue2作为入栈操作的辅助队列。
*
*/
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
/** Initialize your data structure here. */
public MyStack() {
queue1 = new LinkedList<Integer