【数据结构和算法9】自定义实现队列结构

文章介绍了队列的基本概念,并展示了如何在Java中自定义队列接口。接着,它详细探讨了两种实现队列的方法:一是基于带有哨兵的单向循环链表,二是基于环形数组。这两种实现都包括了添加、移除、检查队头元素、判断队列是否为空或已满以及获取元素数量等基本操作。
摘要由CSDN通过智能技术生成

目录

1、队列的介绍

2、自定义队列接口

3、基于单项循环链表(带哨兵)实现队列结构

4、基于环形数组实现队列结构


1、队列的介绍

计算机科学中,queue 是以顺序的方式维护的一组数据集合,在一端添加数据,从另一端移除数据。习惯来说,添加的一端称为,移除的一端称为,就如同生活中的排队买商品。

在 JDK 中,自带的 队列接口为 java.util.Queue 如下。

在 队列中常用的方法有 offer、peek 等等。

 

2、自定义队列接口

在自定义队列接口中,定义了对队列操作的常见的几个抽象方法,如 offer(向队尾添加一个元素)、poll(移除并返回队列头部第一个元素)等等。

在这里使用了泛型 <E> ,可以向队列中操作不同类型的数据。

 /**
  * 队列基础接口
  *
  * @author zjj_admin
  */
 public interface Queue<E> {
 ​
 ​
     /**
      * 向队列的尾部插入数据
      *
      * @param value 添加数据
      * @return 是否添加成功
      */
     boolean offer(E value);
 ​
     /**
      * 从队列的头部拉取一个数据,并从队列中移除
      *
      * @return 返回头部数据,没有数据时返回 null
      */
     E poll();
 ​
     /**
      * 获取队列头部数据,不移除数据
      *
      * @return
      */
     E peek();
 ​
     /**
      * 判断队列是否为空
      *
      * @return 队列是否为空
      */
     boolean isEmpty();
 ​
     /**
      * 队列是否已经满了
      *
      * @return 队列是否已满
      */
     boolean isFull();
 ​
     /**
      * 获取队列中元素的个数
      *
      * @return 元素个数
      */
     int size();
 ​
 }
 ​

3、基于单项循环链表(带哨兵)实现队列结构

 

实现步骤:

  1. 基于链表实现,定义链表的基础结构 Node ;

  2. 声明链表的头(head)和尾(tail),在这里,head 为链表的哨兵节点,不存储数据;tail 节点为尾结点,用于存储链表的最后一个节点数据,tail.next = head;

  3. 声明队列的元素个数(size)和队列的容量(capacity);

  4. 实现队列中声明的每一个抽象方法即可;

  5. 让自定义队列实现 Iterable 接口,使其可以使用增强 for 循环遍历。

具体的代码实现

 /**
  * 使用单向环形链表(带哨兵)的数结构实现队列
  *
  * @author zjj_admin
  */
 public class LinkedListQueue<E> implements Queue<E>, Iterable<E> {
 ​
 ​
     /**
      * 队列中链表的数据结构
      *
      * @param <E>
      */
     private static class Node<E> {
         /**
          * 当前节点数据
          */
         E value;
         /**
          * 下一个节点
          */
         Node<E> next;
 ​
         public Node(E value, Node<E> next) {
             this.value = value;
             this.next = next;
         }
     }
 ​
     /**
      * 头结点,也是哨兵节点,不存储真实数据
      */
     private Node<E> head = new Node<>(null, null);
 ​
     /**
      * 队尾,存储真实节点数据
      */
     private Node<E> tail = head;
 ​
     /**
      * 队列的数据个数
      */
     private int size;
 ​
     /**
      * 队列的容量大小
      */
     private int capacity = Integer.MAX_VALUE;
 ​
     public LinkedListQueue(int capacity) {
         //调用无参构造,必须放在第一行
         this();
         if (capacity < 0) {
             throw new IndexOutOfBoundsException("capacity 不能小于 0");
         }
         this.capacity = capacity;
     }
 ​
     /**
      * 构造方法
      */
     public LinkedListQueue() {
         head.next = tail;
         tail.next = head;
         this.size = 0;
     }
 ​
 ​
     /**
      * 添加步骤
      * 1、创建一个新节点,让新节点的 next 指针指向头结点
      * 2、让 tail 的 next 指针指向 新添加的节点
      * 3、让 tail 指向 新添加的节点即可。
      *
      * @param value 添加数据
      * @return
      */
     @Override
     public boolean offer(E value) {
         if (isFull()) {
             return false;
         }
         Node<E> added = new Node<>(value, head);
         tail.next = added;
         tail = added;
         size++;
         return true;
     }
 ​
 ​
     @Override
     public E poll() {
         if (isEmpty()) {
             return null;
         }
         Node<E> first = head.next;
         //移除 first
         head.next = first.next;
         //当 first 是尾结点时,让新的 tail 指向 head
         if (first == tail) {
             tail = head;
         }
         size--;
         return first.value;
     }
 ​
     @Override
     public E peek() {
         if (isEmpty()) {
             return null;
         }
         return head.next.value;
     }
 ​
     @Override
     public boolean isEmpty() {
         return head == tail;
     }
 ​
     @Override
     public boolean isFull() {
         return size == capacity;
     }
 ​
 ​
     @Override
     public int size() {
         return size;
     }
 ​
     @Override
     public Iterator<E> iterator() {
         return new Iterator<E>() {
             Node<E> p = head.next;
 ​
             @Override
             public boolean hasNext() {
                 return p != head;
             }
 ​
             @Override
             public E next() {
                 E value = p.value;
                 p = p.next;
                 return value;
             }
         };
     }
 }
 ​

4、基于环形数组实现队列结构

使用环形数组的好处

  1. 对比普通数组,起点和终点更为自由,不用考虑数据移动

  2. “环”意味着不会存在【越界】问题

  3. 数组性能更佳

  4. 环形数组比较适合实现有界队列、RingBuffer 等

 

具体的代码实现

 /**
  * 基于环形数组实现队列
  * 环形数组有 head 和 tail ,head 表示第一个真实数据,tail 不存储数据,表示队列的尾部
  * 使用环形数组的好处是,任何一盒位置都可以作为新的 head 和 tail
  *
  * @author zjj_admin
  */
 public class RingArrayQueue1<E> implements Queue<E>, Iterable<E> {
 ​
     /**
      * 队列元素
      */
     private final E[] array;
     /**
      * 头结点的索引
      */
     private int head;
     /**
      * 尾结点的索引
      */
     private int tail;
 ​
     /**
      * 队列元素个数
      */
     private int size;
 ​
     /**
      * 队列的容量,最大值为 1000
      */
     private int capacity = 10000;
 ​
 ​
     {
         // 代码块,初始化数据
         head = 0;
         tail = 0;
         size = 0;
     }
 ​
     @SuppressWarnings("all")
     public RingArrayQueue1(int capacity) {
         if (capacity < 0 || capacity > 10000) {
             throw new IndexOutOfBoundsException("capacity 不符合规范");
         }
         this.capacity = capacity;
         //初始化数组,多余的一个用于存放 tail 指针
         array = (E[]) new Object[capacity + 1];
     }
 ​
     @SuppressWarnings("all")
     public RingArrayQueue1() {
         array = (E[]) new Object[capacity + 1];
     }
 ​
     /**
      * 添加新节点,就将当前的 tail 位置存储数据,并且将 tail 向后移动一位
      *
      * @param value 添加数据
      * @return
      */
     @Override
     public boolean offer(E value) {
         if (isFull()) {
             return false;
         }
         array[tail] = value;
         // 将尾结点向前移动一位
         tail = (tail + 1) % array.length;
         size++;
         return true;
     }
 ​
     /**
      * 移除第一个节点,获取当前 head 节点的数据,并将 head 节点向后移动一位
      *
      * @return
      */
     @Override
     public E poll() {
         if (isEmpty()) {
             return null;
         }
         E value = array[head];
         // 将头结点向前移动一位
         head = (head + 1) % array.length;
         size--;
         return value;
     }
 ​
     @Override
     public E peek() {
         if (isEmpty()) {
             return null;
         }
         return array[head];
     }
 ​
     @Override
     public boolean isEmpty() {
         return size == 0;
     }
 ​
     /**
      * 这里当队列满时
      *
      * @return
      */
     @Override
     public boolean isFull() {
         return size == array.length - 1;
     }
 ​
     @Override
     public int size() {
         return size;
     }
 ​
     @Override
     public Iterator<E> iterator() {
         return new Iterator<E>() {
             int index = head;
             int count = 0;
 ​
             @Override
             public boolean hasNext() {
                 return count < size;
             }
 ​
             @Override
             public E next() {
                 E value = array[index];
                 index = (index + 1) % array.length;
                 count++;
                 return value;
             }
         };
     }
 }
 ​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值