与栈相似的线性结构还有一种,那就是队列。
栈遵循后进先出(Last In First Out, LIFO)原则,而队列则恰好相反,遵循先进先出(First In First Out, FIFO)原则。
与栈相同,从存储结构来对队列进行划分,队列可以分为两类:
- 顺序队列结构:用一组地址连续的内存单元依次保存队列中的数据。
- 链式队列结构:用链表的形式保存队列中的数据。
如上图所示,队列是两端操作,入队列的一端为队尾,而出队列的一端为队头。一般的队列的基本操作有两个:
- 入队列(Enter):将数据保存到队列尾部。
- 出队列(Out):将对头的数据弹出(读取后删除)。
代码如下:
package ds.queue;
public class Queue<E> {
private static final int MAX_LENGTH = 20;// 队列的默认最大长度
private Object[] objData = null; // 存放队列的数据的数组
private int nHead;// 用于标记队列头部下标
private int nTail;// 用于标记队列尾部下标
private int nLen;// 用于记录队列初始化时的最大长度
/**
* 初始化队列,默认队列长度为50
*/
Queue() {
this(MAX_LENGTH);
}
/**
* 初始化队列
*
* @param nInitSize
* 队列的最大长度
*/
Queue(int nInitSize) {
if (nInitSize > 0) {
objData = new Object[nInitSize];
nLen = nInitSize;
nHead = 0;
nTail = -1;
} else {
System.out.println("栈的初始化长度不能小于等于0:" + nInitSize);
}
}
/**
* 获取队列的已使用的长度
*
* @param queue
* 队列的引用
* @return 队列已使用的长度
*/
int getSize(Queue<E> queue) {
// if (nHead == 0 && nTail == -1) {
// return 0;
// }
if (nHead > nTail) {
return 0;
}
return (queue.nTail - queue.nHead + 1);
}
/**
* 判断队列是否空
*
* @param queue
* 队列的引用
* @return 若队列为空,则返回true,否则返回false
*/
boolean isEmpty(Queue<E> queue) {
return (getSize(queue) == 0);
}
/**
* 判断队列是否满
*
* @param queue
* 队列的引用
* @return 若队列为满,则返回true,否则返回false
*/
boolean isFull(Queue<E> queue) {
return (queue.nTail + 1 == nLen);
}
/**
* 清空队列,但不释放内存
*
* @param queue
* 队列的引用
*/
void clear(Queue<E> queue) {
nHead = 0;
nTail = -1;
}
/**
* 清空队列,并释放内存
*
* @param queue
* 队列的引用
*/
void free(Queue<E> queue) {
clear(queue);
if (queue != null) {
queue = null;
}
}
/**
* 入队列
*
* @param queue
* 队列的引用
* @param eData
* 入队列的数据
*/
void enter(Queue<E> queue, E eData) {
if (isFull(queue)) {
System.out.println("队列已满,无法入队列:" + eData);
return;
}
queue.objData[++queue.nTail] = eData;
System.out.println(eData + "已入队列");
}
/**
* 出队列
*
* @param queue
* 队列的引用
* @return 队列头部的数据
*/
@SuppressWarnings("unchecked")
E out(Queue<E> queue) {
if (isEmpty(queue)) {
System.out.println("队列已空,无法出队列");
return null;
}
System.out.println((E) queue.objData[queue.nHead] + "出队列");
return (E) queue.objData[queue.nHead++];
}
/**
* 读取队列头部数据
*
* @param queue
* 队列的引用
* @return 队列头部的数据
*/
@SuppressWarnings("unchecked")
E peek(Queue<E> queue) {
if (isEmpty(queue)) {
System.out.println("队列已空,无法读取队列头部");
return null;
}
System.out.println((E) queue.objData[queue.nHead] + "已读取");
return (E) queue.objData[queue.nHead];
}
}
测试代码:
package ds.queue;
public class Simple {
public static void main(String[] args) {
System.out.println("----新建空队列----");
Queue<String> queue = new Queue<String>(5);
System.out.println("队列的现有长度:" + queue.getSize(queue));
System.out.println("----测试入队列操作----");
for (int i = 0; i < 6; i++) {
System.out.println("将test" + i + "压入队列");
queue.enter(queue, "test" + i);
}
System.out.println("队列的现有长度:" + queue.getSize(queue));
System.out.println("----测试读取队列顶操作----");
queue.peek(queue);
queue.peek(queue);
System.out.println("----测试出队列操作----");
for (int i = 0; i < 7; i++) {
queue.out(queue);
}
System.out.println("队列的现有长度:" + queue.getSize(queue));
}
}
测试结果:
----新建空队列----
队列的现有长度:0
----测试入队列操作----
将test0压入队列
test0已入队列
将test1压入队列
test1已入队列
将test2压入队列
test2已入队列
将test3压入队列
test3已入队列
将test4压入队列
test4已入队列
将test5压入队列
队列已满,无法入队列:test5
队列的现有长度:5
----测试读取队列顶操作----
test0已读取
test0已读取
----测试出队列操作----
test0出队列
test1出队列
test2出队列
test3出队列
test4出队列
队列已空,无法出队列
队列已空,无法出队列
队列的现有长度:0
上述代码实现的队列是队列长度不可变的顺序队列结构,这里就不再实现队列长度可变的顺序队列结构了。
如需队列长可变的读者,可以根据栈的简单实现(二)进行相应的改动即可。