队列的简单理解
Posted ljsh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了队列的简单理解相关的知识,希望对你有一定的参考价值。
? 队列(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的位操作可谓十分精髓,一定要拜读一下源码。
以上是关于队列的简单理解的主要内容,如果未能解决你的问题,请参考以下文章