zoukankan      html  css  js  c++  java
  • 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
        }
    }
  • 相关阅读:
    ant
    Java中的值传递和引用传递
    待解决的问题
    Ant生成文件解析
    JUnit初学
    遍历枚举
    2013年5月阅读链接
    《C Primer Plus》阅读笔记(3)
    《C Primer Plus》阅读笔记(2)
    《C Primer Plus》阅读笔记(4)
  • 原文地址:https://www.cnblogs.com/zouke1220/p/9490913.html
Copyright © 2011-2022 走看看