目录
队列的概念
队列的使用
队列的具体方法
编辑队列的实现(双向链表实现)
双向链表的定义
入队方法
出队方法
获取队头元素
获取有效元素个数
队列是否为空
用单链表实现队列(补充)
循环队列
定义
循环队列的实现
循环队列的代码实现
定义
入队操作
出队操作
获取队首元素
获取队尾元素
队列是否为空
队列是否为满
双端队列
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
在Java中,Queue是个接口,底层是通过链表实现的。
public static class ListNode{
int val; // 值域
ListNode prev;// 前驱
ListNode next;// 后继
public ListNode(int val) {
this.val = val;
}
}
public ListNode head; // 头节点记录
public ListNode tail; // 尾节点记录
public int size; // 链表的数量(队列大小)
public void offer(int e){
ListNode node = new ListNode(e);
//说明此时的链表为空,所以头节点和尾节点同时指向node即可
if(head == null){
head = node;
}else {
//不是空链表,操作尾节点进行入队操作
tail.next = node;
node.prev = tail;
}
tail = node;
//队列大小加一
size++;
}
public int poll(){
//首先判断队列是不是为空
if(isEmpty()){
throw new RuntimeException("队列为空");
}
//记录头节点下一个值
int headValue = head.val;
// 头节点往后走一步
head = head.next;
// 后面再无节点
if(head == null){
tail = null;
}else {
// 删除头节点操作
head.prev.next = null;
head.prev = null;
}
//队列大小减一
size--;
// 返回出队的值
return headValue;
}
public int peek(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
//直接返回头节点的值即可
return head.val;
}
public int size(){
return size;
}
private boolean isEmpty() {
return size == 0;
}
到此队列用双向链表实现完成,完整代码如下:
/**
* 功能描述
* 双向链表实现队列
* @author wileda
* @date 2022/10/18 21:22
*/
public class MyQueue {
public static class ListNode{
int val;
ListNode prev;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode tail;
public int size;
public void offer(int e){
ListNode node = new ListNode(e);
if(head == null){
head = node;
}else {
tail.next = node;
node.prev = tail;
}
tail = node;
size++;
}
public int poll(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
int headValue = head.val;
head = head.next;
if(head == null){
tail = null;
}else {
head.prev.next = null;
head.prev = null;
}
size--;
return headValue;
}
public int peek(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
return head.val;
}
public int size(){
return size;
}
private boolean isEmpty() {
return size == 0;
}
public void display(){
StringBuilder sb = new StringBuilder();
if (head == null){
sb.append("[]");
}
ListNode current = head;
sb.append("[");
while(current != null){
sb.append(current.val);
if(current.next != null){
sb.append(",");
}
current = current.next;
}
sb.append("]");
System.out.println(sb);
}
}
/**
* 功能描述
* 单向链表实现队列
* @author wileda
* @date 2022/10/18 21:59
*/
public class MyQueue1 {
public static class ListNode{
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
int size;
ListNode first;
ListNode last;
public void offer(int e){
ListNode node = new ListNode(e);
if (first == null){
first = node;
}else {
last.next = node;
node.next = null;
}
last = node;
size++;
}
public int poll(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
int firstValue = first.val;
if(first == null){
last = null;
}else {
first = first.next;
}
size--;
return firstValue;
}
public int peek(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
return first.val;
}
public int size(){
return size;
}
private boolean isEmpty() {
return size == 0;
}
public void display(){
StringBuilder sb = new StringBuilder();
if (first == null){
sb.append("[]");
}
ListNode current = first;
sb.append("[");
while(current != null){
sb.append(current.val);
if(current.next != null){
sb.append(",");
}
current = current.next;
}
sb.append("]");
System.out.println(sb);
}
}
将队列臆造成一个环状的空间,即把存储队列元素的表从按逻辑上视为一个环,称为循环队列。循环队列在底层还是是由一个数组实现的。指定一个数组大小,首尾相接之后就呈现为循环队列,当队列为满时不能再入队,同样当队列为空时不能再出队。
队列为空时,front ,rear指向同一位置 入队时front ,rear所指的位置
所以我们在循环队列入队操作时,在rear位置添加元素,然后rear++;
那么我们如何判断队列空或者队列已满呢?
满队列 空队列
1)可以记录一个属性size,来判断队列的空与满,当front == rear,size == 0时,证明队列为空,
front == rear,size == n 时,队列已满。
2)加一个冗余位置,当rear + 1 = front时,则可以判定队列已满
那么我们怎么让最后一个下标 + 1,回到数组的第一个位置?
改变下标位置,用一个公式来处理(index + x)% array.length = 正确下标 ,用以上公式,我们就可以很好确定插入位置的下表,也便于我们后续操作。
private int[] elementData;
// 队首的下标
private int front;
// 队尾的下标
private int rear;
public MyCircularQueue(int k) {
// 用冗余最后一个数组空间的方式去处理,这时要让k + 1,真实存储数据的大小不变
elementData = new int[k + 1];
}
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
// 开始处理数组元素与下标
elementData[rear] = value;
// 新的rear位置
rear = (rear + 1 ) % elementData.length;
return true;
}
public boolean deQueue() {
if(isEmpty()) {
return false;
}
front = (front + 1) % elementData.length;
return true;
}
public int Front() {
if (isEmpty()) {
return 0;
}
return elementData[front];
}
队尾位置rear特殊,若rear此时不是0下表,我们可以简单获取队尾元素,rear - 1即可,但是若rear此时位于0号下标,那我们想要直接获取位置就会出现数组越界错误,因此我们获取队尾元素操作为 (rear + array.length - 1)% array.length,
public int Rear() {
if(isEmpty()) {
return 0;
}
return elementData[(rear - 1 + elementData.length) % elementData.length];
}
public boolean isEmpty() {
return front == rear;
}
public boolean isFull() {
return (rear + 1) % elementData.length == front;
}
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Deque是一个接口,使用时必须创建LinkedList的对象。