3.数据结构--队列

Posted zouke1220

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
",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
    }
}

 

以上是关于3.数据结构--队列的主要内容,如果未能解决你的问题,请参考以下文章

perl中的队列

VSCode自定义代码片段5——HTML元素结构

VSCode自定义代码片段5——HTML元素结构

VSCode自定义代码片段5——HTML元素结构

是否需要锁定阅读对象?

IPC System V 消息队列 - 发送一个数组块