队列

(一)队列

队列也是一种线性结构,相比数组,队列对应的操作是数组的子集。队列只能从队尾添加元素,从队首取出元素。并且,队列是一种先进先出的数据结构First In First Out (FIFO)

(二)队列的实现

队列接口实现如下:

public interface Queue<E>{
    void enqueue(E e);//入列
    E dequeue();//出列
    E getFront();//获取队列的第一个元素
    int getSize();//获取队列的大小
    boolean isEmpty();//判断队列是否为空
}

 基于动态数组的队列:

  • 声明
    public class ArrayQueue<E> implements Queue<E> {
    
        private Array<E> array;
    
        public ArrayQueue(int capacity){
            array = new Array<>(capacity);
        }
        public ArrayQueue(){
            array = new Array<>();
        }
    
    
        public int getCapacity(){
            return array.getCapacity();
        }
    
        //队列的输出格式
        @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();
        }
    
    }
    
    
    
    
    
    

     

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

     

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

     

  • 获取队列的第一个元素
     @Override
        public E getFront(){
            return array.getFirst();
        }

     

  • 获取队列的大小
     @Override
        public int getSize(){
            return array.getSize();
        }

     

  • 判断队列是否为空
    @Override
        public boolean isEmpty(){
            return array.isEmpty();
        }

     

(三)数组队列的复杂度分析

  • ArrayQueue<E>
    • void enqueue(E e);    O(1)均摊
    • E dequeue();    O(n)
    • E getFront();    O(1)
    • int getSize();    O(1)
    • boolean isEmpty();    O(1)

(四)循环队列

  • front == tail 队列为空
  • (tail + 1) % capacity == front 队列满
  • capacity中浪费一个空间

(五)循环队列的实现

  • 声明
    public class LoopQueue<E> implements Queue<E> {
    
        private E[] data;
        private int front,tail;//队首、队尾索引
        private int size;//队列的大小,只使用front,tail怎么计算size?
    
        public LoopQueue(int capacity){
            data = (E[])new Object[capacity + 1];//循环队列要浪费一个空间,故+1
            front = 0;
            tail = 0;
            size = 0;
        }
    
        public LoopQueue() {
            this(10);
        }
    
        public int getCapacity(){
            return data.length - 1;
        }
    
        //循环队列的输出方式
        @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 = front; i != tail; i = (i + 1) % data.length){//循环队列遍历方式2
                res.append(data[i]);
                if ((i + 1) % data.length != tail)
                    res.append(", ");
            }
            res.append("] tail");
    
            return res.toString();
        }
    
    
    }
    

     

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

     

  • 获取循环队列的大小
    @Override
        public int getSize(){
            return size;
        }

     

  • 向循环队列中添加元素e
     @Override
        public void enqueue(E e){
            if((tail + 1) % data.length == front)
                resize(getCapacity()*2);//增容
            data[tail] = e;
            tail = (tail + 1) % data.length;
            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;
            size --;
            if(size == getCapacity() / 4 && getCapacity() / 2 != 0)
                resize(getCapacity() / 2);//缩容
            return ret;
        }

     

  • 获取循环队列的第一个元素
    @Override
        public E getFront(){
            if (isEmpty())//判断队列是否为空
                throw new IllegalArgumentException("cannot dequeue from an empty queue");
    
            return data[front];
        }

     

  • 扩容或缩容
    private void resize(int capacity){
            E[] newData = (E[])new Object[capacity + 1];
    
            for (int i = 0; i < size; i ++)//循环队列遍历方式1
                newData[i] = data[(i + front) % data.length];
    
            data = newData;
            front = 0;
            tail = size;
    }

     

(六)循环队列的复杂度分析

  • LoopQueue<E>
    • void enqueue(E e);    O(1)均摊
    • E dequeue();    O(1)均摊
    • E getFront();    O(1)
    • int getSize();    O(1)
    • boolean isEmpty();    O(1)

(七) 数组队列和循环队列的比较

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) {
	// write your code here

        int opCount = 100000;

        ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
        double time1 = testQueue(arrayQueue,opCount);
        System.out.println("ArrayQueue, time: " + time1 + "s");


        LoopQueue<Integer> loopQueue = new LoopQueue<>();
        double time2 = testQueue(loopQueue, opCount);
        System.out.println("LoopQueue, time: " + time2 + "s");

    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值