美文网首页
线性结构--队列

线性结构--队列

作者: 二妹是只猫 | 来源:发表于2019-06-28 13:43 被阅读0次

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

数组队列(ArrayQueue)

接口queue:

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

实现:ArrayQueue

public class ArrayQueue<E> implements Queue<E> {
    private Array<E>  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<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]
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<E> implements Queue<E> {
    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<Integer> 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<Integer> 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

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

相关文章

  • 2019-05-14*(线性结构 和 非线性结构的分类)

    线性结构: 百度百科:线性结构是一个有序数据元素的[集合]常用的线性结构有:线性表,栈,队列,双队列,数组,串。 ...

  • Java数据结构和算法概览

    Java数据结构和算法概览 数据结构 线性数据结构:常见的有一维数组,线性表,栈,队列,双队列,串。 非线性数据结...

  • MQ(message queue)

    是什么? 1.什么是队列? 队列是一种先进先出的数据结构。 数据结构 线性数据结构:常用的:线性表、栈、队列、串等...

  • 线性结构-队列

    编译环境:python v3.5.0, mac osx 10.11.4 什么是队列 具有一定操作约束的线性表,只能...

  • 线性结构--队列

    队列和栈其实很类似,最大的不同就是栈是先将后出,而队列是先进先出,就像一根水管固定一端进,而另一端负责出队列又分为...

  • 线性结构:队列

    定义 队列是一种先进先出(First In First Out,FIFO)的数据结构。 实现 可以和栈一样,把队列...

  • 数据结构(线性结构 栈与队列)

    栈与队列都是特殊的线性表,它们也是线性结构。用户可以采用顺序存储结构和链式存储结构两种方式来存储。栈和队列结构是各...

  • C#之数据结构(上)

    数据结构 一般将数据结构分为两大类: 线性数据结构和非线性数据结构。 线性数据结构有: 线性表、栈、队列、串、数组...

  • 2018-07-29--------数据结构汇总

    数据结构 1、数据结构的三要素:逻辑结构,存储结构,数据运算 2、逻辑结构: 1)线性结构:线性表,栈,队列 ...

  • NO.26 队列和栈、Map查询表

    队列(Queue)是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添...

网友评论

      本文标题:线性结构--队列

      本文链接:https://www.haomeiwen.com/subject/bjmlcctx.html