队列的定义与实现

简介: 前面一章讲了栈的定义与实现,我们一样可以通过限制线性表的一些基本操作来实现另一种常用的数据结构---队列,这一章简单来讲讲队列的定义与实现。


一、定义



队列是限定只能在表的一端进行插入,在表的另一端进行删除的线性表,进行插入操作的一端称为队尾(rear),进行删除的一端为队头(front),插入数据元素的操作叫做入队,删除数据元素的操作叫做出队,它具有先进先出(First In First Out,FIFO)的特性。

微信图片15.png

二、队列的基本操作分析



前面章节说到,线性表可以通过顺序存储和链式存储来实现,作为操作受限的线性表也一样,所以后面也会通过两种存储结构来分析队列的基本操作实现。

在顺序存储结构中,一般会使用数组来实现,定义一个队列,约定使用 front 来存放队头元素的位置,使用 rear 来存放队尾的位置,当 front=rear=-1 时表示队列为空,入队和出队都是通过移动 rearfront 指针来实现,如下图所示,当 a9 入队后不能再做入队操作,而实际上空间并没有占满,此时会出现假溢出现象,为了避免这种情况,我们可以把该连续空间视为“循环顺序队列”。

微信图片17.png在循环顺序队列中,当 a9 入队后再做入队操作时,会将数据元素存储在数组前面出队后的空闲空间中,这样数据元素在数组中就会形成一个循环的队列,在这种情况下,当队满或者空队时都会存在 front=rear ,所以要通过 size 来判断队列是空还是满,当 size=array.length 时队满,当 size=0 时队空。使用循环队列时,队头和队尾的位置变换实现如下:

 
         

微信图片14.png在链式存储结构中,可以直接使用单向链表来实现队列,此时,我们把链表的头部作为队头,把链表的尾部作为队尾,也就是说,在链表尾部插入,链表头部删除,在代码实现中,需要定义两个指针 frontrear 分别指向链表的第1个结点和最后1个结点。

微信图片13.png

1. 入队


在顺序存储中进行入队操作,只需要通过 rear 来定位队尾位置,然后做入队操作,执行完后 rear 移到新的队尾位置。链式存储中,需要将新的结点添加到链表尾部,首先把 a4 赋值给 rear.next,然后新结点 a4 成为新的尾部结点。

微信图片12.png

2. 出队


顺序存储中进行出队操作,通过 front 定位并获取队头元素,然后做出队操作,执行完后 front 移动到新的队头位置。链式存储中,做出队操作时,获取到头部结点元素 a1 后,然后直接把 front.next 赋值给 front 来实现队头元素的删除。

微信图片11.png下面新建一个队列常规操作的接口,然后我们分别使用顺序结构(数组)和链式结构来实现它,读者可以分别对两种存储结构实现的接口的时间复杂度进行分析。

public interface Queue<T> {
    /**
     * 入队
     */
    boolean add(T t);
    /**
     * 出队
     */
    T remove();
    /**
     * 判断队列是否为空
     */
    boolean isEmpty();
    /**
     * 打印队列所有数据
     */
    void print();
}


三、队列的基本操作实现



和线性表实现一样,顺序结构使用数组来存储数据,链式结构使用结点来存储数据。


1. 顺序存储实现

public class ArrayQueue<T> implements Queue<T> {
    private int front = 0;
    private int rear = 0;
    private int size = 0;
    private Object[] elementData;
    public ArrayQueue(int size) {
        this.elementData = new Object[size];
    }
    @Override
    public boolean add(T t) {
        if (front == rear && size == elementData.length) {
            throw new ArrayIndexOutOfBoundsException();
        }
        elementData[rear] = t;
        rear = (rear + 1) % elementData.length;
        size++;
        return true;
    }
    @SuppressWarnings("unchecked")
    @Override
    public T remove() {
        if (front == rear && size == 0) {
            throw new NullPointerException();
        }
        Object t = elementData[front];
        elementData[front] = null;
        front = (front + 1) % elementData.length;
        size--;
        return (T) t;
    }
    @Override
    public boolean isEmpty() {
        return size == 0;
    }
    @Override
    public void print() {
        System.out.println(Arrays.toString(elementData));
    }
}


2. 链式存储实现

public class LinkedQueue<T> implements Queue<T> {
    private Node front;
    private Node rear;
    private int size = 0;
    private class Node {
        private T t;
        private Node next;
        public Node() {
        }
        public Node(T t, Node next) {
            this.t = t;
            this.next = next;
        }
    }
    @Override
    public boolean add(T t) {
        Node n = new Node(t, null);
        if (rear != null) {
            rear.next = n;
        }
        if (front == null) {
            front = n;
        }
        rear = n;
        size++;
        return false;
    }
    @Override
    public T remove() {
        if (front == null) {
            throw new NullPointerException();
        }
        T t = front.t;
        front = front.next;
        size--;
        return t;
    }
    @Override
    public boolean isEmpty() {
        return size == 0;
    }
    @Override
    public void print() {
        Node n = front;
        while (n != null) {
            System.out.print(n.t + ",");
            n = n.next;
        }
        System.out.println();
    }
}


目录
相关文章
|
7月前
|
消息中间件 监控 Go
合并队列的例子
【5月更文挑战第14天】文中探讨了如何跨线程或机器合并两个有序任务队列, 利用队列有序性优化合并效率。任务队列用于工作单元调度,通过消息代理在客户端和工作进程间通信,实现高可用和可扩展系统。队列功能包括监控、调度、工作流程、资源保护、时间和速率限制以及组件定制。合并操作的时间复杂度在最好情况下为O(N),最坏情况为O(N²),其中N为较短队列的长度。
245 0
合并队列的例子
|
6月前
|
存储 算法
数据结构和算法学习记录——特殊线性表之队列-队列的概念、队列结构体类型定义 、基本接口函数、初始化函数、销毁队列函数、入队列函数、判断队列是否为空、出队列函数、读取队头队尾的数据 、计算队列数据个数
数据结构和算法学习记录——特殊线性表之队列-队列的概念、队列结构体类型定义 、基本接口函数、初始化函数、销毁队列函数、入队列函数、判断队列是否为空、出队列函数、读取队头队尾的数据 、计算队列数据个数
42 0
|
7月前
|
存储
DS:链式结构实现队列
DS:链式结构实现队列
|
存储 Java
【数据结构】 队列(Queue)与队列的模拟实现
【数据结构】 队列(Queue)与队列的模拟实现
|
存储
什么是队列,如何实现?
什么是队列,如何实现?
115 0
什么是队列,如何实现?
队列的定义、基本操作、顺序实现(初始化,入队,出队)
数据结构:队列的定义、基本操作、顺序实现(初始化,入队,出队)附有代码讲解
663 0
|
存储
队列的定义及基本操作实现(链式)
队列的定义及基本操作实现(链式)
156 0
|
存储
设计一个名为Queue的类用于存储整数。在栈中,元素以“后进先出”的方式获取。在队列中,元素以“先进先出”方法获取。
设计一个名为Queue的类用于存储整数。在栈中,元素以“后进先出”的方式获取。在队列中,元素以“先进先出”方法获取。
114 0
|
C++
队列类(C++)
构造函数:初始化队列:将初始尺寸保存到 size,将队首 front 和队尾 rear 均置为 0,为动态数组分配内存并将起始地址保存到 element。Dequeue 函数:若队列不空,则从队列中取出元素并保存到 value中,函数值为 true;size 为动态数组的尺寸,front 为队首元素的下标,rear 为队尾元素下一位置的下标,element 为动态数组的起始地址。Clear 函数:清空队列,将 front 和 rear 重置为 0,操作成功,函数值为 true。首先定义数据元素类型。
147 0