线性结构--队列

队列和栈其实很类似,最大的不同就是栈是先将后出,而队列是先进先出,就像一根水管固定一端进,而另一端负责出
队列又分为数组队列和循环队列,数组队列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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352