我的Github地址
小码哥《恋上数据结构与算法》笔记
极客时间《iOS开发高手课》笔记
iOS大厂面试高频算法题总结
iOS面试资料汇总
参考:小码哥数据结构与算法(六): 队列
数据结构和算法动态可视化
一、队列
队列是一种特殊的线性表,只能在头尾两端操作。
- 队尾(rear): 只能从
队尾添加元素, 一般叫做enQueue, 入队。
- 对头(front): 只能从
队头移除元素, 一般叫做deQueue, 出队。
- 先进先出的原则,First In First Out,FIFO。
- 队列的内部实现可以使用
动态数组或双向链表实现。
- 优先使用
双向链表,因为队列主要是往头尾操作元素。

二、队列的接口设计
public class Queue<E> {
private List<E> list = new LinkedList<>();
public int size();
public boolean isEmpty();
public void enQueue(E element);
public E deQueue();
public E front();
}
三、队列的实现
public class Queue<E> {
private List<E> list = new LinkedList<>();
public int size() {
return list.size();
}
public boolean isEmpty() {
return list.isEmpty();
}
public void enQueue(E element) {
list.add(element);
}
public E deQueue() {
return list.remove(0);
}
public E front() {
return list.get(0);
}
}
四、leetcode算法题
用栈实现队列

public class _232_用栈实现队列 {
private Stack<Integer> inStack;
private Stack<Integer> outStack;
public _232_用栈实现队列() {
inStack = new Stack<>();
outStack = new Stack<>();
}
public void push(int x) {
inStack.push(x);
}
public int pop() {
checkOutStack();
return outStack.pop();
}
public int peek() {
checkOutStack();
return outStack.peek();
}
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}
private void checkOutStack() {
if (outStack.isEmpty()) {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
}
}
五、双端队列(Deque)
- 双端队列是能在
头尾两端添加、删除的队列。

六、双端队列的接口设计&实现
public class Deque<E> {
private List<E> list = new LinkedList<>();
public int size() {
return list.size();
}
public boolean isEmpty() {
return list.isEmpty();
}
public E deQueueFront() {
return list.remove(0);
}
public void enQueueFront(E element) {
list.add(0, element);
}
public void enQueueRear(E element) {
list.add(element);
}
public E deQueueRear() {
return list.remove(list.size() - 1);
}
public E front() {
return list.get(0);
}
public E rear() {
return list.get(list.size() - 1);
}
}
七、循环队列(Circle Queue)
- 请阅读小码哥《恋上数据结构与算法》笔记(一):动态数组,第五条:ArrayList能否进一步优化。
- 队列内部实现也可以用
动态数组实现,并且将各项接口优化到O(1)的时间复杂度,
这个用数组实现并优化之后的队列就叫做: 循环队列。

- 使用一个索引变量
front控制第0个元素所在位置。
- 每一次
出栈,就将front位置的元素取出并删除,然后front向后+1。
- 每一次
入栈,都根据front和当前元素数量计算出入栈元素应该存入的索引,然后将元素存入到数组对应索引的位置上。
八、循环队列的接口设计
public class CircleQueue<E> {
private int front;
private int size;
private E[] elements;
public int size();
public boolean isEmpty();
public void enQueue(E element);
public E deQueue();
public E front();
}
九、循环队列的实现
1、构造方法
- 如果构建的数组空间小于默认空间,则会以默认空间构建数组。
public class ArrayList<E> {
private E[] elements;
private static final int DEFAULT_CAPACITY = 10;
public CircleQueue() {
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
}
2、入队
- 入队前需要考虑两个问题:
队列是否需要扩容和计算入队实际索引。
2.1、数组扩容
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) return;
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[index(i)];
}
elements = newElements;
front = 0;
}
2.2、索引计算
预期入队索引 = 第0个元素索引 + 当前队列元素个数。
- 如果
预期入队索引大于等于数组长度,实际入队索引 = 预期入队索引 - 数组长度。
- 如果
预期入队索引小于数组长度,实际入队索引 = 预期入队索引。
private int index(int index) {
index += front;
return index - (index >= elements.length ? elements.length : 0);
}
public void enQueue(E element) {
ensureCapacity(size + 1);
elements[index(size)] = element;
size++;
}
3、出队
public E deQueue() {
E frontElement = elements[front];
elements[front] = null;
front = index(1);
size--;
return frontElement;
}
十、循环双端队列