3.数据结构--队列

队列是一种先进先出的线性数据结构

1.队列的实现

public interface Queue<E> {
    int getSize();
    boolean isEmpty();
    void enqueue(E e);
    E dequeue();
    E getFront();
}

实现队列

public class ArrayQueue<E>  implements Queue<E>{
    private Array<E> array;

    public ArrayQueue(int capacity){
       array = new Array<>(capacity);
    }

    public ArrayQueue(){
        array = new Array<>();
    }

    @Override
    public int getSize() {
        return array.getSize();
    }

    @Override
    public boolean isEmpty(){
        return array.isEmpty();
    }

    public int getCapacity(){
        return array.getCapacity();
    }

    @Override
    public void enqueue(E e) {
        array.addLast(e);
    }

    @Override
    public E dequeue() {
        return array.removeFirst();
    }

    @Override
    public E getFront(){
        return array.getFirst();
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();
        res.append("Queue:");
        res.append("front [");
        for(int i = 0;i < array.getSize();i ++){
            res.append(array.get(i));
            if(i != array.getSize() - 1)
                res.append(",");
        }
        res.append("] tail");
        return res.toString();
    }
}

调用实例

public class Main {
    public static void main(String[] args) {
        ArrayQueue<Integer> queue = new ArrayQueue<>();
        for(int i = 0;i < 10;i ++){
            queue.enqueue(i);
            System.out.println(queue);

            if(i % 3 == 2){
                queue.dequeue();
                System.out.println(queue);
            }
        }
//        Queue:front [0] tail
//        Queue:front [0,1] tail
//        Queue:front [0,1,2] tail
//        Queue:front [1,2] tail
//        Queue:front [1,2,3] tail
//        Queue:front [1,2,3,4] tail
//        Queue:front [1,2,3,4,5] tail
//        Queue:front [2,3,4,5] tail
//        Queue:front [2,3,4,5,6] tail
//        Queue:front [2,3,4,5,6,7] tail
//        Queue:front [2,3,4,5,6,7,8] tail
//        Queue:front [3,4,5,6,7,8] tail
//        Queue:front [3,4,5,6,7,8,9] tail
    }
}

2.数组队列的复杂度分析

3.数组队列的问题

循环队列

tail和front互相追赶着,这个追赶过程就是队列添加和删除的过程,如果tail追到front说明队列满了,如果front追到tail说明队列为空。

令队列空间中的一个单元闲置,使得队列非空时,tail与front之间至少间隔一个空闲单元。

当tail=front的时候,队列可能是满,也可能是空。

因为存在满和空两种情况,我们需要分别判断:

:当队列添加元素到tail的下一个元素是front的时候,也就是转圈子要碰头了,我们就认为队列满了。(tail+1)%c=front

:当队列删除元素到front=tail的时候,我们认为队列空了。tail==front,不一定为0

(1) 入队

(2)出队

public class LoopQueue<E> implements Queue<E> {
    private E[] data;
    private int front,tail;
    private int size;

    public LoopQueue(int capacity){
        data = (E[])new Object[capacity + 1];
        front = 0;
        tail = 0;
        size =0;
    }

    public LoopQueue(){
        this(10);
    }

    //获取队列容量
    public int getCapacity(){
        return data.length - 1;
    }

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

    //获取队列元素个数
    @Override
    public int getSize(){
        return size;
    }

    //入队
    @Override
    public void enqueue(E e){
        //队列满
        if((tail + 1) % data.length == front)
            resize(getCapacity() * 2); //扩容

        //将入队元素放到尾指针指向的位置
        data[tail] = e;
        tail =(tail +1) % data.length;  //由于是循环队列,所以后面要% data.length
        /*
         *           front        tail
         * 0   1   2   3   4   5   6   7   8   9     //3、4、5、6是满的
         * 接着入队的元素,要放到(6+1) % 10=7 指针所指的位置处
         *            front                   tail
         * 0   1   2   3   4   5   6   7   8   9     //3、4、5、6、7、8、9是满的
         * 最右侧已经满了,后面入队元素要放到(9+1) % 10=0 指针所指的位置处
         *
         * tail       front
         * 0   1   2   3   4   5   6   7   8   9     //3、4、5、6、7、8、9、0是满的
         * 下一个入队元素,放的位置就是(0+1) % 10=1 指针所指的位置处
         */
        size ++;
    }

    //出队
    @Override
    public E dequeue(){
        if(isEmpty())
            throw new IllegalArgumentException("cannot dequeue from an empty queue");

        E ret = data[front]; //保存出队元素
        data[front] = null;  //出队
        front = (front + 1) % data.length;  //由于是循环队列,所以后面要% data.length
        size --;
        if(size == getCapacity() / 4 && getCapacity() / 2 != 0)
            resize(getCapacity() / 2); //缩容
        return ret;
    }

    @Override
    public E getFront(){
        if(isEmpty())
            throw new IllegalArgumentException("Queue is Empty");
        return data[front];

    }

    private void resize(int newCapacity){
        E[] newData = (E[])new Object[newCapacity + 1];    //此处由于原来浪费了1个空间,所以扩容时要加上这1个
        for(int i = 0;i < size;i ++)
            //把原来的位置的元素重新放到新开辟的空间内,从指针位置0处开始放
            newData[i] = data[(i + front) % data.length];  //由于是循环队列,所以后面要% data.length
        /*
         * tail  front
         *  -       -   -   -   -   -   -   -   -
         *  0   1   2   3   4   5   6   7   8   9
         * 上面的队列已满,开始扩容
         *
         * 扩容后为原来的2倍,原来位置的数据要重新放到新的空间,从指针0处开始放置
         * front                           tail
         *  -   -   -   -   -   -   -   -   -
         *  0   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20
         *
         *      原来位置                     新位置
         *   (0 + 2) % 10 = 2                  0
         *   (1 + 2) % 10 = 3                  1
         *   (2 + 2) % 10 = 4                  4
         *   ...............
         */
        data = newData;
        front = 0;
        tail = size;
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();
        res.append(String.format("Queue:size = %d , capacity = %d\n",size,getCapacity()));
        res.append("front [");

        //非循环队列
        //for(int i = 0; i < size ; i++)

        //循环队列
        /*
         * 第一次循环  front=2  i=(2 + 1) % 10 = 3
         * 第二次循环  front=2  i=(3 + 1) % 10 = 4
         * ....
         * 第八次循环  front=2  i=(9 + 1) % 10 = 0
         * 第九次循环  front=2  i=(0 + 1) % 10 = 1
         */
        for(int i = front;i != tail;i = (i + 1) % data.length){
            res.append(data[i]);
            if((i + 1) % data.length != tail)
                res.append(", ");
        }
        res.append("] tail");
        return res.toString();
    }
}

调用循环队列

public class Main {
    public static void main(String[] args) {
        LoopQueue<Integer> queue = new LoopQueue<>();
        for(int i = 0;i < 10;i ++){
            queue.enqueue(i);
            System.out.println(queue);

            if(i % 3 == 2){
                queue.dequeue();
                System.out.println(queue);
            }
        }
//        Queue:size = 1 , capacity = 10
//        front [0] tail
//        Queue:size = 2 , capacity = 10
//        front [0, 1] tail
//        Queue:size = 3 , capacity = 10
//        front [0, 1, 2] tail
//        Queue:size = 2 , capacity = 5
//        front [1, 2] tail
//        Queue:size = 3 , capacity = 5
//        front [1, 2, 3] tail
//        Queue:size = 4 , capacity = 5
//        front [1, 2, 3, 4] tail
//        Queue:size = 5 , capacity = 5
//        front [1, 2, 3, 4, 5] tail
//        Queue:size = 4 , capacity = 5
//        front [2, 3, 4, 5] tail
//        Queue:size = 5 , capacity = 5
//        front [2, 3, 4, 5, 6] tail
//        Queue:size = 6 , capacity = 10
//        front [2, 3, 4, 5, 6, 7] tail
//        Queue:size = 7 , capacity = 10
//        front [2, 3, 4, 5, 6, 7, 8] tail
//        Queue:size = 6 , capacity = 10
//        front [3, 4, 5, 6, 7, 8] tail
//        Queue:size = 7 , capacity = 10
//        front [3, 4, 5, 6, 7, 8, 9] tail
    }
}

4.循环队列的复杂度分析

5.数组队列和循环队列的比较

import java.util.Random;
public class Main {
    //测试使用q运行opCount个enqueue和dequeue操作所需要的时间,单位:秒
    private static double testQueue(Queue<Integer> q,int opCount){
        long startTime = System.nanoTime();
        Random random = new Random();
        for(int i = 0; i < opCount; i++)
            q.enqueue(random.nextInt(Integer.MAX_VALUE));
        for(int i = 0;i < opCount; i++)
            q.dequeue();
        long endTime = System.nanoTime();
        return (endTime - startTime) / 1000000000.0;
    }

    public static void main(String[] args) {
        int opCount = 100000;
        //数组队列
        ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
        double time1 = testQueue(arrayQueue,opCount);
        System.out.println("ArrayQueue, time:" + time1 + "s"); //ArrayQueue, time:4.859302024s

        //循环队列
        LoopQueue<Integer> loopQueue = new LoopQueue<>();
        double time2 = testQueue(loopQueue,opCount);
        System.out.println("LoopQueue, time:" + time2 + "s");  //LoopQueue, time:0.019878675s
    }
}

原文地址:https://www.cnblogs.com/zouke1220/p/9490913.html

时间: 2024-11-13 10:14:23

3.数据结构--队列的相关文章

基本数据结构-队列的实现及其运用

二.队列 队列是一种先进先出的数据结构,元素只能添加到队尾,而对元素的删除,修改,检索只能在队头进行.与栈的差异是很明显的.同样队列的实现可以基于链表,也可以基于数组.和栈的基本操作差不多,但队列多了一个指针(标号)指向末尾的元素,因为需要在末尾插入元素. 1.队列的链表实现 #ifndef QUEUE_H #define QUEUE_H #include <iostream> template <class T> class queue { public: queue(); ~q

数据结构—队列

数据结构-队列 1.队列的定义 队列(Queue)也是一种运算受限的线性表,它的运算限制与栈不同,是两头都有限制,插入只能在表的一端进行(只进不出),而删除只能在表的另一端进行(只出不进),允许插入的一端称为队尾(rear),允许删除的一端称为队头 (Front) 队列模型 2.队列的操作 队列的操作原则是先进先出的,所以队列又称作FIFO表(First in First out) 置空队:InitQueue(Q) 判队空:QueueEmpty(Q) 判队满:QueueFull(Q) 入队:En

java数据结构队列

队列类似于现实生活中的排队.队列是先进先出的原则,只允许在队列头部去除元素,队列尾部添加元素. 下面是分别用数组和链表为存储结构实现的队列 public interface Queue<T> { int size(); T remove(); void add(T element); boolean isEmpty(); void clear(); boolean hasElement(); } public class ArrayQueue<T> implements Queue

数据结构--队列实现(顺序队列和链队列)与C++模板

数据结构--队列实现(顺序队列和链队列)与C++模板 一.顺序队列 队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表. ①和顺序表一样,顺序队列用一个向量空间来存放当前队列中的元素. ②由于队列的队头和队尾的位置是变化的,设置两个指针front和rear分别指示队头元素和队尾元素在向量空间中的位置,它们的初值在队列初始化时均应置为0. 注意: ①当头尾指针相等时,队列为空. ②在非空队列里,队头指针始终指向队头元素,尾指针始终指向队尾元素的下一位置.(所以以下循环顺序队列中当队尾

数据结构--队列之C数组实现

队列是一种限定操作的线性表,它只能在表的一段插入,另外一段取出. 所以也称为先进先出数据结构(FIFO---First In First Out) C代码如下(有小bug不想调了,作为参考即可): #include<stdio.h> #define maxsize 5 typedef int ElemType; typedef struct queue { int head; int tail; ElemType Data[maxsize]; }Queue; void InitQueue(Qu

java 数据结构 队列的实现

java 数据结构队列的代码实现,可以简单的进行入队列和出队列的操作 /** * java数据结构之队列的实现 * 2016/4/27 **/ package cn.Link; import java.util.*; public class Queue{ Node tail = new Node(); Node nowNode = new Node(); //永远指向队首 int size; Queue(){}; Queue(String date){ this.tail.date = dat

数据结构队列实例

1 //数据结构 --队列 2 //静态队列-用数组实现 3 //静态队列通常是循环队列 4 //循环队列讲解 5 //1.静态队列为什么必须是循环队列? 6 //2.循环队列需要几个参数来确定? 7 /* 8 front 和 rear 9 1)队列初始化,font和rear的值都为零 10 2)队列非空 11 font代表的是队列的第一个元素 12 rear代表的是队列的最后一个有效元素的下一个元素 13 3)队列空 14 front和rear值 15 */ 16 //3.循环队列各个参数的含

JavaScript数据结构——队列的实现

前面楼主简单介绍了JavaScript数据结构栈的实现,http://www.cnblogs.com/qq503665965/p/6537894.html,本次将介绍队列的实现. 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表.进行插入操作的端称为队尾,进行删除操作的端称为队头. 队列的两种主要操作是:向队列中插入新元素和删除队列中的元素.插入操作也叫做入队,删除操作也叫做出队.入队操

数据结构-队列_习题

<实用数据结构> 第4章 6.1题 //算法设计题.要求:设一个循环队列Queue,只有头指针front,不设尾指针,另设一个含有元素个数的记录器count, //试写出相应的入队和出队的算法 #include <iostream> #include <stdlib.h> #include <time.h> using namespace std; #define MAXLEN 10 #define Status int #define datatype i

数据结构——队列及循环队列

说明:严蔚敏的<数据结构>(C语言版)学习笔记,记录一下,以备后面查看. #include <stdio.h> #include <stdlib.h> #define OK 1; #define ERROR -1; typedef int QElemType; typedef int Status; //定义队列节点 typedef struct QNode{ QElemType data; struct QNode *next; }QNode, *QueuePtr;