/**
* 队列的接口
* 队列是一种先进先出的数据结构
* 队列支持的操作:
* 判断队列是否为空
* 判断队列是否已经满了
* 查看队列已经有多少元素
* 将一个元素入队
* 将一个元素出队
* 查看队头的元素,但不出队
* 队列在底层可以用数组实现,也可以用链表实现
* 但不管实现方式如何,都必须满足此接口中的规定
*/
public interface MyQueue<Item> extends Iterable<Item>{
/**
* 判断队列是否为空
* @return 如果队列为空,则返回true;否则,返回false
*/
public boolean isEmpty();
/**
* 判断队列是否为满,满了返回true,否则返回false
* @return
*/
public boolean isFull();
/**
* 返回队列中元素的个数
* @return 个数
*/
public int size();
/**
* 入队,将一个元素入队
* @param item 待入队的元素
* @return 入队成功返回true,否则返回false
*/
public boolean enqueue(Item item);
/**
* 将一个元素出队
* @return 出队成功返回出队的元素,否则返回null
*/
public Item dequeue();
/**
* 查看队列头部的元素,但不出队
* @return 队列头部的元素,如果没有元素,返回null
*/
public Item get();
}
以上是队列的接口,接下来用数组来实现队列。
/**
* 队列的具体实现,底层使用数组实现
* 数组的实现也分几种情况
*版本1中,维护两个索引,front和rear
* 初始状态front和rear都在-1的位置(假想有-1的位置)
* 第一个元素入队的时候,front=0、rear++、再入队
* 出队的时候,正常情况下front++就可以,但是当front和rear相同的时候出队,那么就把front和rear初始化为-1
* 队列空的条件为front=-1;
* 队列满的条件为(rear+1)%maxsize==front
* Queue还要满足foreach的自动迭代,所以需要实现Iterable<Item>接口
*/
public class ArrayQueueVersion1<Item> implements MyQueue<Item>{
private Item[] items;
private int front;
private int rear;
private int maxsize;
public ArrayQueueVersion1() {
this(10); //调用无参数构造器,实际上调用的是带参数的构造器,默认大小为10
}
/**
* 有参数的构造器,
* @param k 队列的最大容量
*/
public ArrayQueueVersion1(int k) {
items= (Item[]) new Object[k];
maxsize=k;
front=-1;
rear=-1;
}
@Override
public boolean isEmpty() {
return front==-1;
}
@Override
public boolean isFull() {
return (rear+1)%maxsize==front;
}
@Override
public int size() {
if(isFull()){
return maxsize;
}
return (rear+1-front+maxsize)%maxsize;
}
@Override
public boolean enqueue(Item item) {
if(isFull()){
return false;
}
if(front==-1){
front=0;
}
rear=(rear+1)%maxsize;
items[rear]=item;
return true;
}
@Override
public Item dequeue() {
if(isEmpty()){
return null;
}
if(front==rear){
front=-1;
rear=-1;
}
Item delete=items[front];
front=(front+1)%maxsize;
return delete;
}
@Override
public Item get() {
if(isEmpty()){
return null;
}
return items[front];
}
@Override
public Iterator<Item> iterator() {
return new ArrayQueueVersion1Iterator();
}
private class ArrayQueueVersion1Iterator implements Iterator<Item>{
private int cur=front;
@Override
public boolean hasNext() {
return cur<=rear;
}
@Override
public Item next() {
Item nextItem=items[cur];
cur++;
return nextItem;
}
}
}
以上是第一个版本的实现,思路是使用数组来存储元素,并维护两个索引。测试一下:
public class ArrayQueueVersion1Test {
public static void main(String[] args) {
MyQueue<Integer> myQueue=new ArrayQueueVersion1<>(7);
myQueue.enqueue(1);
myQueue.enqueue(2);
myQueue.enqueue(13);
myQueue.enqueue(20);
myQueue.enqueue(35);
myQueue.enqueue(50);
myQueue.enqueue(70);
System.out.println("size is "+myQueue.size());
System.out.println("删除了"+myQueue.dequeue());
System.out.println("删除了"+myQueue.dequeue());
System.out.println("删除了"+myQueue.dequeue());
System.out.println("删除了"+myQueue.dequeue());
myQueue.enqueue(100);
System.out.println("size is "+myQueue.size());
System.out.println("next is "+myQueue.get());
for (int i:myQueue){
System.out.println(i);
}
}
}
结果是:
size is 7
删除了1
删除了2
删除了13
删除了20
size is 4
next is 35
35
50
70
100
满足了队列的基本操作。这就是使用数组来实现队列的第一个版本。
在第二个版本中,思路类似,但是细节变了。具体如下:
/**
* 队列的实现,底层依然是使用数组。
* 但是这次初始化时,front和rear都指向0这个位置。
* 队列为空的条件为:front==rear
* 队列为满的条件不变:(rear+1)%maxsize==front
* 队列中元素的个数为:(rear-front+1+maxsize)%maxsize
* 说明一下为什么队列为空是front==rear:
* 如果底层存储元素的数组只有k个大小,那么在这种实现方式下,要舍弃一个位置的存储空间,
* 这样队列只有在空的时候front才和rear是指向同一个位置的。
* 这种舍弃一个位置的存储空间换来的是判断上的简洁,是值得的。
* @param <Item>
*/
public class ArrayQueueVersion2<Item> implements MyQueue<Item> {
private Item[] items;
private int front;
private int rear;
private int maxsize;
public ArrayQueueVersion2() {
this(10);
}
/**
* 注意这里的k是实际要存储的元素的个数,
* 而真正开辟的数组却要比k大一个空间,
* 因为前面说过要牺牲一个空间来进行条件的判断
* @param k
*/
public ArrayQueueVersion2(int k) {
items= (Item[]) new Object[k+1];
front=0;
rear=0;
maxsize=k+1;
}
@Override
public boolean isEmpty() {
return front==rear;
}
@Override
public boolean isFull() {
return (rear+1)%maxsize==front;
}
@Override
public int size() {
if(isFull()){
return maxsize-1;
}
return (rear-front+1+maxsize)%maxsize-1;
}
@Override
public boolean enqueue(Item item) {
if(isFull()){
return false;
}
items[rear]=item;
rear=(rear+1)%maxsize;
return true;
}
@Override
public Item dequeue() {
if(isEmpty()){
return null;
}
Item delete=items[front];
front=(front+1)%maxsize;
return delete;
}
@Override
public Item get() {
if (isEmpty()){
return null;
}
return items[front];
}
@Override
public Iterator<Item> iterator() {
return new ArrayQueueVersion2Iterator();
}
private class ArrayQueueVersion2Iterator implements Iterator<Item>{
private int cur=front;
@Override
public boolean hasNext() {
return rear!=cur;
}
@Override
public Item next() {
Item nextItem = items[cur];
cur=(cur+1)%maxsize;
return nextItem;
}
}
}
测试一下,因为是面向接口编程,所以只要把之前的测试用例的实现类替换了就行了,这也是多态的好处。
public class ArrayQueueVersion2Test {
public static void main(String[] args) {
MyQueue<Integer> myQueue=new ArrayQueueVersion2<>(7);
myQueue.enqueue(1);
myQueue.enqueue(2);
myQueue.enqueue(13);
myQueue.enqueue(20);
myQueue.enqueue(35);
myQueue.enqueue(50);
myQueue.enqueue(70);
System.out.println("size is "+myQueue.size());
System.out.println("删除了"+myQueue.dequeue());
System.out.println("删除了"+myQueue.dequeue());
System.out.println("删除了"+myQueue.dequeue());
System.out.println("删除了"+myQueue.dequeue());
myQueue.enqueue(100);
System.out.println("size is "+myQueue.size());
System.out.println("next is "+myQueue.get());
for (int i:myQueue){
System.out.println(i);
}
}
}
结果是:
size is 7
删除了1
删除了2
删除了13
删除了20
size is 4
next is 35
35
50
70
100
以上两种思路都需要掌握,因为虽然底层都是使用数组,但是细节完全不一样。