版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons)
前言:
想必大家都有过在车站排队买票的经验吧,先来的排在前面,后来的依次往后排,当排在最前面的买到票则退出队列,这时排在后面的则跟上。其实这跟数据结构中的队列是同一个原理,先进先出。
在实际应用中,不止车站的排队,几乎针对所有有限的资源基本都运用了这一原理。例如,当线程池中的资源被用完了,那么当外部再申请线程时,线程池便可以采用排队的策略来处理该申请。
对于要支持多少的申请进行排队申请则取决于我们采用的是什么数据结构来实现队列了。
一. 什么是队列
队列是一种先进先出的线性结构;
其与栈类似也是一种逻辑结构,也是一种操作受限的线性数据结构,都可通过数组实现,也可通过链表实现。
其中通过数组实现的队列叫顺序队列,通过链表实现的队列叫链式队列。
队列与栈类似也同样也只有两个操作,一个是入队 enqueue(),另一个是出队 dequeue();其中入队是在队尾追加一个元素,出队则是从尾头移出一个元素。操作简易图如下所示:
队列的分类:
链式队列与顺序队列的区别:
二. 队列的实际应用
应用一:线程池、数据库连接池等等有限资源的分配策略都与队列相关;通俗地来说对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现。
应用二:网络爬虫将待分析的网络对应的URL写入队列然后按照队列FIFO的方式进行解析。
应用三:类似“生产者与消费者”的模式,如消息队列,对应的实例有KafKa。
应用四:并发队列、阻塞队列。基于数组的循环队列利用CAS原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。
三. 队列的代码实现
1. 数组实现(顺序队列)
public class ArrayQueue {
// 存储数据的数组
private String[] items;
// 记录数组容量
private int n;
private int size;
// head记录队头索引,tail记录队尾索引
private int head = 0;
private int tail = 0;
// 申请一个指定容量的队列
public ArrayQueue(int capacity) {
items = new String[capacity];
n = capacity;
}
/*
* 入队: 1.堆满的时,入队失败 1.1频繁出入队,造成数组使用不连续 1.2在入队的时候,集中触发进行数据搬移
* 2.在末尾插入数据,注意tail指向队尾元素的索引+1
*/
public boolean enqueue(String item) {
// 表示队满
if (head == 0 && tail == n)
return false;
// 表示需要数据搬移
else if (head != 0 && tail == n) {
for (int i = head; i < tail; i++) {
items[i - head] = items[i];
}
head = 0;
tail = tail - head;
}
// 将数据加入队列
items[tail++] = item;
size++;
return true;
}
// 出队:1.队空时,出队失败;2.出队,head索引+1
public String dequeue() {
String res = null;
if (head == tail)
return res;
res = items[head++];
size--;
return res;
}
}
2. 循环队列(基于数组)
public class LoopArrayQueue {
// 存储数据的数组
private String[] items;
// 记录数组容量
private int n;
private int size = 0;
// head记录队头索引,tail记录队尾索引
private int head = 0;
private int tail = 0;
// 申请一个指定容量的队列
public LoopArrayQueue(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;
size++;
return true;
}
// 出队:关键在于队空的条件
public String dequeue() {
String res = null;
if (head == tail)
return res;
res = items[head];
head = (head + 1) % n;
size--;
return res;
}
}
3. 链表实现(链式队列)
public class LinkedQueue {
// 定义一个节点类
private class Node {
String value;
Node next;
}
// 记录队列元素个数
private int size = 0;
// head指向队头结点,tail指向队尾节点
private Node head;
private Node tail;
// 申请一个队列
public LinkedQueue() {
}
// 入队
public boolean enqueue(String item) {
Node newNode = new Node();
newNode.value = item;
if (size == 0)
head = newNode;
else
tail.next = newNode;
tail = newNode;
size++;
return true;
}
// 出队
public String dequeue() {
String res = null;
if (size == 0)
return res;
if (size == 1)
tail = null;
res = head.value;
head = head.next;
size--;
return res;
}
}
注:该系列博文为笔者学习《数据结构与算法之美》的个人学习笔记小结