一、普通循环队列
1、普通循环队列明细
循环队列是针对顺序队列中最大化利用内存空间的一种解决方法,可以解决当队列(数组)不可再插入新元素但队列的实际可用空间并未占满的问题。
相比普通的队列,多了个指向队头的索引,而且也是只能在头部删除元素,尾部插入元素。故删除元素的时候,该索引需要往后走一个位置(取模)。插入元素还是队尾插入(取模)。
public class CircleQueue<E> {
private int size = 0;
private int front;
private E[] elements;
private final int DEFAULT_CAPACITY = 10;
public CircleQueue() {
elements = (E[]) new Object[DEFAULT_CAPACITY];
front = 0;
}
public boolean isEmpty() {
return size == 0;
}
public void offer(E element) {
ensureCapacity(size + 1);
elements[(front + size) % elements.length] = element; //需要将数组中的索引转换为循环队列中的索引
size++;
}
public E remove() {
E element = elements[front];
elements[front] = null; //清空内存指针指向的对象空间
front = (front + 1) % elements.length; //需要将数组中的索引转换为循环队列中的索引
size--;
return element;
}
public E peek() {
return elements[front];
}
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //扩容为1.5倍
if (capacity > oldCapacity) {
E[] newArray = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newArray[i] = elements[(front + i) % oldCapacity]; //防止数据错乱, 将队列置为头->尾顺序
}
front = 0; //队列头重新指向0
elements = newArray;
System.out.println(oldCapacity + "扩容为" + newCapacity);
}
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("capacity=").append(elements.length).append(", size=").append(size);
str.append(", front=").append(front);
if (elements.length != 0) {
str.append(", [").append(elements[0]);
for (int i = 1; i < elements.length; i++) {
str.append(",").append(elements[i]);
}
str.append("]");
}
return str.toString();
}
public static void main(String[] args) {
CircleQueue<Integer> circleQueue = new CircleQueue<>();
//0 1 2 3 4 5 6 7 8 9
for (int i = 0; i < 10; i++)
circleQueue.offer(i);
//null null null null null 5 6 7 8 9
for (int i = 0; i < 5; i++)
circleQueue.remove();
//0 3 6 9 12 5 6 7 8 9
for (int i = 0; i < 5; i++)
circleQueue.offer(i * 3);
//5 6 7 8 9 0 3 6 9 12 15 18 21 null null
for (int i = 5; i < 8; i++)
circleQueue.offer(i * 3);
System.out.println(circleQueue);
while (!circleQueue.isEmpty()) {
System.out.print(circleQueue.remove() + " ");
}
}
}
2、测试结果
二、双向循环队列
1、双向循环队列明细
双向的循环队列不仅可以在头部删除元素还可以在头部添加元素,尾部也可以添加和删除元素,而且也有指向队头的索引。
循环链表索引需要进行映射,这里对映射函数进行了简单封装。索引映射的时候取模的运算比较慢,可采用加法来优化。
public class CircleDeque<E> {
private int size = 0;
private int front;
private E[] elements;
private final int DEFAULT_CAPACITY = 10;
public CircleDeque() {
elements = (E[]) new Object[DEFAULT_CAPACITY];
front = 0;
}
public boolean isEmpty() {
return size == 0;
}
public void offerFirst(E element) {
ensureCapacity(size + 1);
// int index = --front < 0 ? front + elements.length : front;
int index = front - 1 < 0 ? front - 1 + elements.length : getIndex(-1); //封装索引映射
elements[index] = element;
front = index;
size++;
}
public void offerLast(E element) {
ensureCapacity(size + 1);
// elements[(front + size) % elements.length] = element; //需要将数组中的索引转换为循环队列中的索引
elements[getIndex(size)] = element; //封装索引映射
size++;
}
public E removeFirst() {
E element = elements[front];
elements[front] = null; //清空内存指针指向的对象空间
// front = (front + 1) % elements.length; //需要将数组中的索引转换为循环队列中的索引
front = getIndex(1); //封装索引映射
size--;
return element;
}
public E removeLast() {
// int index = (front + size - 1) % elements.length;
int index = getIndex(size - 1); //封装索引映射
E element = elements[index];
elements[index] = null;
size--;
return element;
}
public E peekFirst() {
return elements[front];
}
public E peekLast() {
return elements[getIndex(this.size - 1)];
}
/**
* 索引映射(映射为循环队列中的索引)
*
* @param index
* @return
*/
private int getIndex(int index) {
//优化取模运算
return (index + front - elements.length < 0 ? index + front : index + front - elements.length);
// return (index + this.front) % elements.length;
}
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //扩容为1.5倍
if (capacity > oldCapacity) {
E[] newArray = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newArray[i] = elements[(front + i) % oldCapacity]; //防止数据错乱, 将队列置为头->尾顺序
}
front = 0; //队列头重新指向0
elements = newArray;
System.out.println(oldCapacity + "扩容为" + newCapacity);
}
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("capacity=").append(elements.length).append(", size=").append(size);
str.append(", front=").append(front);
if (elements.length != 0) {
str.append(", [").append(elements[0]);
for (int i = 1; i < elements.length; i++) {
str.append(",").append(elements[i]);
}
str.append("]");
}
return str.toString();
}
public static void main(String[] args) {
CircleDeque<Integer> circleDeque = new CircleDeque<>();
//4 3 2 1 0 100 101 102 103 104 105 106 7 6 5
//7 6 5 4 3 2 1 0 100 101 102 103 104 105 106 107 108 109 null null 9 8
for (int i = 0; i < 10; i++) {
circleDeque.offerFirst(i);
circleDeque.offerLast(100 + i);
}
//7 6 5 4 3 2 1 0 100 101 102 103 104 null null null null null null null 9 8
for (int i = 0; i < 5; i++)
circleDeque.removeLast();
System.out.println(circleDeque);
while (!circleDeque.isEmpty()) {
System.out.print(circleDeque.removeFirst() + " ");
}
}
}
2、测试结果
来源:CSDN
作者:cj1561435010
链接:https://blog.csdn.net/cj1561435010/article/details/104480101