1. 概念
前面一个篇博客介绍了栈,现在来介绍一下队列,它和栈有一定的相似度,可以看完两个之后对比记忆。
队列是一个先进先出
的数据结构,允许插入的一端称为队尾
,允许删除的一端称为对首
,示意图如下。
和栈一样,可以基于数组和链表实现队列。
2. 队列的实现
2.1 基于数组
示意图:
代码实现:
public class ArrayQueue {
private Object[] data; // [0]位置看做成队首,[tail]看做队尾
private int tail;
public ArrayQueue(int len) {
if (len < 0) {
throw new IllegalArgumentException("数组大小小于0");
}
data = new Object[len];
tail = -1;
}
/**
* 入队
*/
public void enqueue(Object o) {
// 如果已经达到最大容量,扩容2倍
if (data.length == tail + 1) {
Object[] newArr = new Object[data.length << 1];
System.arraycopy(data, 0, newArr, 0, data.length);
data = newArr;
}
data[++tail] = o;
}
public Object dequeue() {
if (empty()) {
throw new RuntimeException("队列元素为空");
}
Object ret = data[0];
// 所有的元素向左移动
for (int i = 1; i <= tail; i++) {
data[i - 1] = data[i];
}
tail--;
return ret;
}
public boolean empty() {
return tail == -1;
}
/**
* 打印方式: 队首->队尾
* @return
*/
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer("(");
for(int i =0 ; i <= tail ; i++){
stringBuffer.append(data[i]);
stringBuffer.append(",");
}
if (stringBuffer.length() == 1) {
return stringBuffer.toString() + ")";
} else {
return stringBuffer.substring(0, stringBuffer.length() - 1) + ")";
}
}
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(3);
arrayQueue.enqueue(12);
arrayQueue.enqueue(13);
arrayQueue.enqueue(14);
arrayQueue.enqueue(15);
// (12,13,14,15)
System.out.println(arrayQueue);
// 3
System.out.println(arrayQueue.tail);
arrayQueue.dequeue();
arrayQueue.dequeue();
arrayQueue.dequeue();
// (15)
System.out.println(arrayQueue);
arrayQueue.dequeue();
// ()
System.out.println(arrayQueue);
// Exception in thread "main" java.lang.RuntimeException: 队列元素为空
arrayQueue.dequeue();
}
}
-
属性分析
data
: [0]位置看做成队首,[tail]看做队尾,队列只是data的一个元素子集,通过tail控制队列元素范围。
tail
: 队尾元素位置 -
方法分析
enqueue
: 入队,在tail一个元素位置添加一个新的元素。如果超出数组长度,需要扩容。
dequeue
: 出队,取数组下标为0的元素,并数组范围[0,tail]整体向左移动。
2.2 基于链表
示意图:
代码实现:
public class LinkedQueue {
private Node head; // 队首
private Node tail; // 队尾
private int size;
private class Node {
private Object data;
private Node next;
Node(Object data) {
this.data = data;
}
}
public void enqueue(Object o) {
// 没有元素
if (head == null) {
head = new Node(o);
tail = head;
head.next = null;
} else {
Node cur = new Node(o);
// 原来的尾节点next指向新节点
tail.next = cur;
// 新节点变成现在的尾节点
tail = cur;
// 将新尾节点的next置为null
tail.next = null;
}
size++;
}
public Object dequeue() {
if (head == null) {
throw new RuntimeException("没有队列元素");
}
Object ret = head.data;
head = head.next;
size--;
return ret;
}
public boolean empty() {
return size == 0;
}
/**
* 打印方式: 队首->队尾
* @return
*/
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer("(");
Node cur = head;
while (cur != null) {
stringBuffer.append(cur.data);
stringBuffer.append(",");
cur = cur.next;
}
if (stringBuffer.length() == 1) {
return stringBuffer.toString() + ")";
} else {
return stringBuffer.substring(0, stringBuffer.length() - 1) + ")";
}
}
public static void main(String[] args) {
LinkedQueue linkedQueue = new LinkedQueue();
linkedQueue.enqueue(1);
linkedQueue.enqueue(2);
linkedQueue.enqueue(3);
// (1,2,3)
System.out.println(linkedQueue);
linkedQueue.dequeue();
linkedQueue.dequeue();
// (3)
System.out.println(linkedQueue);
// Exception in thread "main" java.lang.RuntimeException: 没有队列元素
linkedQueue.dequeue();
linkedQueue.dequeue();
}
}
上面的代码和原来操作链表的博客非常相似,这里就不做赘述。参考:数据结-栈
2.3 循环队列
在基于数组的队列实现,可以看到每次出队之后需要将队列元素整体移动,这个是比较消耗性能的,有没有方式可以将解决这个问题呢?有,就向下面示意图一样,我们可以增加一个head的指针,控制数组下标为[head,tail]这段的数据作为队列元素。
但是还是存在一个问题:head前面的元素空间会遗留越来越多,即:队列的无效空间会一直增大,这个又需要如果解决呢?
通过上面的方式,就可以解决空间浪费的问题。
代码实现:
public class CircleArrayQueue {
private Object[] data;
private int maxSize; // 队列的最大长度
private int head; //
private int tail; // 指向尾元素的下一个位置
private int size; // 当前队列大小
public CircleArrayQueue(int maxSize) {
data = new Object[maxSize];
this.maxSize = maxSize;
}
public void enqueue(Object o) {
if (full()) {
throw new RuntimeException("已经达到队列的最大长度");
}
data[tail] = o;
tail = (tail + 1) % maxSize;
size++;
}
public Object dequeue() {
if (empty()) {
throw new RuntimeException("没有队列元素");
}
Object ret = data[head];
head = (head + 1) % maxSize;
size--;
return ret;
}
public boolean empty() {
return size == 0;
}
public boolean full() {
return size == maxSize;
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer("(");
int cur = head;
for (int i = 0; i < size ; i++) {
cur = (cur + i) % maxSize;
stringBuffer.append(data[cur]);
stringBuffer.append(",");
}
if (stringBuffer.length() == 1) {
return stringBuffer.toString() + ")";
} else {
return stringBuffer.substring(0, stringBuffer.length() - 1) + ")";
}
}
public static void main(String[] args) {
CircleArrayQueue circleArrayQueue = new CircleArrayQueue(3);
circleArrayQueue.enqueue(1);
circleArrayQueue.enqueue(2);
circleArrayQueue.enqueue(3);
// (1,2,1)
System.out.println(circleArrayQueue);
// 0
System.out.println(circleArrayQueue.head);
// 0
System.out.println(circleArrayQueue.tail);
// java.lang.RuntimeException: 已经达到队列的最大长度
// circleArrayQueue.enqueue(4);
circleArrayQueue.dequeue();
// 1
System.out.println(circleArrayQueue.head);
// 0
System.out.println(circleArrayQueue.tail);
circleArrayQueue.dequeue();
circleArrayQueue.dequeue();
// java.lang.RuntimeException: 没有队列元素
circleArrayQueue.dequeue();
}
}