个人博客:www.hellocode.top
⭐所有文章均在上方博客首发,其他平台同步更新
本文专栏:《数据结构与算法》
⚡如有问题,欢迎指正,一起学习~~
文章参考整理自小码哥的《恋上数据结构和算法》课程,图片转载自课程PPT,如有侵权,请联系删除~~
rear
): 只能从队尾添加
元素, 一般叫做enQueue
, 入队front
): 只能从队头移除
元素, 一般叫做deQueue
, 出队循环队列和循环双端队列使用并不多,但是可以学到新的思路以及动态数组的一些优化方法
size
、isEmpty
、clear
等方法,主要就是入队(enQueue
)和出队(deQueue
)操作了public class Queue<E> {
// 使用双向链表实现队列
private List<E> list = new DoubleLinkedList<>();
// 元素的数量
public int size();
// 是否为空
public boolean isEmpty();
// 入队
public void enQueue(E element);
// 出队
public E deQueue();
// 获取队列的头元素
public E front();
// 清空队列
public void clear();
}
实现思路和栈很相似,将(双向)链表作为队的成员变量,调用链表的方法实现队列
package top.hellocode.Queue;
import java.util.LinkedList;
import java.util.List;
/**
* @author HelloCode
* @site https://www.hellocode.top
* @date 2022年05月25日 20:14
*/
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);
}
// 清空队列
public void clear(){
list.clear();
}
}
public class Deque<E> {
private List<E> list = new DoubleLinkedList<>();
// 元素的数量
public int size();
// 队列是否为空
public boolean isEmpty();
// 从队尾入队
public void enQueueRear(E element);
// 从队头出队
public E deQueueFront();
// 从队头入队
public void enQueueFront(E element);
// 从队尾出队
public E deQueueRear();
// 获取队列的头元素
public E front();
// 获取队列的尾元素
public E rear();
// 清空队列
public void clear();
}
package top.hellocode.Queue;
import top.hellocode.List.LinkedList;
import top.hellocode.List.List;
/**
* @author HelloCode
* @site https://www.hellocode.top
* @date 2022年05月25日 20:20
*/
public class Deque<E> {
private List<E> list = new LinkedList<>();
// 元素的数量
public int size() {
return list.size();
}
// 队列是否为空
public boolean isEmpty() {
return list.isEmpty();
}
// 从队尾入队
public void enQueueRear(E element) {
list.add(element); // 链表默认添加到表尾
}
// 从队头出队
public E deQueueFront() {
return list.remove(0); // 删除表头元素并返回
}
// 从队头入队
public void enQueueFront(E element) {
list.add(0, 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);
}
// 清空队列
public void clear(){
list.clear();
}
}
这里只有队头指针front,并没有队尾指针rear,是因为队尾可以通过
front + size - 1
计算出来,没必要浪费多余的空间来记录队尾
public class CircleQueue<E> {
// 记录第0个元素的索引
private int front;
// 当前队列存储的元素个数
private int size;
// 用来存储元素的数组
private E[] elements;
// 当前队列存储的元素数量
public int size();
// 当前队列是否为空
public boolean isEmpty();
// 入队
public void enQueue(E element);
// 出队
public E deQueue();
// 查看索引为0的元素
public E front();
}
构造方法
public class CircleQueue<E> {
// 记录第0个元素的索引
private int front;
// 当前队列存储的元素个数
private int size;
// 用来存储元素的容器
private E[] elements;
private static final int DEFAULT_CAPACITY = 10; // 数组默认容量
public CircleQueue(){ // 构造方法,使用默认容量为数组开辟空间
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
}
使用数组作为容器,构造方法中为数组开辟空间,默认容量为10
入队
自动扩容
44,55,66
,而不是66,44,55
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length; // 旧容量
if(oldCapacity >= capacity) return; // 不需要扩容就返回
// 计算新容量
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为1.5倍,不理解这个算法可以查看动态数组文章
E[] newElements = (E[]) new Object[newCapacity]; // 创建新数组
for (int i = 0; i < size; i++) { // 移动元素
newElements[i] = elements[index(i)];
}
elements = newElements;
// 重置front
front = 0;
}
索引计算
(front + index) % elements.length
预期入队索引
= 第0个元素索引
+ 当前队列元素个数
预期入队索引
大于等于数组长度
,实际入队索引
= 预期入队索引
- 数组长度
预期入队索引
小于数组长度
,实际入队索引
= 预期入队索引
// 索引计算
private int index(int index) {
// return (front + index) % elements.length;
// 下面两行代码只是为了优化 % 的运算,效果和上方代码类似
index += front;
return index - (index >= elements.length ? elements.length : 0);
}
入队
// 入队
public void enQueue(E element) {
// 扩容
ensureCapacity(size + 1);
elements[index(size)] = element;
size++; // size自增
}
出队
// 出队
public E deQueue() {
// 获取出队元素
E frontElement = elements[front];
// 将front位置元素置空
elements[front] = null;
// 更新front
front = index(1);
size--;
return frontElement;
}
完整代码
package top.hellocode.Queue;
/**
* @author HelloCode
* @site https://www.hellocode.top
* @date 2022年05月25日 20:26
*/
public class CircleQueue<E> {
// 记录第0个元素的索引
private int front;
// 当前队列存储的元素个数
private int size;
// 用来存储元素的容器
private E[] elements;
private static final int DEFAULT_CAPACITY = 10; // 数组默认容量
public CircleQueue() { // 构造方法,使用默认容量为数组开辟空间
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
// 当前队列存储的元素数量
public int size() {
return size;
}
// 当前队列是否为空
public boolean isEmpty() {
return size == 0;
}
// 入队
public void enQueue(E element) {
// 扩容
ensureCapacity(size + 1);
elements[index(size)] = element;
size++; // size自增
}
// 自动扩容
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length; // 旧容量
if(oldCapacity >= capacity) return; // 不需要扩容就返回
// 计算新容量
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为1.5倍,不理解这个算法可以查看动态数组文章
E[] newElements = (E[]) new Object[newCapacity]; // 创建新数组
for (int i = 0; i < size; i++) { // 移动元素
newElements[i] = elements[index(i)];
}
elements = newElements;
// 重置front
front = 0;
}
// 索引计算
private int index(int index) {
// return (front + index) % elements.length;
// 下面两行代码只是为了优化 % 的运算,效果和上方代码类似
index += front;
return index - (index >= elements.length ? elements.length : 0);
}
// 出队
public E deQueue() {
// 获取出队元素
E frontElement = elements[front];
// 将front位置元素置空
elements[front] = null;
// 更新front
front = index(1);
size--;
return frontElement;
}
// 查看索引为0的元素
public E front() {
return elements[front];
}
// 清空
public void clear(){
for (int i = 0; i < size; i++){
elements[i] = null;
}
front = 0;
}
}
public class CircleDeque<E> {
// 存储队头(首元素)元素的下标
private int front;
private int size;
private E[] elements;
private static final int DEFAULT_CAPACITY = 10;
public CircleDeque() {
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
// 元素的数量
public int size();
// 队列是否为空
public boolean isEmpty();
// 从队尾入队
public void enQueueRear(E element);
// 从队头出队
public E deQueueFront();
// 从队头入队
public void enQueueFront(E element);
// 从队尾出队
public E deQueueRear();
// 获取队列的头元素
public E front();
// 获取队列的尾元素
public E rear();
}
index + elements.length
// 索引计算
private int index(int index) {
// return (front + index) % elements.length;
// 下面两行代码只是为了优化 % 的运算,效果和上方代码类似
index += front;
if(index < 0) return index + elements.length;
return index - (index >= elements.length ? elements.length : 0);
}
从队头入队时,因为改变了队头位置,所以需要更新front
同理,从队尾出队时,因为没改变队头位置,所以不需要更新front
package top.hellocode.Queue;
/**
* @author HelloCode
* @site https://www.hellocode.top
* @date 2022年05月25日 20:27
*/
public class CircleDeque<E> {
// 存储队头(首元素)元素的下标
private int front;
private int size;
private E[] elements;
private static final int DEFAULT_CAPACITY = 10;
public CircleDeque() {
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
// 元素的数量
public int size(){
return size;
}
// 队列是否为空
public boolean isEmpty(){
return size == 0;
}
// 从队尾入队
public void enQueueRear(E element){
ensureCapacity(size + 1);
elements[index(size)] = element;
size++;
}
// 从队头出队
public E deQueueFront(){
E frontElement = elements[front];
elements[front] = null;
front = index(1);
size--;
return frontElement;
}
// 从队头入队
public void enQueueFront(E element){
ensureCapacity(size + 1);
front = index(-1);
elements[front] = element;
size++;
}
// 从队尾出队
public E deQueueRear(){
E rearElement = elements[size - 1];
elements[size - 1] = null;
size--;
return rearElement;
}
// 获取队列的头元素
public E front(){
return elements[index(size - 1)];
}
// 获取队列的尾元素
public E rear(){
return elements[index(front)];
}
// 索引计算
private int index(int index) {
// return (front + index) % elements.length;
// 下面两行代码只是为了优化 % 的运算,效果和上方代码类似
index += front;
if(index < 0) return index + elements.length;
return index - (index >= elements.length ? elements.length : 0);
}
// 自动扩容
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length; // 旧容量
if(oldCapacity >= capacity) return; // 不需要扩容就返回
// 计算新容量
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为1.5倍,不理解这个算法可以查看动态数组文章
E[] newElements = (E[]) new Object[newCapacity]; // 创建新数组
for (int i = 0; i < size; i++) { // 移动元素
newElements[i] = elements[index(i)];
}
elements = newElements;
// 重置front
front = 0;
}
}
整体上和循环队列类似,只需要注意一下出入队操作和index索引计算方法即可