什么是队列?
队列(Queue)是一种基于元素的集合,特点是只允许在列表的一端(称为队尾rear)进行插入,而在列表的另一端(称为队头front)进行删除。因为队列遵循先进先出(FIFO,First In First Out)的原则,所以也被称为先进先出的线性表。
队列的基本操作
- enqueue:将元素添加到队尾,也就是加入队列末尾。
- dequeue:将元素从队头移除,只能从队头移除元素,实现先进先出(FIFO)。
- peek:查看队列队头的元素,不对队列产生影响。
- size:查看队列当前包含的元素数量。
- isEmpty:判断队列是否为空
- isFull:检查队列是否已满。
- clear:清除队列中的所有元素。
数组模拟队列
在数组的队列中,我们通常使用两个指针来跟踪和更新队列的状态。一个是头部指针,指向队列中的第一个元素;另一个是尾部指针,指向队列中的最后一个有效元素的下一个位置。当我们添加一个新元素时,我们更新尾部指针。当我们移除一个元素时,我们更新头部指针。
/**
* @Description: 基于数组的循环队列
* 为了解决假溢出问题,把顺序表转为首尾相连的循环队列
* 1.少用一个存储单元
* 判空:front == rear
* 判满:front == (rear+1)%maxSize
* 2.设置一个标志变量,入列flag=1,出列flag=0
* 判空:front==rear && flag==0
* 判满:front==rear && flag==1
* 3.设置一个计时器,入列num++,出列num--
* 判空:num==0
* 判满:num>0 && front==rear
*/
public class SqQueue<E> {
private static final int DEFAULT_CAPACITY = 10;
private E[] queue;
private int front;
private int rear;
public SqQueue(E[] queue) {
this.queue = queue;
this.front = 0;
this.rear = queue.length-1;
}
public SqQueue(int length) {
this.queue = (E[]) new Object[length];
this.front = 0;
this.rear = 0;
}
public SqQueue() {
this.queue = (E[]) new Object[DEFAULT_CAPACITY];
this.front = 0;
this.rear = 0;
}
public void offer(E e) {
if(isFull()){
System.out.println("队满");
return;
}
queue[rear] = e;
rear = (rear+1)%queue.length;
}
public E poll() {
if(isEmpty()){
System.out.println("队空");
return null;
}
E e = queue[front];
queue[front] = null;
front = (front+1)%queue.length;
return e;
}
public E getFront() {
if(isEmpty()){
System.out.println("队空");
return null;
}
return queue[front];
}
public E getReal() {
if(isEmpty()){
System.out.println("队空");
return null;
}
return queue[(rear-1+queue.length)%queue.length];
}
/*
* 因为rear-front有可能为负数,如果为负数,则其绝对值表示的是空的存储空间的个数(出现假溢出了),
* 加上queue.length,则就能求有多少元素.如果为正数,则其值直接就是队列中存储元素的个数
*/
public int getSize() {
return (rear-front+queue.length)%queue.length;
}
public boolean isEmpty() {
return front==rear;
}
public boolean isFull(){
return front==(rear+1)%queue.length;
}
public void display(){
if(isEmpty()){
System.out.println("队空");
return;
}
System.out.print("队列的物理结构:{");
for (E e : queue) {
System.out.print(e+" ");
}
System.out.print("}\n队列的逻辑结构:{");
for (int i = front; i!=rear ; i=(i+1)%queue.length) {
System.out.print(queue[i]+" ");
}
System.out.println("}");
}
}
链表模拟队列
链表由一系列节点组成,每个节点包含两个部分:元素和指向下一个节点的链接。在链表的队列中,我们同样保持一个头部和一个尾部链接。当我们添加一个新元素时,我们更新尾部链接。当我们移除一个元素时,我们更新头部链接。
public class Node<E> {
public E data;
public Node next;
public Node(E data) {
this.data = data;
this.next = null;
}
public Node() {
this.data = null;
this.next = null;
}
}
public class LinkListQueue<E> {
Node front;
Node rear;
private int size;
public LinkListQueue() {
}
public LinkListQueue(E[] arr) {
for (E e : arr) {
this.offer(e);
}
}
public LinkListQueue(Node front) {
this.front = front;
this.rear = front;
this.size = 0;
}
public void offer(E e) {
Node p = new Node(e);
if(isEmpty()){
front = p;
rear = p;
size++;
return;
}
rear.next = p;
rear = p;
size++;
}
public E poll() {
if(isEmpty()){
return null;
}
E e = (E) front.data;
front = front.next;
size--;
return e;
}
public E getFront() {
if (isEmpty()){
return null;
}
return (E) front.data;
}
public E getReal() {
if (isEmpty()){
return null;
}
return (E) rear.data;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size==0;
}
public void display() {
Node e = front;
System.out.print("队列的结构为:{");
if(e.next==null){
System.out.println(e.data+"}");
return;
}
while (e.next!=null){
System.out.print(e.data+"-->");
e = e.next;
}
System.out.println(e.data+"}");
}
}
队列测试代码
public class test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String[] q = {"新","年","快","乐","这","是","一","个","队","列"};
// SqQueue<String> queue = new SqQueue<String>(q);
// LinkListQueue<String> queue = new LinkListQueue<String>(new Node<String>());
LinkListQueue<String> queue = new LinkListQueue<String>(q);
queue.display();
boolean flag = true;
String c = null;
String e = null;
while (flag){
System.out.print("请输入要进行的操作:");
c = sc.next();
switch (c){
case "offer":
System.out.println("请输入要插入的元素");
e = sc.next();
queue.offer(e);
break;
case "poll":
e = queue.poll();
if(e!=null){
System.out.println("出队元素为:"+e);
}
break;
case "getFront":
e = queue.getFront();
if(e!=null) {
System.out.println("栈头元素为:" + e);
}
break;
case "getRear":
e = queue.getReal();
if(e!=null) {
System.out.println("栈尾元素为:" + e);
}
break;
case "display":
queue.display();
break;
default:
flag = false;
break;
}
}
}
}
Java封装好的队列
Java提供了java.util.Queue接口,该接口在Java集合框架中表示一个队列。Queue接口定义了在队列两端插入和删除元素的方法。它的实现类如LinkedList。LinkedList是一个以双向链表实现的List,在实现时提供了队列的特性。
public class QueueTest {
public static void main(String[] args) {
// 创建队列实例
Queue<Integer> queue = new LinkedList<>();
// 判断队列是否为空
System.out.println("队列是否为空:" + queue.isEmpty());
// 入队操作
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
// 获取队头元素
System.out.println("队头元素:" + queue.peek());
// 出队操作
System.out.println("出队元素:" + queue.remove());
// 判断队列是否为空
System.out.println("队列是否为空:" + queue.isEmpty());
}
}
队列实际应用
- 计算机系统中的任务调度:计算机系统常常需要管理大量的执行任务,队列是实现任务管理的一种重要方式。例如,CPU的任务调度,打印机的打印任务等。
- 网络中的数据传输:在网络中,数据包的发送和接收都需要队列进行处理。发送数据包时,数据包会被放入队列,等待网络可用时发送;接收数据包时,数据包会被放入队列,等待应用程序处理。
- 在线程之间进行同步:在多线程编程中,队列常常用于实现线程之间的同步,也就是一个线程生成的数据,另一个线程消费的数据。
队列的这些应用都充分体现了其“先进先出”的特性,能够保证元素处理的顺序性和公平性。