数据结构-队列
一、什么是队列?
队列(Queue)是一种基本的线性数据结构,它遵循先进先出(First In First Out, FIFO
)的原则。这意味着最先被添加到队列中的元素将会是最先被移除的。
如图所示
- 队列的头部,俗称前端或者队头。
- 队列的尾部,俗称后端或者队尾。
- 出队,从队列的前端(front)移除一个元素。
- 入队,在队列的后端(rear)添加一个新元素。
二、队列的实现
队列既可以用数组来实现,也可以用链表来实现。
用数组实现的队列称为顺序队列
用链表实现的队列称为链式队列。
2.1用数组实现
使用数组实现一个简单的队列可以通过以下步骤来完成。该类将包含必要的操作,如入队、出队、查看队列头部元素以及检查队列是否为空。
public class ArrayQueue {
private int[] queue; // 存储队列数据的数组
private int front; // 指向队列头部
private int rear; // 指向队列尾部
private int size; // 当前队列中元素的数量
private int capacity; // 队列的容量
// 构造函数
public ArrayQueue(int capacity) {
this.capacity = capacity;
this.queue = new int[capacity];
this.front = 0;
this.rear = -1;
this.size = 0;
}
// 入队操作
public void enqueue(int item) {
if (isFull()) {
throw new IllegalStateException("队列已满");
}
rear = (rear + 1) % capacity;
queue[rear] = item;
size++;
}
// 出队操作
public int dequeue() {
if (isEmpty()) {
throw new IllegalStateException("队列为空");
}
int item = queue[front];
front = (front + 1) % capacity;
size--;
return item;
}
// 查看队列头部元素
public int peek() {
if (isEmpty()) {
throw new IllegalStateException("队列为空");
}
return queue[front];
}
// 检查队列是否为空
public boolean isEmpty() {
return size == 0;
}
// 检查队列是否已满
public boolean isFull() {
return size == capacity;
}
// 获取队列中的元素数量
public int getSize() {
return size;
}
// 打印队列中的所有元素
public void display() {
if (isEmpty()) {
System.out.println("队列为空");
return;
}
System.out.print("队列中的元素: ");
for (int i = 0; i < size; i++) {
System.out.print(queue[(front + i) % capacity] + " ");
}
System.out.println();
}
}
2.2用链表实现
使用链表实现队列是一种很好的选择,特别是当你需要动态调整队列大小时。以下是一个简单的链表实现队列的Java示例:
public class LinkedListQueue<T> {
private Node<T> front;
private Node<T> rear;
private int size;
// 内部节点类
private static class Node<T> {
T data;
Node<T> next;
Node(T data) {
this.data = data;
this.next = null;
}
}
public LinkedListQueue() {
this.front = null;
this.rear = null;
this.size = 0;
}
// 入队操作
public void enqueue(T item) {
Node<T> newNode = new Node<>(item);
if (isEmpty()) {
front = rear = newNode;
} else {
rear.next = newNode;
rear = newNode;
}
size++;
}
// 出队操作
public T dequeue() {
if (isEmpty()) {
throw new IllegalStateException("Queue is empty");
}
T item = front.data;
front = front.next;
if (front == null) {
rear = null;
}
size--;
return item;
}
// 获取队首元素
public T peek() {
if (isEmpty()) {
throw new IllegalStateException("Queue is empty");
}
return front.data;
}
// 检查队列是否为空
public boolean isEmpty() {
return front == null;
}
// 获取队列大小
public int getSize() {
return size;
}
// 打印队列内容
public void display() {
if (isEmpty()) {
System.out.println("Queue is empty");
return;
}
Node<T> current = front;
while (current != null) {
System.out.print(current.data + " ");
current = current.next;
}
System.out.println();
}
}
二者的区别
**队列数据结构使用数组和链表的主要区别在于存储方式、操作效率、空间复杂度和时间复杂度。**
- 存储方式:
- 数组实现队列时,元素在内存中是连续存储的,通过索引可以直接访问元素。这种存储方式使得数组实现的队列在访问元素时具有较高的效率,但需要一块连续的内存空间,且大小在声明时确定,不能动态扩展12。
- 链表实现队列时,由若干个节点组成,每个节点包含数据和指向下一个节点的指针,节点在内存中可以不连续。这种存储方式使得链表实现的队列在插入和删除操作时具有较高的效率,因为只需要修改指针的指向,而不需要移动其他元素的位置12。
- 操作效率:
- 数组实现的队列在访问元素时具有较高的效率,因为可以直接通过索引访问任何位置的元素,时间复杂度为O(1)12。但是,数组在插入和删除元素时,尤其是从头部或中间位置插入和删除,效率较低,因为可能需要移动其他元素来腾出空间或填补空缺,时间复杂度为O(n)12。
- 链表实现的队列在插入和删除操作时具有较高的效率,因为只需要修改指针的指向,时间复杂度为O(1)12。但是,链表在访问特定位置的元素时效率较低,需要从头节点开始遍历链表直到找到目标节点,时间复杂度为O(n)12。
- 空间复杂度和时间复杂度:
- 数组实现的队列在空间上相对节省,因为数组的存储空间是预先分配的,且可以复用已分配的空间(如果队列中的元素被删除,其占用的空间不会立即被释放,而是等待被后续的元素覆盖)12。但是,数组的大小在声明时确定,不能动态扩展12。
- 链表实现的队列在空间上较为灵活,可以根据需要动态添加或删除节点。链表的每个节点可以单独分配和释放内存,因此链表的大小可以动态变化12。但是,链表需要额外的空间来存储指针,因此在存储相同数量的数据时,链表使用的空间通常比数组大12。
综上所述,数组和链表实现队列各有优势和劣势。数组实现的队列在访问元素时具有较高的效率,而链表实现的队列在插入和删除操作时具有较高的效率。选择哪种实现方式取决于具体的应用场景和对性能的具体要求。