具体代码在 Queue , 欢迎
star
跳转到目录
队列
是一种特殊的线性表
,只能在头尾两端
操作。rear
): 只能从队尾添加
元素, 一般叫做enQueue
, 入队。front
): 只能从队头移除
元素, 一般叫做deQueue
, 出队。FIFO
。动态数组
或双向链表
实现。头尾操作
元素。跳转到目录
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();
}
跳转到目录
/**
* Description: 队列的实现(根据双向链表实现)
*
* @author zygui
* @date 2020/4/11 21:08
*/
public class Queue<E> {
private List<E> list = new DoubleLinkedList<>();
// 元素的数量
public int size() {
return list.size();
}
// 队列是否为空
public boolean isEmpty() {
return list.inEmpty();
}
// 入队
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();
}
}
/**
* Description: 栈实现队列 https://leetcode-cn.com/problems/implement-queue-using-stacks/
*
* @author zygui
* @date 2020/4/11 21:27
*/
public class _232_用栈实现队列 {
private Stack<Integer> inStack = new Stack<>();
private Stack<Integer> outStack = new Stack<>();
/**
* Initialize your data structure here.
*/
public _232_用栈实现队列() {
}
// 入队
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 outStack.isEmpty() && inStack.isEmpty();
}
private void checkOutStack() {
if (outStack.isEmpty()) {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
}
}
跳转到目录
头尾
两端添加
、删除
的队列。跳转到目录
/**
* Description: 双端队列的实现
*
* -------------------
* 队尾(rear) 队头(front)
* -------------------
*
* @author zygui
* @date 2020/4/11 21:49
*/
public class Deque<E> {
private List<E> list = new DoubleLinkedList<>();
// 元素的数量
public int size() {
return list.size();
}
// 队列是否为空
public boolean isEmpty() {
return list.inEmpty();
}
// 从队尾入队
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);
}
}
测试
/**
* Description: 测试类
*
* @author zygui
* @date 2020/4/11 21:07
*/
public class Main {
public static void main(String[] args) {
Deque<Integer> deque = new Deque<>();
deque.enQueueFront(11); // 从队头入队
deque.enQueueFront(22);
deque.enQueueRear(33); // 从队尾入队
deque.enQueueRear(44);
/* 44 33 11 22 */
while (!deque.isEmpty()) {
// 如果双端队列不为空,则从队头出队
//System.out.println(deque.deQueueFront()); // 22, 11, 33, 44
// 如果双端队列不为空,则从队尾出队
System.out.println(deque.deQueueRear()); // 44, 33, 11, 22
}
}
}
跳转到目录
思路
: 请先看《恋上数据结构与算法》笔记(一):动态数组 ArrayList能否进一步优化的部分。动态数组
实现,并且将各项接口优化到O(1)
的时间复杂度, 这个用数组实现并优化之后的队列就叫做: 循环队列
。front
来记录首元素
的下标出栈
,就将front
位置的元素取出并删除,然后front
向后+1
。入栈
,都根据front
和当前元素数量
计算出入栈元素应该存入的索引
,然后将元素存入到数组对应索引
的位置上。跳转到目录
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();
}
跳转到目录
跳转到目录
private static final int DEFAULT_CAPACITY = 10;
public CircleQueue() {
elements = (E[]) new Object[DEFAULT_CAPACITY];
}
跳转到目录
入队前需要考虑两个问题:队列是否需要扩容
和计算入队实际索引
。
根据front
和当前元素数量
计算出入栈元素应该存入的索引
,然后将元素存入到数组对应索引
的位置上。
public void enQueue(E element) {
// 计算出入队的实际位置
elements[(front + size) % elements.length] = element;
size++;
}
跳转到目录
/**
* 保证要有capacity的容量
*
* @param capacity
*/
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[(i + front) % elements.length];
}
elements = newElements;
// 重置front
front = 0;
}
跳转到目录
预期入队索引
= 第0个元素索引
+ 当前队列元素个数
。预期入队索引
大于等于数组长度
,实际入队索引
= 预期入队索引
- 数组长度
。预期入队索
引小于数组长度
,实际入队索引
= 预期入队索引
。/**
* 将之前的索引转换到现在循环数组的真实索引
*
* @param index
* @return
*/
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++;
}
跳转到目录
front
。public E deQueue() {
// 获取出队元素
E frontElement = elements[front];
// 将索引位置致空
elements[front] = null;
// 更新front
//front++; // 需要计算front的位置,不然会指向最后不存在的位置
//front = (front + 1) % elements.length;
front = index(1);
// size减一
size--;
// 返回出队元素
return frontElement;
}
跳转到目录
两端
添加、删除操作的循环队列
。具体代码
/**
* Description: 循环双端队列
*
* @author zygui
* @date 2020/4/14 08:07
*/
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);
/*
if(front - 1 < 0){
front += elements.length;
}
front = front - 1;
elements[front-1] = element;
*/
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() {
int rearIndex = index(size - 1);
E rear = elements[rearIndex];
elements[rearIndex] = null;
size--;
return rear;
}
// 获取队列的头元素
public E front() {
return elements[front];
}
// 获取队列的尾元素
public E rear() {
// 队头下标+size-1就是队尾下标
// return elements[(front + size - 1) % elements.length];
return elements[index(size - 1)];
}
/**
* 保证要有capacity的容量
*
* @param capacity
*/
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍
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
front = 0;
}
/**
* 将之前的索引转换到现在循环数组的真实索引
*
* @param index
* @return
*/
private int index(int index) {
index += front;
if (index < 0)
return index + elements.length;
//return index % elements.length;
return index - (index >= elements.length ? elements.length : 0);
}
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("capacity=").append(elements.length)
.append(" size=").append(size)
.append(" front=").append(front)
.append(", [");
for (int i = 0; i < elements.length; i++) {
if (i != 0) {
string.append(", ");
}
string.append(elements[i]);
}
string.append("]");
return string.toString();
}
}
测试
/**
* Description: 测试类
*
* @author zygui
* @date 2020/4/11 21:07
*/
public class Main {
public static void main(String[] args) {
CircleDeque<Integer> queue = new CircleDeque<>();
// 头 5 4 3 2 1 100 101 102 103 104 105 106 8 7 6 尾
// 头 8 7 6 5 4 3 2 1 100 101 102 103 104 105 106 107 108 109 null null 10 9 尾
for (int i = 0; i < 10; i++) {
queue.enQueueFront(i + 1);
queue.enQueueRear(i + 100);
}
System.out.println(queue);
// 头 null 7 6 5 4 3 2 1 100 101 102 103 104 105 106 null null null null null null null 尾
for (int i = 0; i < 3; i++) {
queue.deQueueFront();
queue.deQueueRear();
}
System.out.println(queue);
// 头 11 7 6 5 4 3 2 1 100 101 102 103 104 105 106 null null null null null null 12 尾
queue.enQueueFront(11);
queue.enQueueFront(12);
System.out.println(queue);
while (!queue.isEmpty()) {
System.out.print(queue.deQueueFront() + " ");
}
}
}