当前位置:   article > 正文

数据结构———队列

数据结构———队列

目录

1.基本概念

2.队列常用操作

3.队列的实现

3.1数组实现

3.2链表实现

队列的典型应用


1.基本概念

定义:
队列是一种特殊的线性表,它只允许在一端进行插入操作,在另一端进行删除操作。
插入的一端称为队尾(Rear),删除的一端称为队头(Front)。
操作:
入队(Enqueue):在队尾添加一个新元素。
出队(Dequeue):从队头移除一个元素。
查看队头元素(Front):获取队头元素而不移除它。
查看队尾元素(Rear):获取队尾元素而不移除它。
判空(IsEmpty):检查队列是否为空。
长度(Size):返回队列中的元素数量。
特性:
先进先出(FIFO):队列遵循先进先出的原则,即最先加入队列的元素将是最先被移除的。
顺序存储:队列可以使用数组或链表实现,其中数组实现通常固定大小,而链表实现可以动态调整大小。

2.队列常用操作

队列的常见操作如表 5-2 所示。需要注意的是,不同编程语言的方法名称可能会有所不同。我们在此采用与栈相同的方法命名。

表 5-2   队列操作效率

方法名描述时间复杂度
push()元素入队,即将元素添加至队尾O(1)
pop()队首元素出队O(1)
peek()访问队首元素O(1)

我们可以直接使用编程语言中现成的队列类:

  1. /* 初始化队列 */
  2. Queue<Integer> queue = new LinkedList<>();
  3. /* 元素入队 */
  4. queue.offer(1);
  5. queue.offer(3);
  6. queue.offer(2);
  7. queue.offer(5);
  8. queue.offer(4);
  9. /* 访问队首元素 */
  10. int peek = queue.peek();
  11. /* 元素出队 */
  12. int pop = queue.poll();
  13. /* 获取队列的长度 */
  14. int size = queue.size();
  15. /* 判断队列是否为空 */
  16. boolean isEmpty = queue.isEmpty();

3.队列的实现

3.1数组实现

数组实现的队列通常有一个固定的大小,需要额外处理队满的情况。(将数组替换成动态数组)

在数组中删除首元素的时间复杂度为 O(n) ,这会导致出队操作效率较低。然而,我们可以采用以下巧妙方法来避免这个问题。

我们可以使用一个变量 front 指向队首元素的索引,并维护一个变量 size 用于记录队列长度。定义 rear = front + size ,这个公式计算出的 rear 指向队尾元素之后的下一个位置。

基于此设计,数组中包含元素的有效区间为 [front, rear - 1],各种操作的实现方法下所示。

  • 入队操作:将输入元素赋值给 rear 索引处,并将 size 增加 1 。
  • 出队操作:只需将 front 增加 1 ,并将 size 减少 1 。

可以看到,入队和出队操作都只需进行一次操作,时间复杂度均为 O(1)。

  1. public class ArrayQueue {
  2. private int front;
  3. private int rear;
  4. private int[] queue;
  5. private final int capacity;
  6. public ArrayQueue(int size) {
  7. this.capacity = size;
  8. this.queue = new int[size];
  9. this.front = -1;
  10. this.rear = -1;
  11. }
  12. // 入队
  13. public boolean enqueue(int item) {
  14. if (isFull()) {
  15. return false;
  16. }
  17. if (isEmpty()) {
  18. front = 0;
  19. }
  20. rear++;
  21. if (rear == capacity) {
  22. rear = 0;
  23. }
  24. queue[rear] = item;
  25. return true;
  26. }
  27. // 出队
  28. public int dequeue() {
  29. if (isEmpty()) {
  30. throw new IllegalStateException("Queue is empty");
  31. }
  32. int item = queue[front];
  33. if (front == rear) {
  34. front = -1;
  35. rear = -1;
  36. } else {
  37. front++;
  38. if (front == capacity) {
  39. front = 0;
  40. }
  41. }
  42. return item;
  43. }
  44. // 查看队头元素
  45. public int peek() {
  46. if (isEmpty()) {
  47. throw new IllegalStateException("Queue is empty");
  48. }
  49. return queue[front];
  50. }
  51. // 判空
  52. public boolean isEmpty() {
  53. return front == -1;
  54. }
  55. // 判满
  56. public boolean isFull() {
  57. return (rear + 1) % capacity == front;
  58. }
  59. }

3.2链表实现

链表实现:使用单链表或双链表来存储队列中的元素,同样通过两个指针来跟踪队头和队尾。

链表实现的队列可以动态扩展,不需要担心队满的问题。

  1. public class LinkedListQueue<T> {
  2. private Node<T> front;
  3. private Node<T> rear;
  4. private int size;
  5. private static class Node<T> {
  6. T data;
  7. Node<T> next;
  8. Node(T data) {
  9. this.data = data;
  10. }
  11. }
  12. public LinkedListQueue() {
  13. front = null;
  14. rear = null;
  15. size = 0;
  16. }
  17. // 入队
  18. public void enqueue(T item) {
  19. Node<T> newNode = new Node<>(item);
  20. if (isEmpty()) {
  21. front = newNode;
  22. } else {
  23. rear.next = newNode;
  24. }
  25. rear = newNode;
  26. size++;
  27. }
  28. // 出队
  29. public T dequeue() {
  30. if (isEmpty()) {
  31. throw new IllegalStateException("Queue is empty");
  32. }
  33. T item = front.data;
  34. front = front.next;
  35. if (front == null) {
  36. rear = null;
  37. }
  38. size--;
  39. return item;
  40. }
  41. // 查看队头元素
  42. public T peek() {
  43. if (isEmpty()) {
  44. throw new IllegalStateException("Queue is empty");
  45. }
  46. return front.data;
  47. }
  48. // 判空
  49. public boolean isEmpty() {
  50. return front == null;
  51. }
  52. // 获取队列大小
  53. public int size() {
  54. return size;
  55. }
  56. }

队列的典型应用

  1. 异步处理:当一个系统需要执行一些耗时的任务时,可以将这些任务放入队列中,然后由后台进程或线程池来处理这些任务。这种方式可以避免阻塞主线程,提高系统的响应速度。例如,用户注册后发送欢迎邮件或短信,这些操作可以放入队列中异步处理。
  2. 应用解耦:在分布式系统中,不同的服务之间通过消息队列进行通信,可以降低服务间的依赖关系,提高系统的可维护性和可扩展性。比如,订单服务和库存服务之间可以通过消息队列传递订单状态更新的消息。
  3. 流量削锋:当系统在短时间内接收大量请求时,可以使用队列来缓存这些请求,避免直接将压力传递给后端服务,从而保护后端服务免受瞬时高并发的影响。例如,网站在促销活动期间可能会出现大量的访问请求,可以使用队列来平滑这些请求。
  4. 消息通讯:在多线程或多进程环境中,队列可以作为线程间或进程间通信的工具,确保消息按照一定的顺序被处理。比如,生产者-消费者模型中,生产者将数据放入队列,消费者从队列中取出数据进行处理。
  5. 任务调度:在操作系统中,队列用于管理等待执行的任务或进程,确保它们按照一定的顺序被执行。例如,打印队列管理待打印的文档。
  6. 银行排队系统:在银行等服务场所,顾客按照到达的顺序排队等候服务,这可以模拟成队列的行为。每当有一个服务窗口可用时,队列中的下一个顾客就会被服务。
  7. 缓冲区管理:在网络传输或文件系统中,队列可以用来管理缓冲区中的数据块,确保数据按顺序处理。
  8. 模拟和游戏开发:在模拟程序和游戏中,队列可以用来管理事件发生的顺序,确保事件按照预定的时间顺序发生。
  9. 编译器和解析器:编译器和解析器中,队列可以用来暂存字符或符号,以便按顺序处理。
  10. 资源管理:在多用户系统中,队列可以用来管理对共享资源的访问,比如打印机、磁盘等。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/937899
推荐阅读
相关标签
  

闽ICP备14008679号