一.概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First
In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
1 队列的使用
在Java中,Queue是个接口,底层是通过链表实现的。
Queue是普通队列
Deque是双端队列
1.1.队列的方法
peek:获取对头元素但不删除
//这样写是队列Queue<Integer> queue=new LinkedList<>();
//如下这样写表示链表List<Integer> queue1=new LinkedList<>();
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
public class Text2 {public static void main(String[] args) {Queue<Integer> queue = new LinkedList<>();queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);System.out.println(queue.peek());System.out.println(queue.poll());System.out.println(queue.poll());}
}
2.模拟实现队列
如上我们演示队列的时候,他是一个双向链表,这也就意味着,模拟实现队列的时候就是我们自己要实现一个双向链表.这个双向链表提供了一个offer方法,一个peek方法,一个poll方法,实际上就是对链表实现一个简单的操作.
假如按照如上图这样,如果添加元素就要在队尾添加,出队从对头出,也可以把他们反过来,不管从那边进出,时间复杂度都是O(1),所以这里相当于我们提供一个双向链表,给他提供一个尾插,一个头删.
代码如下:
public class MyLinkQueue {static class ListNode{public int val;public ListNode next;public ListNode prev;public ListNode(int val) {this.val = val;}}public ListNode head;public ListNode last;public int usedSize;//尾插法(入队)public boolean offer(int val){ListNode node=new ListNode(val);if(head==null){head=node;last=node;}else {last.next=node;node.prev=last;last=last.next;}usedSize++;return true;}//删除节点(出队)public int poll(){if(head==null){return -1;}//只有一个节点int retVal=head.val;if(head.next==null){head=null;last=null;return retVal;}//有多个节点head=head.next;head.prev=null;usedSize--;return retVal;}//瞄一眼public int peek(){if(head==null){return -1;}return head.val;}//是否为空public boolean empty(){//head为空就为空return head==null;}public int size(){return usedSize;}}
测试如下:
public class Text {public static void main(String[] args) {MyLinkQueue myLinkQueue=new MyLinkQueue();myLinkQueue.offer(1);myLinkQueue.offer(2);myLinkQueue.offer(3);myLinkQueue.offer(4);System.out.println(myLinkQueue.peek());System.out.println(myLinkQueue.poll());System.out.println(myLinkQueue.poll());}
}
这里我们想一个问题,数组能不能实现一个队列呢?他会面临什么样的问题呢?
定义一个head为队头,rear为队尾, 如上图1,把上面那些元素一个个放进数组里,rear往后走,一直到图2位置,假如现在要出队,从队头出,让front往前走,如上图3,然后再往进放,把56,放进数组的4号标,且rear再往后走,一直到图4位置,此时我们发现没有下标5的位置,那剩下的元素应该怎么放呢?
这里应该让rear再回到0下标,把剩下的元素放进数组.如上第五幅图.这里相当于把这个数组看成一个圈(这里注意,rear是当前可以存放数据元素的下标),如下图这样.
这里有两个问题:
在这个圆里,现在rear和front相遇了,他们是空的还是满的,rear怎么从4下标走到0小标呢?
这里解决空有很多种方案,这里举例三种如下:
1.使用usedSize进行记录
2.浪费一个空间来表示满(判断rear的下一个是不是front,是满了,如果是空的,没满)
3.使用标记
正常情况下0下标到1下标直接rear+1即可, 这里麻烦的是从4下标到0下标这里不好搞,其实这里只要让rear等于(rear+1)%数组的长度.