zoukankan      html  css  js  c++  java
  • 队列的简单理解

    ​ 队列(Queue)是一个先进先出(FIFO)的数据结构,下面我们直接来看Java里的实现。Queue在Java里作为一个接口,在java.util包下继承自Collection,共有六个方法如下。

    方法 用处 出错时
    boolean add(E e); 向队列尾部插入一个元素 超长抛异常
    boolean offer(E e); 向队列尾部插入一个元素 超长返回false
    E remove(); 移除并返回队首元素 队列为空时产生异常
    E poll(); 移除并返回队首元素 队列为空时返回null
    E element(); 获取队首元素 队列为空时产生异常
    E peek(); 获取队首元素 队列为空时返回null

    总结起来就是三种操作,插入,删除,和查询,分别有对应的异常和返回值方法。

    插入

    删除

    查询

    ​ 以上是基本的队列只能在尾部插入,头部移除,我们给他丰富一下功能,双端都可以插入和移除,这就是双向队列(Deque),来看一下Java的Dequne接口的方法,位于java.util下,这里主要查看Deque的一些方法,忽略了,Queue的方法和Stack的方法已及实现Collection的方法

    方法 说明 出错时
    void addFirst(E e); 向队首插入元素 超长时抛异常
    void addLast(E e); 向队尾插入元素 超长时抛异常
    boolean offerFirst(E e); 向队首插入元素 超长时返回false
    boolean offerLast(E e); 向队尾插入元素 超长时返回false
    E removeFirst(); 移除并返回队首元素 队列为空抛异常
    E removeLast(); 移除并返回队尾元素 队列为空抛异常
    E pollFirst(); 移除并返回队首元素 队列为空返回null
    E pollLast(); 移除并返回队尾元素 队列为空返回null
    E getFirst(); 获取队首元素 队列为空抛异常
    E getLast(); 获取队尾元素 队列为空抛异常
    E peekFirst(); 获取队首元素 队列为空时返回null
    E peekLast(); 获取队尾元素 队列为空时返会null

    总结起来就是Queue的方法x2,分别也是 插入,删除,查询

    插入

    删除

    查询

    Deque包含了Queue的功能,我们直接来看一下Deque的实现类ArrayDeque的代码,这里是用数组实现了队列,同样也有用链表实现队列的,那就比较容易理解了,链表删除表头和表尾元素都不难,扩充也简单。对于数组实现队列来说,扩充容量肯定是要解决的问题,我猜想扩容的方法和之前的数组篇没啥大体区别。先来看插入

    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }
    

    主要看下面句,其余的都比较好理解,doubleCapacity是扩容函数稍后再看

    elements[head = (head - 1) & (elements.length - 1)] = e;
    

    &是与操作,都为1的位为1,其余为0。这里的elements.length - 1作为一个掩码来使用,光这么看理解起来有些晦涩,直接看图吧,有两种情况,第一种是head为0时

    巧妙的运用了&操作使数组首尾相接,第二种时head不为0时

    这个就比较好理解了,接下来再看addLast

    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
    

    因为需要判断在head==tail的时候来扩容,所以这里的tail都是指向没有元素的格子的,相反的head都是指向队首元素所在的位置的,所以addFirst和addLast再插入元素的时候稍微有点区别。addLast是先写入再增加,还是看这句

     if ( (tail = (tail + 1) & (elements.length - 1)) == head)
    

    依然是两种情况第一种是tail加完之后等于队列的长度

    巧妙的&操作让队列首位相连,当tail部位队列长度-1时

    接下来看看这个doubleCapacity函数是怎么个原理

    private void doubleCapacity() {
        assert head == tail;	//确保头尾相等
        int p = head;	
        int n = elements.length;
        int r = n - p; // number of elements to the right of p  
        int newCapacity = n << 1;	//扩大二倍
        if (newCapacity < 0)
            throw new IllegalStateException("Sorry, deque too big");
        Object[] a = new Object[newCapacity];	//创建新的数组
        System.arraycopy(elements, p, a, 0, r);	//复制head之后的元素
        System.arraycopy(elements, 0, a, r, p); //复制head之前的元素
        elements = a;
        head = 0;
        tail = n;
    }
    

    这个函数arraycopy这部分不太好理解,还是结合图来说

    数组扩容总是放生在add操作上,无论是head后边追上tail还是tail后退碰到head,都是tail在前,head在后,r,p都如图中所示。

    删除操作和这个增加操作其实是相对应的头删除对应尾插入,尾删除对应头插入,一样用&来连接,如果队列为空了,取不到元素了就会返回Null或者报错了,本来还想写一下优先队列的,感觉篇幅有点长了还是单开一文吧

    总结

    队列Queue有先进先出的特点,Deque可以两头进出,ArrayDeque的位操作可谓十分精髓,一定要拜读一下源码。

  • 相关阅读:
    设计模式 23
    生活杂谈
    设计模式经典书籍
    ABP 样板开发框架系列
    关键字
    vs 2015
    优秀文章推荐
    Parallel 并行编程
    CSRF
    sql性能优化
  • 原文地址:https://www.cnblogs.com/ljsh/p/12810601.html
Copyright © 2011-2022 走看看