当前位置:主页 > 查看内容

一文详解「队列」,手撸队列的3种方法!

发布时间:2021-04-20 00:00| 位朋友查看

简介:本文已收录至我的 Github《算法图解》系列:https://github.com/vipstone/algorithm 前面我们介绍了栈(Stack),队列和栈是比较像的一种数据结构。我们可以想象有很多辆汽车正在通过单行道的隧道,所有车辆不能插队、不能掉头,先进来的车也先出去,我们可以……

 

本文已收录至我的 Github《算法图解》系列:https://github.com/vipstone/algorithm

前面我们介绍了栈(Stack),队列和栈是比较像的一种数据结构。我们可以想象有很多辆汽车正在通过单行道的隧道,所有车辆不能插队、不能掉头,先进来的车也先出去,我们可以把这种特征的数据结构称之为队列。


队列也属于逻辑结构,所谓的物理结构是指可以将数据存储在物理空间中,比如数组和链表都属于物理数据结构;而逻辑结构则是用于描述数据间的逻辑关系的,它可以由多种不同的物理结构来实现,比如队列和栈都属于逻辑结构。

队列特性

队列中的元素必须是先进先出(First In First Out,FIFO)的,它有两个重要的方法:入队(enqueue)和出队(dequeue)。队列的入口端叫队尾(rear),出口端叫队头(front),如下图所示:

手撸队列

学习了队列的基本知识之后,接下来我们将使用代码来实现一个队列。

首先我们先使用数组来实现一个队列,它的结构如下图所示:

1.自定义队列—数组

  1. public class MyQueue<E> { 
  2.  
  3.     private Object[] queue; // 存储容器 
  4.     private int head; // 头部指针 
  5.     private int tail; // 尾部指针 
  6.     private int size; // 队列实际存储长度 
  7.     private int maxSize; // 最大容量 
  8.  
  9.     public MyQueue() { 
  10.         // 无参构造函数,设置默认参数 
  11.         this.maxSize = 10; 
  12.         this.head = 0; 
  13.         this.tail = -1; 
  14.         this.size = 0; 
  15.         this.queue = new Object[this.maxSize]; 
  16.     } 
  17.  
  18.     public MyQueue(int initSize) { 
  19.         // 有参构造函数,设置参数 
  20.         this.maxSize = initSize; 
  21.         this.head = 0; 
  22.         this.tail = -1; 
  23.         this.size = 0; 
  24.         this.queue = new Object[this.maxSize]; 
  25.     } 
  26.  
  27.     /** 
  28.      * 查询队头元素 
  29.      */ 
  30.     public E peek() throws Exception { 
  31.         if (size == 0) { 
  32.             throw new Exception("队列中暂无数据"); 
  33.         } 
  34.         return (E) this.queue[this.head]; 
  35.     } 
  36.  
  37.     /** 
  38.      * 入列 
  39.      */ 
  40.     public boolean offer(E e) throws Exception { 
  41.         if (tail >= (maxSize - 1)) { 
  42.             throw new Exception("添加失败,队列已满"); 
  43.         } 
  44.         this.queue[++tail] = e; 
  45.         size++; 
  46.         return true
  47.     } 
  48.  
  49.     /** 
  50.      * 出列 
  51.      */ 
  52.     public E poll() throws Exception { 
  53.         if (size == 0) { 
  54.             throw new Exception("删除失败,队列为空"); 
  55.         } 
  56.         size--; 
  57.         return (E) this.queue[head++]; 
  58.     } 
  59.  
  60.     /** 
  61.      * 代码测试 
  62.      */ 
  63.     public static void main(String[] args) throws Exception { 
  64.         MyQueue queue = new MyQueue(); 
  65.         queue.offer("Hello"); 
  66.         queue.offer("Java"); 
  67.         System.out.println(queue.peek()); 
  68.         queue.poll(); 
  69.         System.out.println(queue.poll()); 
  70.     } 

以上代码的执行结果如下:

  • Hello
  • Java

2.自定义队列—链表

用链表实现队列的数据结构如下图所示:

实现代码如下:

  1. public class QueueByLinked { 
  2.  
  3.     /** 
  4.      * 声明链表节点 
  5.      */ 
  6.     static class Node<E> { 
  7.         E item; // 当前的值 
  8.  
  9.         Node<E> next; // 下一个节点 
  10.  
  11.         Node(E e) { 
  12.             this.item = e; 
  13.         } 
  14.     } 
  15.  
  16.     private Node firstNode; // 队头元素 
  17.     private Node lastNode; // 队尾元素 
  18.     private int size; // 队列实际存储数量 
  19.     private int maxSize; // 队列最大容量 
  20.  
  21.     public QueueByLinked(int maxSize) { 
  22.         if (maxSize <= 0) throw new RuntimeException("队列最大容量不能为空"); 
  23.         // 默认初始化函数 
  24.         firstNode = lastNode = new Node(null); 
  25.         this.size = 0; 
  26.         this.maxSize = maxSize; 
  27.     } 
  28.  
  29.     /** 
  30.      * 判断队列是否为空 
  31.      */ 
  32.     public boolean isEmpty() { 
  33.         return size == 0; 
  34.     } 
  35.  
  36.     /** 
  37.      * 入列 
  38.      */ 
  39.     public void offer(Object e) { 
  40.         // 最大值效验 
  41.         if (maxSize <= size) throw new RuntimeException("队列已满"); 
  42.         Node node = new Node(e); 
  43.         lastNode = lastNode.next = node; // 设置最后一个节点和倒数第二个节点的 next 
  44.         size++; // 队列数量 +1 
  45.     } 
  46.  
  47.     /** 
  48.      * 出列 
  49.      */ 
  50.     public Node poll() { 
  51.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  52.         size--; // 队列数量 -1 
  53.         return firstNode = firstNode.next; // 设置并返回队头元素(第一个节点是 null,当前元素则为 Node.next) 
  54.     } 
  55.      
  56.     /** 
  57.      * 查询队头元素 
  58.      */ 
  59.     public Node peek() { 
  60.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  61.         return firstNode.next;  // 返回队头元素(第一个节点是 null,当前元素则为 Node.next) 
  62.     } 
  63.  
  64.     /** 
  65.      * 代码测试 
  66.      */ 
  67.     public static void main(String[] args) { 
  68.         QueueByLinked queue = new QueueByLinked(10); 
  69.         queue.offer("Hello"); 
  70.         queue.offer("JDK"); 
  71.         queue.offer("Java"); 
  72.         System.out.println(queue.poll().item); 
  73.         System.out.println(queue.poll().item); 
  74.         System.out.println(queue.poll().item); 
  75.     } 

以上代码的执行结果如下:

  • Hello
  • JDK
  • Java

3.扩展:使用 List 实现自定义队列

除了以上两种方式之外,我们还可以使用 Java 自身的数据结构来实现队列,比如 List,我们这里提供一个实现的思路(但并不建议在实际工作中使用),实现代码如下:

  1. import java.util.ArrayList; 
  2. import java.util.List; 
  3.  
  4. /** 
  5.  * 自定义队列(List方式) 
  6.  */ 
  7. public class QueueByList<E> { 
  8.  
  9.     private List value; // 队列存储容器 
  10.  
  11.     public QueueByList() { 
  12.         // 初始化 
  13.         value = new ArrayList(); 
  14.     } 
  15.  
  16.     /** 
  17.      * 判断队列是否为空 
  18.      */ 
  19.     public boolean isEmpty() { 
  20.         return value.size() == 0; 
  21.     } 
  22.  
  23.     /** 
  24.      * 入列 
  25.      */ 
  26.     public void offer(Object e) { 
  27.         value.add(e); 
  28.     } 
  29.  
  30.     /** 
  31.      * 出列 
  32.      */ 
  33.     public E poll() { 
  34.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  35.         E item = (E) value.get(0); 
  36.         value.remove(0); 
  37.         return item; 
  38.     } 
  39.  
  40.     /** 
  41.      * 查询队头元素 
  42.      */ 
  43.     public E peek() { 
  44.         if (isEmpty()) throw new RuntimeException("队列为空"); 
  45.         return (E) value.get(0); 
  46.     } 
  47.  
  48.     /** 
  49.      * 代码测试 
  50.      */ 
  51.     public static void main(String[] args) { 
  52.         QueueByList queue = new QueueByList(); 
  53.         queue.offer("Hello"); 
  54.         queue.offer("JDK"); 
  55.         queue.offer("Java"); 
  56.         System.out.println(queue.poll()); 
  57.         System.out.println(queue.poll()); 
  58.         System.out.println(queue.poll()); 
  59.     } 

以上代码的执行结果如下:

  • Hello
  • JDK
  • Java

队列使用场景

队列的常见使用场景有:

  • 存储多线程中等待排队执行的任务;
  • 存储多线程公平锁中等待执行任务的线程;
  • 常见消息中间件的任务队列等。

总结

通过以上三种队列的实现方式我们可以看出,任意容器都是可以用来实现队列(Queue)的,只要保证队列的元素先进先出(FIFO),并且在实现类中需要包含队列的四个核心方法:入列、出列、查询队列是否为空、返回队头元素等,就可以称为实现了一个自定义的队列。


本文转载自网络,原文链接:https://mp.weixin.qq.com/s/11UIrorraAUHhK4XeiUNKw
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!

推荐图文

  • 周排行
  • 月排行
  • 总排行

随机推荐