超全面Java中的队列(Queue)

超全面Java中的队列(Queue)

目录

引言:敲门砖​编辑

认识Queue基础

Queue定义

Queue 使用

这就是循环队列 ⬆️

Queue 基本方法

模拟实现队列

一:by 数组

二:by 单链表

实现循环队列 :

双端队列

创建

方法

引言:敲门砖

(👈图片from豆包) 排队在日常生活中难以避免,排队不插队成了当今社会的公序良俗,干饭人干饭魂,谁第一个冲到食堂,站在打饭窗口,谁就第一个能拿到饭后继的人排在其后,排成一列(先到先吃)。那在计算机中的队列呢?

认识Queue基础

from JDK 17 Documentation

Queue定义

队列(Queue)也是一个进行了进一步的封装和限制的线性表,提供了入队列、出队列、取队首元素。在 Java 中,Queue 是一个接口,它继承自 Collection 接口,通常遵循 FIFO(先进先出) 原则。

队列这样的数据结构,常用于辅助“耗时较长的”的操作、无法一次处理完的操作,而这就需要按照顺序,一个一个的进行处理,这时就可以将待处理的数据放到队列中,确保处理顺序不乱,先进的先处理。

Queue 使用

由于Queue 是一个接口,不能创建实例只能通过创建其他类去实现该接口,并实现抽象方法。

Queue queue = new LinkedList<>();

// 之所以 new LinkedList 是因为其实现了Queue 接口

Queue queue = new ArrayDeque<>();

// 基于数组 更高效、元素个数上限可控 内存使用率更高

如上图 基于数组,初始时,head 和 tail 都指向 0 位置,此时认为是一个空队列。 随着元素入队[head,tail) 前闭后开,tail后移;元素出队,head后移 ,head++ 后其之前指向的元素就被视为无效了,逻辑删除了。

数组是有固定容量的,当tail 走到数组末尾是否意味着队列就满了呢?

给上图变形一下

首尾相接,当head 和 tail 重合时,队列就满了 。 但上面说过 初始时 head , tail 重合,队列为空 ❓那怎么区分 满 or 空

方案一:

牺牲一个格子,当 tail 走到 head 前面一个位置时就视作队列为满; 方案二:

引入变量 size 当 size == 数组长度时即为满;size == 0 空 入队 size++

出队 size--

这就是循环队列 ⬆️

在用代码实现循环队列前先认识一下队列的基本方法:

Queue 基本方法

方法类别方法名功能描述队列空时队列满时入队列add(E e)把元素添加到队列尾部抛出异常抛出异常offer(E e)尝试将元素添加到队列尾部抛出异常返回 false出队列remove()移除队首元素抛出异常抛出异常poll()移除队首元素返回 null返回 null查看队首元素element()查看队首,不取出抛出异常抛出异常peek()查看队首,不取出返回 null返回 null

黄色的方法更常用

还有 size() 获取队中元素个数 isEmpty() 判空

模拟实现队列

一:by 数组

package queue;

import java.util.Scanner;

public class MyQueueByArray {

private String[] arr = null;

private int head = 0;

private int tail = 0;

private int size = 0;

public MyQueueByArray(){

arr = new String[1000];

}

public MyQueueByArray(int capacity){

arr = new String[capacity];

}

public void offer(String val){

// 如果队列满了

if(size == arr.length){

// 可抛异常,可扩容(不建议)

return;

}

// 把新的元素放到 tail 的位置上

arr[tail] = val;

// 更新 tail 的指向

tail++;

if(tail == arr.length){

tail = 0;

}

// 更新 tail 的指向 的另一种写法

// tail = (tail + 1) % arr.length;

// 更新元素个数

size++;

}

public String poll(){

if(size == 0){

return null;

}

// 取出队首元素,保存起来,以便返回

String val = arr[head];

head++;

if(head == arr.length){

head = 0;

}

size--;

return val;

}

public String peek(){

if(size == 0){

return null;

}

return arr[head];

}

public boolean isEmpty(){

return size == 0;

}

public int size(){

return size;

}

}

二:by 单链表

package queue;

import java.util.ArrayDeque;

import java.util.Queue;

import java.util.Scanner;

// 基于单链表实现队列

public class MyQueue {

// 链表的一个节点

static class Node{

public String val;

public Node next;

public Node(String val){

this.val = val;

this.next = null;

}

}

// 把队列的头部和尾部都记录下来

// 基于链表实现队列:

// 这个做法 链表的头/尾与队列的头/尾是一致的

// 1. 入队 -> 尾插

// 2. 出队 -> 头删

// 也可以

// 1. 入队 -> 头插

// 2. 出队 -> 尾删

//

private Node head = null;

private Node tail = null;

// 入队

public void offer(String val){

// 链表尾插

Node newNode = new Node(val);

// 链表为空

if(head == null){

head = newNode;

tail = newNode;

return;

}

// 非空

tail.next = newNode;

tail = newNode;

}

// 出队列

public String poll(){

// 头删

if(head == null){

return null;

}

// 保存头部节点的值

// 接下来把这个节点删除掉,返回这个值

String val = head.val;

// 更新 head 的指向

head = head.next;

// 如果链表的节点数超出 1 个 ,删掉一个元素,不影响 tail 的指向

// 但是,链表的节点数只有一个,删掉这个元素,tail 就应该指向 null

if(head == null){

tail = null;

}

return val;

}

// 取队首元素

public String peek(){

if(head == null){

return null;

}

return head.val;

}

}

实现循环队列 :

package queue;

import java.util.Scanner;

public class MyCirecleQueue {

// 创建一个数组

private int[] arr;

private int elem = 0;

private int head = 0;

private int tail = 0;

private int size = 0;

// 构造方法

public MyCirecleQueue(int cap){

this.arr = new int[cap];

}

// 判空

public boolean isEmpty() {

return size == 0;

}

// 判满

public boolean isFull() {

return size == arr.length;

}

public int size() {

return size;

}

// 入队

public boolean enqueue(int elem){

if(isFull()){

return false;

}

arr[tail] = elem;

tail = (tail + 1) % arr.length; // 利用取模运算实现循环

size++;

return true;

}

// 出队

public Integer dequeue() {

if (isEmpty()) return null;

int value = arr[head];

head = (head + 1) % arr.length;

size--;

return value;

}

// 查看队首元素

public Integer peek() {

if (isEmpty()) return null;

return arr[head];

}

}

双端队列

Deque: 两端都可以进⾏⼊队和出队操作的队列。上图

Deque 就不用遵守先进先出的规则了 ,更加灵活。同样其也是一个接口不能创建实例。

创建

Deque deque = new ArrayDeque<>();

Deque deque = new LinkedList<>();

方法

addFirst() // 从队首入队列

addLast() // 从队尾入队列

removeFirst() // 从队首出队列

removeLast() // 从队尾出队列

addFirst() + removeFirst() 相当于栈

addLast() + removeLast() 相当于队列

队列练习

1.用队列实现栈

leetcode 225 用队列实现栈

思路:1.准备A,B 两个队列 2.入栈:往 A 中 入队列 3.出栈:先把A中的所有元素循环出队列,并入队列到 B 中,当A中仅剩最后一个元素时,这个元素就是栈顶元素,删除掉。 完成一次出栈,元素都到 B 中了,方便后续入栈,交换A,B 指向 4.取栈顶:把A中元素往B中放,仅剩最后一个元素时返回其值。

A 为主 B 为辅 辅助删除元素

class MyStack {

private Queue A = new LinkedList<>();

private Queue B = new LinkedList<>();

public MyStack() {

//上面初始化过了,空着即可

}

// 交换 A B 指向

public void swap(){

Queue temp = new LinkedList<>();

temp = A;

A = B;

B = temp;

}

public void push(int x) {

A.offer(x);

}

public int pop() {

while(A.size() > 1){

B.offer(A.poll());

}

int result = A.poll();

swap();

return result;

}

public int top() {

while(A.size() > 1){

B.offer(A.poll());

}

int result = A.poll();

B.offer(result);

swap();

return result;

}

public boolean empty() {

return A.isEmpty();

}

}

2.用栈实现队列

思路:准备两个栈 A专门负责入队列 B专门负责出队列 入队列,把所有B中的元素出栈 入栈到A 出队列,把所有A中的元素出站,入站到B,从B中出战 取栈顶元素,把A的元素全部转移到B中,取B的栈顶。 判定为空,确保两个站都不空,此时整体就是非空

class MyQueue {

private Stack A = new Stack<>(); // 用于入队操作的栈A

private Stack B = new Stack<>(); // 用于出队操作的栈B

public MyQueue() {

}

public void push(int x) {

A.push(x);

}

public int pop() {

if (B.empty()) {

while (!A.empty()) {

B.push(A.pop());

}

}

return B.pop();

}

public int peek() {

if (B.empty()) {

while (!A.empty()) {

B.push(A.pop());

}

}

return B.peek();

}

public boolean empty() {

return A.empty() && B.empty();

}

}

头脑🌪️ 上述两问是否能用双端队列解决呢 ?

人生偶尔会走上一条陌路 像是没有指标的地图 别让她们说你该知足 只有你知道什么是你的幸福 ———— 二十二 DT 👍⭐️🦀🦀

🎊 相关推荐

苹果手机如何实现全屏显示?详细教程与技巧分享!
绍的意思
365体育外围

绍的意思

📅 10-06 👀 6416
dnf阿修罗暗影9攻略 暗影9瞎子厉害吗怎么玩
365体育外围

dnf阿修罗暗影9攻略 暗影9瞎子厉害吗怎么玩

📅 08-01 👀 5965