02. 队列
队列:优雅的数据结构
队列是一种经典的数据结构,它以先进先出(First In First Out,FIFO)的方式管理数据。
在计算机科学中,队列被广泛应用于各种场景,从操作系统任务调度到网络通信,都能看到它的身影。
1. 队列的基本概念
队列是一种线性数据结构,具有两个主要操作:入队(enqueue)和出队(dequeue)。数据项从队尾入队,从队头出队,保证了先进入队列的元素先被取出。这种特性使得队列非常适用于需要按照顺序处理的问题。
2. 队列的实现
2.1 队列的实现思路
队列本身是有序列表,若使用数组结构存储队列数据,则队列数据声明如下图,其中maxSize是该队列的最大容量。

队列的输入、输出分别从前后端来处理,因此需要两个变量front、rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则随着数据输入而改变。
数据存入称为addQueue:
- 队尾指针往后移: rear+1, front == rear时队列为空
- 队尾指针rear小于队列的最大标maxSize-1,则将数据存入rear所指的数组元组中,否则无法存入数据。rear == maxSize-1时,队列满。
2.2 Java实现队列的基本结构
package org.example;  
  
import java.util.Scanner;  
  
public class ArrayQueue {  
    private int maxSize;  
    private int front;  
    private int rear;  
    private int[] arr;//该数组用于存放数据,模拟队列  
  
    //创建队列的构造器  
    public ArrayQueue(int arrMaxSize) {  
        maxSize = arrMaxSize;  
        arr = new int[maxSize];  
        front = -1;//指向队列头部,分析出front是指向队列头的前一个位置  
        rear = -1;//指向队列尾部,指向队列尾的数据(即就是队列最后一个数据)  
    }  
  
    //判断队列是否满  
    public boolean isFull() {  
        return rear == maxSize - 1;  
    }  
  
    //判断队列是否为空  
    public boolean isEmpty() {  
        return rear == front;  
    }  
  
    //添加数据到队列  
    public void addQueue(int n) {  
        //判断队列是否满  
        if (isFull()) {  
            System.out.println("队列满,不能加入数据");  
            return;  
        }  
        rear++;//让rear后移  
        arr[rear] = n;  
    }  
  
    //获取队列的数据,出队列  
    public int getQueue() {  
        //判断队列是否为空  
        if (isEmpty()) {  
            //通过抛出异常  
            throw new RuntimeException("队列空,不能取数据");  
        }  
        front++;//front后移  
        return arr[front];  
    }  
  
    //显示队列的所有数据  
    public void showQueue() {  
        //遍历  
        if (isEmpty()) {  
            System.out.println("队列空的,没有数据");  
            return;  
        }  
        for (int i = 0; i < arr.length; i++) {  
            System.out.printf("arr[%d]=%d\n", i, arr[i]);  
        }  
    }  
  
    //显示队列的头数据,注意不是取出数据  
    public int headQueue() {  
        //判断  
        if (isEmpty()) {  
            throw new RuntimeException("队列空的,没有数据");  
        }  
        return arr[front + 1];  
    }  
  
    public static void main(String[] args) {  
        //测试一把  
        System.out.println("测试数组模拟队列的案例~~~");  
        //创建一个队列  
        ArrayQueue queue = new ArrayQueue(3);  
        char key = ' ';//接收用户输入  
        Scanner scanner = new Scanner(System.in);  
        boolean loop = true;  
        //输出一个菜单  
        while (loop) {  
            System.out.println("s(show):显示队列");  
            System.out.println("e(exit):退出程序");  
            System.out.println("a(add):添加数据到队列");  
            System.out.println("g(get):从队列取出数据");  
            System.out.println("h(head):查看队列头的数据");  
            //接收一个字符  
            key = scanner.next().charAt(0);  
            switch (key) {  
                case 's'://显示队列  
                    queue.showQueue();  
                    break;  
                case 'a'://添加数据到队列  
                    System.out.println("输出一个数");  
                    int value = scanner.nextInt();  
                    queue.addQueue(value);  
                    break;  
                case 'g'://从队列取出数据  
                    try {  
                        int res = queue.getQueue();  
                        System.out.printf("取出的数据是%d\n", res);  
                    } catch (Exception e) {  
                        System.out.println(e.getMessage());  
                    }  
                    break;  
                case 'h'://查看队列头的数据  
                    try {  
                        int res = queue.headQueue();  
                        System.out.printf("队列头的数据是%d\n", res);  
                    } catch (Exception e) {  
                        System.out.println(e.getMessage());  
                    }  
                    break;  
                case 'e'://退出  
                    scanner.close();  
                    loop = false;  
                    break;  
                default:  
                    break;  
            }  
        }  
        System.out.println("程序退出~~~");  
    }  
}
2.3 Python实现队列的基本结构
队列可以使用数组或链表等数据结构来实现。以下是一个简单的队列实现示例(使用Python):
python class Queue:
def __init__(self):
    self.items = []
def is_empty(self):
    return len(self.items) == 0
def enqueue(self, item):
    self.items.append(item)
def dequeue(self):
    if not self.is_empty():
        return self.items.pop(0)
def size(self):
    return len(self.items)
### 2.4示例:使用队列解决问题
#### 场景:打印任务队列
考虑一个打印任务队列的场景。当多个打印任务同时到达,我们希望它们按照先来先服务的原则依次完成打印。使用队列可以轻松实现这一需求。
python
创建一个打印任务队列
print_queue = Queue()
入队打印任务
printqueue.enqueue("Print Document 1") printqueue.enqueue("Print Document 2") print_queue.enqueue("Print Document 3")
出队并执行打印
while not printqueue.isempty():
current_task = print_queue.dequeue()
print("Printing:", current_task)
这个示例展示了队列在处理按顺序到达的任务时的便利性。
## 3. 环形队列
> 循环队列是队列的一种变体,它通过循环使用数组或链表的空间,使得队列可以在队尾和队头之间形成环形结构。在这篇文章中,我们将深入研究循环队列的基本概念、实现方式以及在实际应用中的优势。
### 实现思路:
1. front指向队列的第一个元素,初始值默认为0
2. rear指向队列最后一个元素的后一个位置,初始值为0。空出空间作为约定。
3. 当队列满时,(rear+1)% maxSize == front
4. 当队列空时,rear == front
5. 队列中有效的数据个数,(rear+maxSize-front)% maxSize
### 3.1 循环队列的基本概念
> 与普通队列不同,循环队列在队尾和队头之间建立了连接,形成了一个环。这意味着当队尾指针到达数组的末尾时,它可以循环回到数组的开头,从而更好地利用存储空间。
### 3.2 Java实现环形队列的基本结构
java package org.example;
import java.util.Scanner;
public class CircleArrayQueue {
private int maxSize;  
private int front;  
private int rear;  
private int[] arr;//该数组用于存放数据,模拟队列//创建队列的构造器
public CircleArrayQueue(int arrMaxSize) {  
    maxSize = arrMaxSize;  
    arr = new int[maxSize];  
    front = 0;//指向队列头部,分析出front是指向队列头的前一个位置  
    rear = 0;//指向队列尾部,指向队列尾的数据(即就是队列最后一个数据)  
}//判断队列是否满
public boolean isFull() {  
    return (rear + 1) % maxSize == front;  
}//判断队列是否为空
public boolean isEmpty() {  
    return rear == front;  
}//添加数据到队列
public void addQueue(int n) {  
    //判断队列是否满  
    if (isFull()) {  
        System.out.println("队列满,不能加入数据");  
        return;  
    }  
    arr[rear] = n;  
    rear = (rear + 1) % maxSize;  
}//获取队列的数据,出队列
public int getQueue() {  
    //判断队列是否为空  
    if (isEmpty()) {  
        //通过抛出异常  
        throw new RuntimeException("队列空,不能取数据");  
    }  
    int value = arr[front];  
    front = (front + 1) % maxSize;  
    return value;  
}//显示队列的所有数据
public void showQueue() {  
    //遍历  
    if (isEmpty()) {  
        System.out.println("队列空的,没有数据");  
        return;  
    }  
    for (int i = front; i < front + size(); i++) {  
        System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);  
    }  
}//当前队列的有效数据
public int size() {  
    return (rear + maxSize - front) % maxSize;  
}}
### 3.3 Python实现环形队列的基本结构
python class CircularQueue:
def __init__(self, capacity):
    self.capacity = capacity
    self.queue = [None] * capacity
    self.front = self.rear = -1
def is_empty(self):
    return self.front == -1
def is_full(self):
    return (self.rear + 1) % self.capacity == self.front
def enqueue(self, item):
    if self.is_full():
        print("Queue is full. Cannot enqueue.")
    else:
        if self.is_empty():
            self.front = self.rear = 0
        else:
            self.rear = (self.rear + 1) % self.capacity
        self.queue[self.rear] = item
def dequeue(self):
    if self.is_empty():
        print("Queue is empty. Cannot dequeue.")
    else:
        removed_item = self.queue[self.front]
        if self.front == self.rear:
            self.front = self.rear = -1
        else:
            self.front = (self.front + 1) % self.capacity
        return removed_item
### 3.4 示例:使用循环队列解决问题
#### 场景:循环缓冲区
考虑一个循环缓冲区的场景,其中数据按顺序写入循环队列,当队列满时,新的数据会覆盖最早的数据。
python
创建一个容量为 5 的循环队列
circular_queue = CircularQueue(5)
入队数据
for i in range(1, 8):
circular_queue.enqueue(f"Data {i}")出队并显示队列内容
while not circularqueue.isempty():
print("Dequeue:", circular_queue.dequeue())
### 3.5 循环队列的优势
#### 3.5.1 更高的空间利用率
由于循环队列的环形结构,它能够更好地利用存储空间,减少了不必要的内存浪费。
#### 3.5.2 简化指针操作
循环队列通过使用取余运算简化了指针的操作,使得队头和队尾的指针移动更加高效。
### 3.6 循环队列的应用场景
#### 3.6.1 缓存
循环队列常用于缓存系统中,用于处理数据的轮转和更新。
#### 3.6.2 实时数据处理
在需要按时间顺序处理数据的场景中,循环队列能够提供高效的数据流管理。