参考资料:[Day10] 栈与队列理论基础、leetcode 232. 用栈实现队列 225. 用队列实现栈 · 语雀
Stack类表示对象的后进先出(LIFO)堆栈。它使用五个操作扩展了Vector类,允许将vector视为stack。提供了通常的push和pop操作,以及一种查看堆栈顶部项的方法,一种测试堆栈是否为空的方法,以及一个在堆栈中搜索项并发现它离顶部有多远的方法。
首次创建堆栈时,它不包含任何项,即空栈。
Deque接口及其实现提供了一组更完整、更一致的后进先出堆栈操作,应该优先使用该类。例如:Deque<Integer>stack=new ArrayDeque
push(E item)
将元素添加到栈的顶部,源码调用的是Vector的addElemnt方法,将添加的元素放在Vector末尾。注意调用push时,会将添加的元素返回
peek()
查看此堆栈顶部的对象,而不将其从堆栈中移除。返回值为该堆栈顶部的对象(the last item of the Vector object,Vector对象的最后一项)。如果栈为空,则抛出异常EmptyStack Exception
pop()
移除此堆栈顶部的对象,并将该对象作为此函数的值返回(the last item of the Vector object,Vector对象的最后一项)。如果栈为空,则抛出异常EmptyStackException
可以看到pop()是基于peek和removeElementAt实现的
empty()
测试此堆栈是否为空。如果为空返回true,否则返回false
search(Object o)
返回对象在此堆栈上的基于1的位置,如果对象o是该stack中的一项,则此方法返回距离最接近栈顶部的对象的堆栈顶部的距离,堆栈上最顶端的项被认为距离为1。如果对象o不在该stack则返回-1
Queue接口与List、Set同一级别,都是继承了Collection接口。
-------| Collection 单例集合的根接口。
------------| List 如果是实现了List接口的集合类,具备的特点: 有序,可重复。
------------| Set 如果是实现了Set接口的集合类,具备特点: 无序,不可重复。
------------| Queue 如果是实现了Queue接口的集合类,具备特点: 模拟队列存储结构,有序,先入先出。
队列是一种数据结构,它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素。
Queue是java中实现队列的接口,它总共只有6个方法,我们一般只用其中3个就可以了。
插入操作的后一种形式(返回特殊值)是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。
Queue的6个方法分类:
压入元素(添加):add()、offer()
相同:未超出容量,从队尾压入元素,返回压入的那个元素。
区别:在超出容量(队列满)时,add()方法会抛出异常,offer()返回false
弹出元素(删除):remove()、poll()
相同:容量大于0的时候,删除并返回队头被删除的那个元素。
区别:在容量为0(空队列)的时候,remove()会抛出异常,poll()返回false
获取队头元素(不删除):element()、peek()
相同:容量大于0的时候,都返回队头元素,但是不删除。
区别:容量为0(空队列)的时候,element()会抛出异常,peek()返回null
三个子接口:BlockingDeque 、 BlockingQueue 、Deque
与 List 接口不同,此接口不支持通过索引访问元素
阻塞队列:
1、理解
阻塞队列是一个支持两个附加操作的队列,即在队列为满时,存储元素的线程会等待队列可用,当队列为空时,获取元素的线程会等待队列为非空。
2、Java中的阻塞队列
阻塞队列接口有BlockingQueue和BlockingDeque两个,其中后者是双向队列。
双端队列(Deque):一个线性 collection,支持在两端插入和移除元素。此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列
此接口定义 在双端队列两端访问元素的方法,提供插入、移除和检查元素的方法。
Deque接口扩展了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素,从 Queue 接口继承的方法完全等效于 Deque 方法。
双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类(Deque<Integer>stack=new ArrayDeque
注意,在将双端队列用作队列或堆栈时,peek 方法同样正常工作,即无论哪种情况下,都从双端队列的开头抽取元素。
Deque 实现通常不定义基于元素的 equals 和 hashCode 方法,而是从 Object 类继承基于身份的 equals 和 hashCode 方法。
Deque方法罗列:
- add(E e) 添加元素至队列的尾部
- addFirst(E e) 将指定元素插入此双端队列的开头
- addLast(E e) 将指定元素插入此双端队列的末尾
- contains(Object o) 如果此双端队列包含指定元素,则返回 true
- descendingIterator() 返回以逆向顺序在此双端队列的元素上进行迭代的迭代器
- element() 获取,但不移除此双端队列所表示的队列的头部
- getFirst() 获取,但不移除此双端队列的第一个元素。
- getLast() 获取,但不移除此双端队列的最后一个元素。
- iterator() 返回以恰当顺序在此双端队列的元素上进行迭代的迭代器。
- offer(E e) 将指定元素插入此双端队列所表示的队列
- peek() 获取,但不移除此双端队列所表示的队列的头部
- poll() 获取并移除此双端队列所表示的队列的头部
- pop() 从此双端队列所表示的堆栈中弹出一个元素
- push(E e) 将一个元素推入此双端队列所表示的堆栈
- remove() 获取并移除此双端队列所表示的队列的头部
- remove(Object o) 从此双端队列中移除第一次出现的指定元素
- removeFirst() 获取并移除此双端队列第一个元素
- removeLast() 获取并移除此双端队列的最后一个元素
- size() 返回此双端队列的元素数
Queue的实现类有 LinkedList 和 PriorityQueue 。最常用的实现类是 LinkedList 。
LinkedList类是List接口的实现类,但是同时也是 Deque 接口实现类,所以 LinkedList 既可以当做 双端队列 来使用,也可以当做 栈 来使用
LinkedList 是采用 链表 的形式实现的,因此LinkedList 比较适合用于经常插入和删除的操作(链表增删操作为O(1)),而以数组实现的更加适合于随机访问。
题目:力扣
使用两个栈来实现队列:
队列是先进先出,而栈是先进后出。
队列和栈不一样的地方主要是出口 - 问题在于如何实现删除队列元素?
那么很巧妙的一个思路就是:
两个栈,一个栈储存刚刚进入队列的元素 - stack1;另一个栈储存准备出队列的元素 - stack2。
在删除队列元素以及查找队头的方法中:
先判断准备出队列的栈stack2中是否有元素;若是没有元素,说明需要将入队列的栈stack1中的元素转存过来。
此处操作为 - 准备出队列的栈stack2一个一个添加入队列的栈stack1中的元素
会发现此时出队列的栈stack2中元素顺序和队列的顺序是一致的;
因此,若是stack2中是有元素,那就可以直接把栈顶的元素移除。
代码实现:
class MyQueue {
LinkedList addlist;
LinkedList removelist;
//两个栈
//一个栈用来储存存入的元素
//一个栈用来储存准备删除的元素
public MyQueue() {
addlist = new LinkedList<>();
removelist = new LinkedList<>();
}
public void push(int x) {
addlist.push(x);
}
public int pop() {
if(removelist.isEmpty()){
while(!addlist.isEmpty()){
removelist.push(addlist.poll());
}
}
return removelist.poll();
}
public int peek() {
if(removelist.isEmpty()){
while(!addlist.isEmpty()){
removelist.push(addlist.poll());
}
}
return removelist.peek();
}
public boolean empty() {
return addlist.size() == 0 && removelist.size() == 0;
}
}
参考资料:代码随想录
题目:力扣
同样采用两个队列来实现。但是这里考虑一个作为输出和一个作为输入队列是不可行的,因为把输入队列中的元素放入到输入队列,元素删除的顺序仍然是先入先出。
一种很巧妙的思想是,把其中一个队列作为辅助队列。
在添加元素的时候:
先将元素放入辅助队列,然后将主队列中的元素一个一个弹出并添加到辅助队列
此时辅助队列中元素删除的顺序和栈中的顺序是相同的。
class MyStack {
LinkedList main;
LinkedList assistant;
public MyStack() {
main = new LinkedList<>();
assistant = new LinkedList<>();
}
public void push(int x) {
assistant.offer(x);
while(!main.isEmpty()){
assistant.offer(main.poll());
}
LinkedList temp = main;
main = assistant;
assistant = temp;
}
public int pop() {
return main.poll();
}
public int top() {
return main.peek();
}
public boolean empty() {
return main.size() == 0;
}
}
参考资料:代码随想录
题目:力扣
括号总共有三种可能,有效的括号是有了左括号就有右括号,且是对称的。有序的
因此可以考虑用栈来储存即将出现的右括号。
遇到左括号则将对称的右括号放入栈。
如果栈顶和右括号相等则抛出。
最后判断栈是否为空,如果是空的说明右括号都存在,那么是有效的。
public boolean isValid(String s) {
LinkedList stack = new LinkedList<>();
for(int i=0; i
参考资料:代码随想录
这里仍然可以利用栈结构。
遍历一遍字符串:
判断元素与栈顶元素是否相同:若相同则删除栈顶元素;若不相同则放入栈。
那么在输出剩下元素的时候需要注意,此时栈中弹出元素顺序刚好和原本字符串中的元素顺序是相反的,因此需要反转。
class Solution {
public String removeDuplicates(String s) {
LinkedList stack = new LinkedList<>();
for(int i=0; i
参考资料:代码随想录
题目:力扣
仔细研究下后缀表达式,可以发现和上一题消除相邻元素的思路非常像;可以同一个stack来储存将要进行运算的数字;在遇到运算符之后把栈顶处前两个元素取出根据运算符:
第二次取出的数 (运算符) 第一次取出的数
然后把结果重新放回栈顶。
代码实现:注意 - 和 / 这两个符号中元素的运算顺序
public int evalRPN(String[] tokens) {
LinkedList stack = new LinkedList<>();
for(int i=0; i
参考资料:代码随想录