队列(Queue)

队列(Queue)数据结构

队列是一种先进先出(FIFO, First In First Out)的线性数据结构,类似于现实生活中的排队场景,先到的人先获得服务。

核心特性

  1. FIFO原则:最先入队的元素最先出队
  2. 受限访问
    • 只能从队尾(rear)插入元素(入队,enqueue)
    • 只能从队首(front)删除元素(出队,dequeue)
  3. 基本操作
    • enqueue:在队尾添加元素
    • dequeue:移除并返回队首元素
    • peek/front:查看队首元素但不移除
    • isEmpty:检查队列是否为空
    • size:获取队列中元素数量

代码实现

1. 基于数组的实现

public class SimpleArrayQueue {
    private final int[] queue;
    private final int front; // 队首指针
    private int rear;  // 队尾指针
    private int size;  // 当前元素数量
    private final int capacity; // 队列容量

    public SimpleArrayQueue(int capacity) {
        this.capacity = capacity;
        this.queue = new int[capacity];
        this.front = 0;
        this.rear = -1;
        this.size = 0;
    }

    // 入队操作,元素放在队尾(所以队尾下标+1)
    public void enqueue(int item) {
        if (isFull()) {
            throw new RuntimeException("Queue is full");
        }
        rear++;
        queue[rear] = item;
        size++;
    }

    // 出队操作,元素顶部弹出(所有元素向前移动,队尾下标要-1)
    public int dequeue() {
        if (isEmpty()) {
            throw new RuntimeException("Queue is empty");
        }
        int item = queue[front];
        // 所有元素向前移动一位
        for (int i = 0; i < size - 1; i++) {
            queue[i] = queue[i + 1];
        }
        rear--;
        size--;
        return item;
    }

    // 查看队首元素(直接访问队首元素)
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("Queue is empty");
        }
        return queue[front];
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public boolean isFull() {
        return size == capacity;
    }

    public int size() {
        return size;
    }



    public static void main(String[] args) {
        SimpleArrayQueue queue = new SimpleArrayQueue(5);

        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);

        System.out.println(queue.dequeue()); // 输出: 10

        queue.enqueue(40);
        queue.enqueue(50);

        System.out.println(queue.peek()); // 输出: 20
        System.out.println(queue.size()); // 输出: 3
    }
}

2. 基于链表的实现

public class LinkedListQueue {
    // 定义链表节点
    private static class Node {
        int data;
        Node next;

        Node(int data) {
            this.data = data;
            this.next = null;
        }
    }

    private Node front; // 队首节点
    private Node rear;  // 队尾节点
    private int size;   // 队列大小

    // 构造函数(初始队首队尾都是空,size 也是 0)
    public LinkedListQueue() {
        front = null;
        rear = null;
        size = 0;
    }

    // 入队操作(链表如果为空:队首队尾都是新元素;链表不为空:队尾是新元素,原队尾的下一个元素是新的队尾元素)
    public void enqueue(int item) {
        Node newNode = new Node(item);
        if (isEmpty()) {
            front = newNode;
        } else {
            rear.next = newNode;
        }
        rear = newNode;
        size++;
    }

    // 出队操作(队首的下一个元素作为新的队首)
    public int dequeue() {
        if (isEmpty()) {
            throw new RuntimeException("Queue is empty");
        }
        int item = front.data;
        front = front.next;
        if (front == null) {
            rear = null; // 如果队列已空,重置rear
        }
        size--;
        return item;
    }

    // 查看队首元素
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("Queue is empty");
        }
        return front.data;
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return front == null;
    }

    // 获取队列大小
    public int size() {
        return size;
    }

    public static void main(String[] args) {
        LinkedListQueue queue = new LinkedListQueue();

        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);
        System.out.println("Dequeued: " + queue.dequeue()); // 输出: Dequeued: 10。弹出队首

        System.out.println("Front element: " + queue.peek()); // 输出: Front element: 20。查看队首
        System.out.println("Queue size: " + queue.size()); // 输出: Queue size: 2。队列数量
    }
}

时间复杂度分析

数组实现

操作 时间复杂度 说明
enqueue() O(1) 在rear位置插入元素
dequeue() O(n) 需要移动所有剩余元素前移
peek() O(1) 访问front位置的元素
isEmpty() O(1) 检查front和rear位置
size() O(1) rear - front + 1

链表实现

操作 时间复杂度 说明
enqueue() O(1) 直接在rear节点后添加新节点
dequeue() O(1) 直接移除front节点
peek() O(1) 访问front节点的数据
isEmpty() O(1) 检查front是否为null
size() O(1) 如果维护size变量

JDK 中的队列

  1. 双端队列(Deque):两端都可插入删除。分成两组,每一组一端进一端出

    ArrayDeque 数组实现,线程不安全(既具有队列的特性也具有栈的特性)
    LinkedList 链表实现,线程不安全
    LinkedBlockingDeque 链表实现,线程安全

  2. 优先队列(Priority Queue):按优先级出队。树实现(平衡树或二叉堆),入队要维护树,出队也要维护树,时间复杂度是 O(log n)。
    因为是树,已经有顺序了,出队时能比较高效的找出哪个元素要优先出队

    PriorityQueue 小顶堆实现(根节点最小,根节点就是最优先的),线程不安全
    PriorityBlockingQueue 小顶堆实现(根节点最小,根节点就是最优先的),线程安全

  3. 循环队列:JDK 中没有已实现好循环队列

  4. 阻塞队列:加锁实现。一把锁方案:出队和入队使用同一把锁;两把锁方案:出队和入队使用不同的锁

    ArrayBlockingQueue 单锁,数组实现,线程安全
    LinkedBlockingQueue 双锁,链表实现,线程安全

队列与栈的对比

特性 队列(Queue) 栈(Stack)
原则 FIFO (先进先出) LIFO (后进先出)
插入位置 队尾(rear) 栈顶(top)
删除位置 队首(front) 栈顶(top)
典型应用 BFS、任务调度 DFS、函数调用栈

队列是计算机科学中最重要的基础数据结构之一,广泛应用于各种需要公平性、顺序处理的场景。

posted @ 2025-04-26 17:35  CyrusHuang  阅读(67)  评论(0)    收藏  举报