1.概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 。入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头 /队首(Head/Front)
2.队列的使用
现实生活中各种排队:都是队列的一种使用。
广义的队列一般有以下几种:
1.普通队列FIFO:链式队列---基于链表实现(单链表足够,尾插头删)
2.循环队列:可以从队尾走到队首,使用定长数组来实现。
3.优先级队列(非线性结构):一般都是先进先出,但是优先级较高的元素可以优先出队。内部其实使用堆这个数据结构来实现(JDK默认使用的是最小堆---完全二叉树)
在Java中,Queue是个接口,底层是通过链表实现的。
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因此LinkedList实现了Queue接口。
Queue<Integer> q = new LinkedList<>(); q.offer(1); q.offer(2); q.offer(3); q.offer(4); q.offer(5); // 从队尾入队列 // 5 System.out.println(q.size()); // 1 System.out.println(q.peek()); // 获取队头元素 q.poll(); // 2 System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回 // 3 if(q.isEmpty()){ System.out.println("队列空"); }else{ System.out.println(q.size()); }
3.队列的模拟实现
package seqlist.stack_queue.queue; /** * 队列的核心接口 * 队列的具体实现可能有多种,普通队列,循环队列,优先级队列 * 无论哪种队列实现都得满足接口中定义的方法 */ public interface Queue { // 入队 void offer(int val); // 出队 int poll(); // 查看队首元素 int peek(); // 判断队列是否为空 boolean isEmpty(); }
package seqlist.stack_queue.queue.impl; import seqlist.stack_queue.queue.Queue; import java.util.NoSuchElementException; /** * @description 基于链表实现的普通队列 */ public class LinkedQueue implements Queue { // 内部的节点类 private static class Node { int val; Node next; public Node(int val) { this.val = val; } } private int size; // 当前链表的头结点,出队元素 private Node head; // 当前链表的尾结点,添加元素 private Node tail; @Override public void offer(int val) { Node node = new Node(val); size ++; if (tail == null) { head = tail = node; return; } // 尾插,从队列的尾部添加元素 tail.next = node; tail = node; } @Override public int poll() { if (isEmpty()) { throw new NoSuchElementException("queue is empty!cannot poll!"); } // 出队在头部进行删除 Node node = head; head = head.next; size --; node.next = null; return node.val; } @Override public int peek() { if (isEmpty()) { throw new NoSuchElementException("queue is empty!cannot peek!"); } return head.val; } @Override public boolean isEmpty() { return size == 0; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("front ["); for (Node x = head;x != null;x = x.next) { sb.append(x.val); if (x.next != null) { sb.append(", "); } } sb.append("] tail"); return sb.toString(); } }
package seqlist.stack_queue.queue.impl; import seqlist.stack_queue.queue.Queue; import seqlist.stack_queue.queue.impl.LinkedQueue; public class QueueTest { public static void main(String[] args) { Queue queue = new LinkedQueue(); queue.offer(1); queue.offer(3); queue.offer(5); queue.offer(7); // 1 System.out.println(queue.poll()); // 3 System.out.println(queue.peek()); System.out.println(queue); } }
4.循环队列
循环队列使用定长数组(保证元素个数)来实现,数组的首位相连就构成了循环队列(就是走到数组最后一个元素时,下一个访问的就是数组的首地址)。循环队列一般用在OS(操作系统)生产消费者模型。环形队列通常使用数组实现。
4.1 如何区分循环队列空与满
循环队列
判断是否为空:head == tail
判断队列是否已满:(tail + 1) % num.length == head
eg:要保存一个最大支持5个元素的循环队列,则开辟一个长度为6的数组!多余这个空间就是来判断队列是否已满。