概述
目标:
- 队列的存储结构及操作特点
java
中队列相关的api
- 基于单链表的队列实现
- 刷题(设计循环队列)
存储结构及特点
队列(queue
) 和栈一样,代表具有一类操作特征的数据结构,拿日常生活中的一个场景举例说明,去车站的窗口买票,就要排队,先来的人就先买,后到的人就后买,先来的人排至队头,后来的人排至队尾,不允许插队,先进先出
,这就是典型的队列
;先进先出(First In First Out) 即 FIFO
,为了方便理解队列这种数据结构,将以两幅图,第一个是队列,第二个是栈,两图作对比
总结: 队列和栈一样都属于一种操作受限
的线性表,栈只允许在一端
进行操作,分别是入栈和出栈,而队列跟栈很相似,支持的操作也有限,最基本的两个操作,一个叫入队对,将数据插入到队列末尾,另一个叫出队列,从队列头部取出一个数据
注意: 入队列和出队列的时间复杂度均为O(1)
队列实现
java api
像队列
和栈
这种数据结构在高级语言中的实现特别的丰富,成熟
Interface Queue 链接
全类名 java.util.Queue
方法摘要
Throws exception | Returns special value | |
---|---|---|
Insert | add(e) | offer(e) |
Remove | remove() | poll() |
Examine | element() | peek() |
链表实现队列
基于单链表实现的队列,需要两个指针:head
指针和 tail
指针;它们分别指向链表的第一个 节点
,如图所示,入队时, tail.next = new_node
,tail = tail.next
即先添加到链表尾,再将 tail
指向新加的 节点
,出队时, head = head.next
即将原来的第一个头节点
,变为原来的第二个节点
实现时,将 java.util.Queue
直接复制过来
队列接口如下:
public interface Queue<E> {boolean add(E e);boolean offer(E e);E remove();E poll();E element();E peek();boolean isEmpty();
}
队列实现类如下:
public class LinkedListQueue<E> implements Queue<E> {// 队列大小int size;// 头节点Node<E> head;// 尾节点Node<E> tail;public LinkedListQueue() {}@Overridepublic boolean add(E e) {addTail(e);return true;}@Overridepublic boolean offer(E e) {addTail(e);return true;}@Overridepublic E remove() {if (size == 0) {throw new NoSuchElementException("队列为空!");}return removeHead().val;}@Overridepublic E poll() {if (size == 0) {return null;}return removeHead().val;}@Overridepublic E element() {if (size == 0) {throw new NoSuchElementException("队列为空!");}return head.val;}@Overridepublic E peek() {if (size == 0) {return null;}return head.val;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();Node<E> h = head;while (h != null) {sb.append(h.val).append("->");h = h.next;}return sb.append("null").toString();}public boolean isEmpty() {return size == 0;}private Node<E> removeHead() {Node<E> h = head;head = head.next;h.next = null;size--;return h;}private void addTail(E item) {Node<E> tmp = tail;Node<E> newNode = new Node<>(item, null);tail = newNode;if (tmp == null) {// 链表为空,即入队既是头又是尾head = newNode;} else {// tmp 此是是倒数第二个节点tmp.next = tail;}size++;}private static class Node<E> {E val;Node<E> next;public Node(E val, Node<E> next) {this.val = val;this.next = next;}}
}
测试类如下:
public static void main(String[] args) {Queue<String> queue = new LinkedListQueue();queue.add("1");queue.add("2");queue.add("3");System.out.println("队列是否为空:" + queue.isEmpty());System.out.println(queue);System.out.println("出队元素:"+queue.remove());System.out.println(queue);System.out.println("出队元素:"+queue.poll());System.out.println(queue);System.out.println("队列头元素:"+queue.peek());System.out.println(queue);}
刷题(设计循环队列)
设计循环队列
为充分利用队列空间,克服"假溢出
"现象的方法是:将队列空间想象
为一个首尾相接
的圆环,循环队列(Circular Queue
)是把顺序队列首尾相连,把存储队列元素的表从逻辑上
看成一个环,成为循环队列
。
在循环队列中,当队列为空时,可知 front = rear
;而当所有队列空间全占满时,也有 front = rear
。为了 区别
这两种情况,假设队列使用的数组有 capacity
个存储空间,则此时规定循环队列最多只能有 capacity - 1
个队列元素,当循环队列中只剩下一个空间存储单元时,则表示队列已满;根据以上可知,队列判空条件是 front = rear
,而队列判断为满的条件是 front = (rear + 1) mod capacity
,如上图队列已满
。对于固定大小的数组,只要知道队尾 rear
与队首 front
,即可计算出队列当前的长度:(rear - front + capacity) mod capacity
,如下图
此时由公式:0 - 1 + 5 % 5 = 4
队列长度为 4
分析完来看代码
public class MyCircularQueue {private int[] data;private int front, rear;public MyCircularQueue(int k) {// 因为满队列实际上是数组少1个,所以加上1data = new int[k + 1];}public boolean enQueue(int value) {if (isFull()) {return false;}data[rear] = value;// 不能直接 rear ++ ,值会无限增大rear = getNext(rear);return true;}public boolean deQueue() {if (isEmpty()) {return false;}front = getNext(front);return true;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append("头节点下标:" + front).append(",").append("尾节点下标:" + rear).append(" ");for (int i = 0; i < this.data.length; i++) {sb.append("a[").append(i).append("] = ").append(this.data[i]).append(",");}return sb.toString();}public boolean isEmpty() {return front == rear;}public boolean isFull() {return getNext(rear) == front;}private int getNext(int cur) {// 获取当前数据的下一个数据的下标return (cur + 1) % data.length;}
}
测试如下图,队列满,然后出一个进一个,重复两次执行
测试
public class MyCircularQueueTest {public static void main(String[] args) {MyCircularQueue queue = new MyCircularQueue(5);System.out.println("队列是否为空:" + queue.isEmpty());queue.enQueue(1);System.out.println(queue);queue.enQueue(2);queue.enQueue(3);queue.enQueue(4);queue.enQueue(5);System.out.println(queue);System.out.println("加入是否成功!" + queue.enQueue(5));queue.deQueue();System.out.println("队列头部元素出队:" + queue);System.out.println("加入是否成功!" + queue.enQueue(6));System.out.println(queue);// -----queue.deQueue();System.out.println("队列头部元素出队:" + queue);System.out.println("加入是否成功!" + queue.enQueue(7));System.out.println(queue);}
}
结束
队列
至此就结束了,如有问题,欢迎评论区留言