一、普通循環隊列
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() + " ");
}
}
}