MyQueue< T >
在 MyStack 的基础上,仿写了MyQueue。其中基于Array实现的Queue即是循环队列,又实现了队列满后自动增加容量。
人生苦短,未言自明。
1. MyLinkedQueue
链表实现更简单,先写链表的实现。双链表的实现是显然的,单链表在实现时,注意到enqueue中的tail。先令tail引用的节点的next链指向新建的节点,然后再让tail去引用新建的节点,就实现了tail变为新增的节点。tail是对象变量,这里充分利用了其引用的特性——我既然单链找不到tail前的节点,那tail就改变自己,再以tail的身份修改了当前的链后,就“不讲道义的变节”,去引用其后的节点。
package MyQueue;
import java.util.NoSuchElementException;
/**
* 2021/7/20.
*
* @author CatInCoffee.
*/
public class MyLinkedQueue<T> {
private static class Node<T> {
public T data;
public Node<T> next;
public Node(T paraData) {
data = paraData;
next = null;
} // of Node's first constructor
public Node(T paraData, Node<T> paraNext) {
data = paraData;
next = paraNext;
} // of Node's second constructor
} // of class Node<T>
private Node<T> frontMarker = new Node<>(null);
private Node<T> rear;
private int theSize;
// *****************************************************************************************
public MyLinkedQueue() {
clear();
}// Of the first constructor
public void clear() {
rear = frontMarker;
theSize = 0;
}// of clear()
// O(N)
public MyLinkedQueue(T[] paraArray) {
this();
for (int i = 0; i < paraArray.length; i++) {
enqueue(paraArray[i]);
} // Of for i
}// Of the second constructor
// *****************************************************************************************
public int size() {
return theSize;
}// of size()
public boolean isEmpty() {
return size() == 0;
}// of isEmpty()
// *****************************************************************************************
public boolean enqueue(T x) {
Node<T> newNode = new Node<>(x);
rear.next = newNode;
rear = newNode;
theSize++;
return true;
}// of enqueue(T)
public T dequeue() {
if (!isEmpty()) {
T old = frontMarker.next.data;
frontMarker.next = frontMarker.next.next;
if(frontMarker.next == null) {
rear = frontMarker;
} // of if
theSize--;
return old;
} else {
throw new NoSuchElementException();
} // of if-else
}// of dequeue()
// *****************************************************************************************
public String toString() {
String s = "It's an empty queue.";
if (!isEmpty()) {
s = "{ ";
Node<T> temp = frontMarker.next;
while (temp.next != null) {
s += temp.data + ", ";
temp = temp.next;
} // of while
s += temp.data + " }";
// for (Node<T> temp = frontMarker.next; temp != null; temp = temp.next) {
// s += temp.data + " ";
// } // of for temp
// s += "}";
} // of if
return s;
}// of toString()
public static void main(String args[]) {
Character[] ch = { 't', 'e', 's', 't' };
MyLinkedQueue<Character> tempQueue = new MyLinkedQueue<>(ch);
System.out.println("Initialized, the list is: " + tempQueue.toString());
Character tempValue;
for (int i = 0; i < 5; i++) {
try {
tempValue = tempQueue.dequeue();
System.out.println("Looped dequeue: " + tempValue + ", the new queue is: " + tempQueue);
} catch (NoSuchElementException e) {
System.out.println("Nothing to dequeue.");
tempQueue.clear();
} // of try-catch
} // Of for i
for (char c = 'a'; c < 'f'; c++) {
tempQueue.enqueue(Character.valueOf(c));
} // Of for i
System.out.println("Enqueued a to e successively, the queue is: " + tempQueue);
tempQueue.dequeue();
tempQueue.dequeue();
System.out.println("Dequeued twice, the queue is: " + tempQueue);
for (char c = 'f'; c < 'i'; c++) {
tempQueue.enqueue(Character.valueOf(c));
} // Of for i
System.out.println("Enqueued, the queue is: " + tempQueue);
System.out.println();
for (int i = 0; i < 4; i++) {
tempValue = tempQueue.dequeue();
System.out.println("Looped dequeue: " + tempValue + ", the new queue is: " + tempQueue);
} // Of for i
tempQueue.enqueue('?');
tempQueue.enqueue('!');
System.out.println("Enqueued: '?' '!', the queue is: " + tempQueue);
} // Of main
}// of class MyLinkedQueue<T>
结果:
Initialized, the list is: { t, e, s, t }
Looped dequeue: t, the new queue is: { e, s, t }
Looped dequeue: e, the new queue is: { s, t }
Looped dequeue: s, the new queue is: { t }
Looped dequeue: t, the new queue is: It's an empty queue.
Nothing to dequeue.
Enqueued a to e successively, the queue is: { a, b, c, d, e }
Dequeued twice, the queue is: { c, d, e }
Enqueued, the queue is: { c, d, e, f, g, h }
Looped dequeue: c, the new queue is: { d, e, f, g, h }
Looped dequeue: d, the new queue is: { e, f, g, h }
Looped dequeue: e, the new queue is: { f, g, h }
Looped dequeue: f, the new queue is: { g, h }
Enqueued: '?' '!', the queue is: { g, h, ?, ! }
2. MyCircularQueue
这是一个基于数组的、实现了自动扩容和循环存储的循环队列。
自动扩容和循环存储都利用了一个功能,那就是 ensureCapacity(int),即保证容量。
首先怎么扩容?
一般来说,给定一个常数用于给出初始数组大小,如果存放的队列元素个数theSize和数组大小length相同了,那么创建一个大小翻倍的数组,并将原数组拷贝进新数组。
那在循环存储的时候,队头front在数组中间时enqueue队满了,像贪吃蛇一样尾碰到头了,这时候该怎么扩容?
ensureCapacity(int)方法就是实现上面的功能(同时可用于初始化),参数如果不小于给定数组规模,那么创建参数大小的数组,并 有条件 的复制。该条件是基于循环数组的特性而产生的,因为循环数组队头front可以为任意位置,所以不能直接复制,而是从front开始;同时数组下标超过length后,要%求模回到循环的前面。特别的,复制扩容后要 重置队头和队尾的位置。
package MyQueue;
import java.util.NoSuchElementException;
/**
* 2021/7/20.
*
* @author CatInCoffee.
*/
public class MyCircularQueue<T> {
private static final int DEFAULT_CAPACITY = 5;
// Sometimes we don't use 'theSize', but it helps to make the code more clear
// while it costs little.
private T[] theItems;
private int theSize;
private int front;
private int rear;
// *****************************************************************************************
public MyCircularQueue() {
clear();
} // Of the first constructor
public void clear() {
theSize = 0;
ensureCapacity(DEFAULT_CAPACITY);
} // of clear()
// O(N)
public MyCircularQueue(T[] paraArray) {
this();
rear = paraArray.length - 1;
theSize = paraArray.length;
theItems = (T[]) (new Object[DEFAULT_CAPACITY]);
for (int i = 0; i < paraArray.length; i++) {
theItems[i] = paraArray[i];
// enqueue(paraArray[i]);
} // Of for i
} // Of the second constructor
// O(N)
private void ensureCapacity(int newCapacity) {
if (newCapacity < DEFAULT_CAPACITY || newCapacity < size()) {
return;
} // if
T[] old = theItems;
theItems = (T[]) new Object[newCapacity];
for (int i = 0; i < size(); i++) {
theItems[i] = old[(front + i) % size()];
} // of for i
front = 0;
rear = size() - 1;
} // of ensureCapacity(int)
// *****************************************************************************************
public int size() {
return theSize;
} // of size()
public boolean isEmpty() {
return size() == 0;
} // of isEmpty()
// *****************************************************************************************
public boolean enqueue(T x) {
if (theItems.length == size()) {
ensureCapacity(2 * size() + 1);
} // of if
rear++;
if (rear == theItems.length) {
rear = 0;
} // of if
theItems[rear] = x;
// The codes above equal to the next one.
// theItems[(rear++ == theItems.length) ? 0 : rear] = x;
theSize++;
return true;
}// of enqueue(T)
public T dequeue() {
if (!isEmpty()) {
if (front == theItems.length) {
front = 0;
} // of if
T old = theItems[front++];
theSize--;
return old;
} else {
System.out.print(" !!!Nothing to dequeue!!! ");
clear();
return null;
// throw new NoSuchElementException();
} // of if-else
}// of dequeue()
// *****************************************************************************************
public String toString() {
String s = "An empty queue.";
if (!isEmpty()) {
s = "{ ";
for (int i = front; i <= (front > rear ? (rear + theItems.length) : rear); i++) {
s += theItems[i % theItems.length] + " ";
} // of for temp
s += "}";
} // of if
return s;
}// of toString()
public int getLength() {
return theItems.length;
}// of getLength()
public static void main(String args[]) {
Character[] ch = { 't', 'e', 's', 't' };
MyCircularQueue<Character> tempQueue = new MyCircularQueue<>(ch);
System.out.println("Initialized, the list is: " + tempQueue.toString());
Character tempValue;
for (int i = 0; i < 5; i++) {
tempValue = tempQueue.dequeue();
System.out.println("Looped dequeue: " + tempValue + ", the new queue is: " + tempQueue);
// try {
// tempValue = tempQueue.dequeue();
// System.out.println("Looped dequeue: " + tempValue + ", the new queue is: " + tempQueue);
// } catch (NoSuchElementException e) {
// System.out.println("Nothing to dequeue.");
// tempQueue.clear();
// } // of try-catch
} // Of for i
for (char c = 'a'; c < 'f'; c++) {
tempQueue.enqueue(Character.valueOf(c));
} // Of for i
System.out.println("Enqueued a to e successively, the queue is: " + tempQueue);
System.out.println("The length of the array to store the queue is: " + tempQueue.getLength() + "\n");
tempQueue.dequeue();
tempQueue.dequeue();
System.out.println("Dequeued twice, the queue is: " + tempQueue);
for (char c = 'f'; c < 'i'; c++) {
tempQueue.enqueue(Character.valueOf(c));
} // Of for i
System.out.println("Enqueued, the queue is: " + tempQueue);
System.out.println("The length of the array to store the queue is: " + tempQueue.getLength());
System.out.println();
for (int i = 0; i < 4; i++) {
tempValue = tempQueue.dequeue();
System.out.println("Looped dequeue: " + tempValue + ", the new queue is: " + tempQueue);
} // Of for i
tempQueue.enqueue('?');
tempQueue.enqueue('!');
System.out.println("Enqueued: '?' '!', the queue is: " + tempQueue);
} // Of main
}// of class MyCircularQueue<T>
结果:
Initialized, the list is: { t e s t }
Looped dequeue: t, the new queue is: { e s t }
Looped dequeue: e, the new queue is: { s t }
Looped dequeue: s, the new queue is: { t }
Looped dequeue: t, the new queue is: An empty queue.
!!!Nothing to dequeue!!! Looped dequeue: null, the new queue is: An empty queue.
Enqueued a to e successively, the queue is: { a b c d e }
The length of the array to store the queue is: 5
Dequeued twice, the queue is: { c d e }
Enqueued, the queue is: { c d e f g h }
The length of the array to store the queue is: 11
Looped dequeue: c, the new queue is: { d e f g h }
Looped dequeue: d, the new queue is: { e f g h }
Looped dequeue: e, the new queue is: { f g h }
Looped dequeue: f, the new queue is: { g h }
Enqueued: '?' '!', the queue is: { g h ? ! }