/**
* 队列的接口
* 队列是一种先进先出的数据结构
* 队列支持的操作:
* 判断队列是否为空
* 判断队列是否已经满了
* 查看队列已经有多少元素
* 将一个元素入队
* 将一个元素出队
* 查看队头的元素,但不出队
* 队列在底层可以用数组实现,也可以用链表实现
* 但不管实现方式如何,都必须满足此接口中的规定
*/
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();
}
/**
* 底层使用链表实现队列,而且这个版本带头结点
* 初始化的时候,指针front和rear都指向头结点
* 入队时,让新的结点插在表尾
* 出队时,删除头结点之后的那个结点
* 用这种方法实现队列最容易出错的地方是在只剩下一个元素的时候出队,
* 因为此时要改变rear的值,让它重新指向头结点
* @param <Item>
*/
public class LinkedQueueWithHeadNode<Item> implements MyQueue<Item> {
private Node front;
private Node rear;
private int size;
private class Node{
Item item;
Node next;
}
/**
* 构造函数初始化队列,生成新的结点,并且让front和rear都指向它
* size=0
*/
public LinkedQueueWithHeadNode() {
front=new Node();
front.item=null; //头结点中的数据域是null,当然默认也是null
front.next=null;
rear=front;
size=0;
}
@Override
public boolean isEmpty() {
return front==rear;
}
/**
* 链式的队列理论上不会满,这也是链式的优点
* @return
*/
@Override
public boolean isFull() {
return false;
}
@Override
public int size() {
return size;
}
@Override
public boolean enqueue(Item item) {
Node newNode =new Node();
newNode.item=item;
newNode.next=null;
rear.next=newNode;
rear=newNode;
size++;
return true;
}
/**
* 出队的逻辑比较复杂
* 如果链表有两个及以上的元素,那么就属于正常情况
* 删除头结点之后的那个结点就可以完成出队
* 然而如果链表就只有一个元素,即头结点和之后的一个结点
* 那么在删除完头结点后面那个结点后,还需要维护rear结点,
* 让rear结点重新指向头结点
* @return
*/
@Override
public Item dequeue() {
if(isEmpty()){
return null;
}
Node p=front.next;
Item old=p.item;
front.next=p.next;
if(p==rear){
rear=front;
}
size--;
return old;
}
@Override
public Item get() {
if(isEmpty()){
return null;
}
return front.next.item;
}
@Override
public Iterator<Item> iterator() {
return new LinkedQueueIterator1();
}
private class LinkedQueueIterator1 implements Iterator<Item>{
private Node p=front;
@Override
public boolean hasNext() {
return p!=rear;
}
@Override
public Item next() {
Item item=p.next.item;
p=p.next;
return item;
}
}
}
测试:
public class LinkedQueueTest1 {
public static void main(String[] args) {
MyQueue<Integer> myQueue=new LinkedQueueWithHeadNode<>();
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都为null
* 入队的时候,要判断是否是从空表到插入第一个结点(这是一个特殊的情况)
* 正常情况是表非空,那么就是正常插入到链表的尾部
* 出队的时候,要判断是剩下唯一一个结点被删除(这是一个特出的情况)
* 如果还是有很多结点,那么从表头删除即可
* 由于不带头结点,所以默认的构造函数即可
* @param <Item>
*/
public class LinkedQueueWithoutHeadNode<Item> implements MyQueue<Item> {
private Node front;
private Node rear;
private int size;
private class Node{
Item item;
Node next;
}
@Override
public boolean isEmpty() {
return front==null;
}
/**
* 链式队列,所以理论上不会满
* @return
*/
@Override
public boolean isFull() {
return false;
}
@Override
public int size() {
return size;
}
@Override
public boolean enqueue(Item item) {
Node newNode =new Node();
newNode.item=item;
newNode.next=null;
if(rear==null){
rear=newNode;
}
rear.next=newNode;
rear=newNode;
if(front==null){
front=rear;
}
size++;
return true;
}
@Override
public Item dequeue() {
if(isEmpty()){
return null;
}
Item old=front.item;
front=front.next;
if(front==null){
rear=null;
}
size--;
return old;
}
@Override
public Item get() {
if (isEmpty()){
return null;
}
return front.item;
}
@Override
public Iterator<Item> iterator() {
return new LinkedQueueIterator2();
}
private class LinkedQueueIterator2 implements Iterator<Item>{
private Node cur=front;
@Override
public boolean hasNext() {
return cur!=null;
}
@Override
public Item next() {
Item item=cur.item;
cur=cur.next;
return item;
}
}
}
测试:
public class LinkedQueueTest2 {
public static void main(String[] args) {
MyQueue<Integer> myQueue=new LinkedQueueWithoutHeadNode<>();
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
到此为止,队列的java基本实现已经全部完毕。
总结:
队列可以在底层用数组和链表来实现。
数组实现一般采用循环数组,因为不会浪费空间。
数组的实现只要控制好了索引的位置,实现的方式可以不同。
具体实现看上一篇博客:
https://blog.csdn.net/Yangziqi_usst/article/details/83657054
而采用链表来实现队列,理论上队列不会满。链式队列分为带头结点和不带头结点。
OVER