学习目标:
掌握队列和栈
学习内容:
- 队列
- 栈
队列
队列是一种先进先出(FIFO)的数据结构,常用于任务调度、广度优先搜索等。Java提供了Queue接口,以及LinkedList和ArrayDeque等实现。
常用队列包括:
- Queue(接口):提供add/offer,element/peak,remove/poll等方法。
- Deque(接口):双向队列。队列两端的元素既能入队(offer)也能出队(poll),如果将Deque限制为只能从一端入队和出队,则可实现栈的数据结构。如果固定一端进另一端出的话,那么它就相当于一个普通的队列。
- LinkedList:链表结构,实现了List,Deque,Cloneable,Serializable等接口。
- ArrayDeque:数组结构,实现了Deque,Cloneable,Serializable等接口。
- PriorityQueue:优先队列,优先队列的作用是保证每次取出的元素都是队列中权重最小的。
双端队列
Deque是一个双端队列接口,继承自Queue接口,Deque的实现类是LinkedList、ArrayDeque、LinkedBlockingDeque,其中LinkedList是最常用的。
-
普通队列(一端进另一端出):
Queue queue = new LinkedList()或Deque deque = new LinkedList() -
双端队列(两端都可进出)
Deque deque = new LinkedList() -
堆栈
Deque deque = new LinkedList()
第一个元素 (头部) | 最后一个元素 (尾部) | |||
---|---|---|---|---|
抛出异常 | 特殊值 | 抛出异常 | 特殊值 | |
插入 | addFirst(e) | offerFirst(e) | addLast(e) | offerLast(e) |
删除 | removeFirst() | pollFirst() | removeLast() | pollLast() |
检查 | getFirst() | peekFirst() | getLast() | peekLast() |
例:利用双端队列Z字层序遍历二叉树
public static List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if (root == null) {
return ret;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
int c1 = 1;//当前层节点数
boolean odd = true;
while (!queue.isEmpty()) {
int c2 = 0;//下一层数量
LinkedList<Integer> level = new LinkedList<>();
for (int i = 0; i < c1; i++) {
TreeNode n = queue.poll();
if(odd){
level.offerLast(n.val);
}else {
level.offerFirst(n.val);
}
if (n.left != null) {
queue.offer(n.left);
c2++;
}
if (n.right != null) {
queue.offer(n.right);
c2++;
}
}
odd = !odd;
ret.add(level);
c1 = c2;
}
return ret;
}
优先级队列
Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,这里主要使用PriorityQueue,PriorityQueue的底层是堆。优先级队列(PriorityQueue)提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新对象。
- 使用时必须导入 PriorityQueue 所在的包 -> import java.util.PriorityQueue
- PriorityQueue中放置的元素必须要能够比较大小
- 不能插入 null 对象,否则会抛出 NullPointerException 异常
- 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
- 插入和删除元素的时间复杂度均为 O(log2N)
创建优先级队列
public static void main(String[] args) {
//默认底层容量是11
PriorityQueue<Integer> queue1 = new PriorityQueue<>();
//initialCapacity是50
PriorityQueue<Integer> queue2 = new PriorityQueue<>(50);
//用集合创建一个优先级队列对象
ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(0);
list.add(2);
list.add(3);
//默认是小顶堆
PriorityQueue<Integer> queue3 = new PriorityQueue<>(list);
System.out.println(queue3.peek());
System.out.println(queue3.size());
}
可以改成大顶堆
PriorityQueue<Integer> queue3 = new PriorityQueue<>((a,b)->Integer.Compare(b,a));
queue3.addAll(list);
阻塞队列
阻塞队列(BlockingQueue) 是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
- ArrayBlockingQueue : 一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue : 一个由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue : 一个支持优先级排序的无界阻塞队列。
- DelayQueue: 一个使用优先级队列实现的无界阻塞队列。
- SynchronousQueue: 一个不存储元素的阻塞队列。
- LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。
堆栈
堆栈是一种后进先出(LIFO)的数据结构,常用于实现撤销操作、表达式求值等。Java堆栈Stack类已经过时,Java官方推荐使用Deque替代Stack使用。Deque堆栈操作方法:push()、pop()、peek()。
例1:判断有效括号
public static boolean isValid(String s) {
LinkedList<Character> stack = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(') stack.push(')');
else if (c == '[') stack.push(']');
else if (c == '{') stack.push('}');
else if (c == ')' || c == ']' || c == '}') {
if (!stack.isEmpty() && c == stack.peek()) {
stack.pop();
} else return false;
}
}
return stack.isEmpty();
}
例2:逆波兰表达式
public static int evalRPN(String[] tokens) {
LinkedList<Character> stack = new LinkedList<>();
for (String token : tokens) {
switch (token) {
case "+" -> {
Integer b = stack.pop();
Integer a = stack.pop();
stack.push(a + b);
}
case "-" -> {
Integer b = stack.pop();
Integer a = stack.pop();
stack.push(a - b);
}
case "*" -> {
Integer b = stack.pop();
Integer a = stack.pop();
stack.push(a * b);
}
case "/" -> {
Integer b = stack.pop();
Integer a = stack.pop();
stack.push(a / b);
}
default -> {
stack.push(Integer.parseInt(token));
}
}
}
return stack.pop();
}
3.中缀转后缀
思路:
1.遇到非运算符,直接拼串
2.遇到±*/ 则判断优先级是否比栈顶运算符高:
- 如果比栈顶高:入栈
- 如果没有栈顶高:把栈里优先级>=它的全出栈,再入栈
3.遍历完成,栈里剩余运算符依次出栈
4.带()
- 遇到 ( 直接入栈,优先级为0
- 遇到 ) 把到 ( 为止的所有运算符出栈
public static int priority(char c) {
return switch (c) {
case '*', '/' -> 2;
case '+', '-' -> 1;
case '(' ->0;
default -> throw new IllegalArgumentException("不合法运算符" + c);
};
}
public static String infixToSuffix(String exp) {
LinkedList<Character> stack = new LinkedList<>();
StringBuilder sb = new StringBuilder(exp.length());
for (int i = 0; i < exp.length(); i++) {
char c = exp.charAt(i);
switch (c) {
case '+', '-', '*', '/' -> {
if (stack.isEmpty()) {
stack.push(c);
} else {
if (priority(c) > priority(stack.peek())) {
stack.push(c);
} else {
while (!stack.isEmpty() && priority(stack.peek()) >= priority(c)) {
sb.append(stack.pop());
}
stack.push(c);
}
}
}
case '(' ->{
stack.push(c);
}
case ')'->{
while(!stack.isEmpty()&&stack.peek()!='('){
sb.append(stack.pop());
}
if(!stack.isEmpty()){
stack.pop();
}
}
default -> {
sb.append(c);
}
}
}
while (!stack.isEmpty()){
sb.append(stack.pop());
}
return sb.toString();
}
4.用两个栈模拟队列
//用栈实现队列
public class myQueue{
LinkedList<Character> s1 = new LinkedList<>();
LinkedList<Character> s2 = new LinkedList<>();
public void push(int x){
s2.push(x);
}
public int pop(){
if (!s1.isEmpty()) {
while(!s2.isEmpty()){
s1.push(s2.pop());
}
}
return s1.pop();
}
public int peek(){
if (!s1.isEmpty()) {
while(!s2.isEmpty()){
s1.push(s2.pop());
}
}
return s1.peek();
}
public boolean empty(){
return s1.isEmpty()&&s2.isEmpty();
}
}
5.用队列模拟栈
public static class MyStack{
Queue<Integer> queue = new LinkedList<>();
public void push(int x ){
queue.offer(x);
int len = queue.size();
for (int i = 0; i < len-1; i++) {
queue.offer(queue.poll());
}
}
public int pop(){
if(!queue.isEmpty())
return queue.poll();
return -1;
}
public int top(){
return queue.peek();
}
public boolean empty(){
return queue.isEmpty();
}
}
参考文章:
https://blog.csdn.net/devnn/article/details/82716447
https://blog.csdn.net/BillieFan/article/details/107578057