队列概念可以理解为一堆人排队买票,先来的先买,后来的排在后面,不允许插队。也就是先进先出,这就是队列。
上一篇栈中,我们学到,栈只有2个操作,入栈和出栈。队列也只有2个操作,入队和出队。
- 入队:放一个数据到队列尾部
- 出队:从队列头部拿出一个数据。

一、顺序队列和链式队列
队列和栈一样,既可以用数组实现,也可以用链表来实现。用数组实现的队列叫顺序队列,用链表实现的队列叫链式队列。
顺序队列的实现:
// 用数组实现的队列
public class ArrayQueue {
// 数组:items,数组大小:n
private String[] items;
private int n = 0;
// head表示队头下标,tail表示队尾下标
private int head = 0;
private int tail = 0;
// 申请一个大小为capacity的数组
public ArrayQueue(int capacity) {
items = new String[capacity];
n = capacity;
}
// 入队操作,将item放入队尾
public boolean enqueue(String item) {
// tail == n表示队列末尾没有空间了
if (tail == n) {
// tail ==n && head==0,表示整个队列都占满了
if (head == 0) return false;
// 数据搬移
for (int i = head; i < tail; ++i) {
items[i-head] = items[i];
}
// 搬移完之后重新更新head和tail
tail -= head;
head = 0;
}
items[tail] = item;
++tail;
return true;
}
// 出队
public String dequeue() {
// 如果head == tail 表示队列为空
if (head == tail) return null;
// 为了让其他语言的同学看的更加明确,把--操作放到单独一行来写了
String ret = items[head];
++head;
return ret;
}
}
这个实现中,一共有2个变量,用来标记数据的头和尾。当数组满后,无法入队。当数组后半部分满了之后,我们入队的时候,需要将数据移动到数组的左边,然后重新修改head和tail。使得数组可以继续入队。

二、循环队列
上面的操作中,当tail==数组的大小n,但是head并不等于0 的时候,我们进行了数据的迁移,这样入队操作的性能肯定会有影响,我们可以用循环队列来解决这个问题。

如图:原本数组是有头有尾的,如果我们能让tail到达7的时候,再有数据入队的时候,tail=0,那么就不需要数据的迁移,而能够继续插入数据了。
循环队列的实现难度在于如何判定队列空和队列满。
队列为空的条件仍然是tail == head。队列满的时候,有一个规律:(tail+1)%n = head。
按下图就是(3+1)%8 = 4

为了判断队列满,我们需要浪费一个存储空间:
public class CircularQueue {
// 数组:items,数组大小:n
private String[] items;
private int n = 0;
// head表示队头下标,tail表示队尾下标
private int head = 0;
private int tail = 0;
// 申请一个大小为capacity的数组
public CircularQueue(int capacity) {
items = new String[capacity];
n = capacity;
}
// 入队
public boolean enqueue(String item) {
// 队列满了
if ((tail + 1) % n == head) return false;
items[tail] = item;
tail = (tail + 1) % n;
return true;
}
// 出队
public String dequeue() {
// 如果head == tail 表示队列为空
if (head == tail) return null;
String ret = items[head];
head = (head + 1) % n;
return ret;
}
}
三、阻塞队列和并发队列
阻塞队列实际就是在队列为空的时候,从队列取数据会被阻塞,程序一直在等待,直到队列中有数据,才会拿到数据返回。同理,插入数据如果队列满了也会被阻塞,直到有位置可以插入。
阻塞队列在多线程情况下会用到,比如生产者消费者模型,快的一方会被阻塞。
多线程如果有多个生产者,同时入队,会有线程安全问题,比如往同一个位置存储数据,会造成数据覆盖而丢失,这个时候,就需要并发队列--线程安全的队列。
四、总结
队列和栈很相似,也只有2个操作,入队和出队。属于先进先出。
循环队列是重点,想要代码实现,重点是判空和判满的条件。
优化队列实现:顺序、循环与并发,深度解析

被折叠的 条评论
为什么被折叠?



