……
一、队列
队列(Queue)是一种特殊的线性表,只能在头尾两端进行操作。先进先出的原则,First In First Out,FIFO。
队尾(rear):只能从队尾添加元素,一般叫做入队(enQueue)。
队头(front):只能从队头移除元素,一般叫做出队(deQueue)。
接口设计
队列的内部实现可以利用动态数组、链表实现,但是优先使用双向链表,因为队列主要是往头尾操作元素,复杂度是O(1)
。
接口:
1 | int size(); // 元素的数量 |
代码:
1 | public class Queue<E> { |
Java官方队列源码:
java.util.Queue
二、双端队列
双端队列(deque:double ended queue)是能在头尾两端添加、删除的队列。
接口设计
接口:
1 | int size(); // 元素的数量 |
代码(基于链表):
1 | public class Deque<E> { |
三、循环队列
其实队列底层也可以使用动态数组实现,并且各项接口也可以优化到O(1)
的时间复杂度,这个用数组实现并且优化之后的队列也叫做:循环队列(Circle Queue)。
核心:指向头部的指针(front
指向的位置就是队头,注意扩容时的对应关系)。
关键代码:索引转换(i + front) % elements.length
代码:
1 | public class CircleQueue<E> { |
四、循环双端队列
循环双端队列是可以进行两端添加、删除操作的循环队列。
尾部不需要指针,只需要头部指针索引加上元素数量模上数组长度。
代码:
1 | public class CircleDeque<E> { |
五、运算符优化
尽量避免使用乘*
、除/
、模%
、浮点数运算,效率低下。
模运算符:
假设有n
,m
两个数,且n >= 0, m > 0
,当两个数相差在两倍(不含)以内时(n < 2m
):
n >= m
,n % m
的运算结果就是n - m
n < m
,n % m
的运算结果就是n
- 结论(已知前提条件下):
n % m
等价于n - (n >= m ? m : 0)
例如上面代码中的模运算符:
优化前:
1 | private int index(int index) { |
优化后:
1 | private int index(int index) { |
六、LeetCode
用栈实现队列
题目:
题解:
将一个栈当作输入栈,用于压入push
传入的数据;另一个栈当作输出栈,用于pop
和peek
操作。
每次pop
或peek
时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。
代码:
1 | class MyQueue { |