线性结构--队列

队列和栈其实很类似,最大的不同就是栈是先将后出,而队列是先进先出,就像一根水管固定一端进,而另一端负责出
队列又分为数组队列和循环队列,数组队列dequeue(出队)方法的时间复杂度为O(n),而循环队列很好的优化了这点,下面通过代码来看看它们是如何实现的.

数组队列(ArrayQueue)

接口queue:

public interface Queue {
    //获取大小
    int getSize();
    //是否为空
    boolean isEmpty();
    //添加数据
    void enqueue(E e);
    //取出数据
    E dequeue();
    //获取第一个数据
    E getFront();
}

实现:ArrayQueue

public class ArrayQueue implements Queue {
    private Array  array;

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

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

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

    @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("]");
        return res.toString();
    }
}

测试:

循环10次入队,每过3次出队

  public static void main(String[] args) {
      ArrayQueue 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]
Queue: front [0, 1]
Queue: front [0, 1, 2]
Queue: front [1, 2]
Queue: front [1, 2, 3]
Queue: front [1, 2, 3, 4]
Queue: front [1, 2, 3, 4, 5]
Queue: front [2, 3, 4, 5]
Queue: front [2, 3, 4, 5, 6]
Queue: front [2, 3, 4, 5, 6, 7]
Queue: front [2, 3, 4, 5, 6, 7, 8]
Queue: front [3, 4, 5, 6, 7, 8]
Queue: front [3, 4, 5, 6, 7, 8, 9]

Process finished with exit code 0

循环队列(LoopQueue)

LoopQueue

如图所示,此时队列虽然有两次出队操作是坐标0和1此时为空,但按照数组队列来说,新入队的h仍会判断此时队列已满--造成扩容,是原本时间复杂度为O(1)的入队操作变回O(n),而循环队列将我们最后一位(tail)直接指向了坐标0,这样就既可以利用前面出队后空出来的0地址又减少了扩容操作,这就是循环队列.

实现LoopQueue:

基础部分:

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

    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 int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return front==size;
    }
    @Override
    public E getFront() {
        return data[front];
    }
}

这里主要需要注意的由于tail是指向队尾的下一个位置,所以在初始化时数组的size+1,相应的返回的队列长度时也需要-1

动态扩容:

    private void resize(int capacity) {
        E[] newData = (E[]) new Object[capacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[((i+front)%data.length)];
        }
        data = newData;
        tail = size;
        front = 0;
    }

这里注意在新的队列中需要将旧的数据从头开始排列(data[((i+front)%data.length)])
入队和出队:

    @Override
    public void enqueue(E e) {
        if ((tail+1)%data.length==front) {
            resize(getCapacity()*2);
        }
        data[tail] = e;
        tail = (tail+1)%data.length;
        size++;
    }

    private void resize(int capacity) {
        E[] newData = (E[]) new Object[capacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[((i+front)%data.length)];
        }
        data = newData;
        tail = size;
        front = 0;
    }

    @Override
    public E dequeue() {
        if(isEmpty())
            throw new IllegalArgumentException("Cannot dequeue from an empty queue.");

        E e = data[front];
        data[front] = null;
        front = (front+1)%data.length;
        size--;
        if(size == getCapacity() / 4 && getCapacity() / 2 != 0)
            resize(getCapacity() / 2);
        return e;
    }

测试:

    @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){
            res.append(data[i]);
            if((i + 1) % data.length != tail)
                res.append(", ");
        }
        res.append("] tail");
        return res.toString();
    }

    public static void main(String[] args){

        LoopQueue queue = new LoopQueue<>(5);
        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 = 5
front [0] tail
Queue: size = 2 , capacity = 5
front [0, 1] tail
Queue: size = 3 , capacity = 5
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

Process finished with exit code 0

动态数组和循环数组效率比较

public class Main {


    //测试使用q出队入队opCount次,所需要的时间,单位:秒
    private static Double testQueue(Queue queue, int opCount){
        long startTime = System.currentTimeMillis();
        Random random = new Random();
        for (int i = 0; i < opCount; i++) {
            queue.enqueue(random.nextInt(Integer.MAX_VALUE));
        }

        for (int i = 0; i < opCount; i++) {
            queue.dequeue();
        }

        long finishTime = System.currentTimeMillis();
        return (finishTime-startTime)/1000.0;
    }
    public static void main(String[] args) {
        int opCount = 100000;
        ArrayQueue arrayQueue = new ArrayQueue();
        System.out.println("arrayQueue, time = " +testQueue(arrayQueue,opCount));


        LoopQueue loopQueue = new LoopQueue();
        System.out.println("loopQueue, time = " +testQueue(loopQueue,opCount));
    }
}

结果:

arrayQueue, time = 6.641
loopQueue, time = 0.01

Process finished with exit code 0

可以看出循环队列的效率大大强于普通的数组队列

你可能感兴趣的:(线性结构--队列)