队列 是 先进先出( FIFO,First In, First Out) 的线性表。在具体应用中通常用链表或者数组来实现,用数组实现的队列叫作 顺序队列 ,用链表实现的队列叫作 链式队列 。队列只允许在后端(rear)进行插入操作也就是 入队 enqueue,在前端(front)进行删除操作也就是出队 dequeue
数组实现的队列叫顺序队列
用front来标记与队头元素位置的关系 用rear来标记与队尾元素位置的关系
如下:当队列为空时,设置front=rear=-1
(-1为初始值,不代表索引
), 当添加元素时:
rear=数组长度-1时该队列已满。此时rear=maxSize-1;front还是在队头的前面。
删除元素:
注意: 所谓的删除,其实就是通过操作front和rear与队头队尾元素索引的关系,使我们访问不到元素,实际上元素还在队列里面
public class ArrayQueueDemo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); ArrayQueue arrayQueue = new ArrayQueue(3); boolean flag=true; while (flag) { System.out.println("请输入:(s:显示队列,a:添加队列,g:取出数据,h:查看队头数据,e:退出)"); switch (sc.next().charAt(0)) { case 's': try { arrayQueue.listQueue(); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'a': System.out.println("请输入:"); try { arrayQueue.addQueue(sc.nextInt()); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'g': try { int queue = arrayQueue.getQueue(); System.out.println(queue); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'h': try { int i = arrayQueue.headQueue(); System.out.println(i); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'e': flag=false; break; default: System.out.println("输入有误!!"); break; } } } } class ArrayQueue{ // 数组实现队列 private int maxSize; // 数组容量 private int front; // 队列头 private int rear; // 队列尾 private int[] arr; // 模拟队列的数组 //队列构造器 public ArrayQueue(int arrMaxSize){ maxSize=arrMaxSize; arr=new int[arrMaxSize]; front=-1; // 记录队列头索引的前一个位置 rear=-1; // 记录队列尾索引 } //判断队列是否为空 public boolean isEmpty() { return rear==front; } //判断队列是否满 public boolean isFull() { return rear==maxSize-1; } // 添加数据到队列 public void addQueue(int n){ //判断是否满 if (isFull()){ throw new RuntimeException("队列满,不能添加数据"); } rear++; //后移1位 arr[rear]=n; } //取出队列数据,出队列 public int getQueue() { //判断队列是否为空 if (isEmpty()){ throw new RuntimeException("队列空,不能获取数据"); } front++; // 后移 return arr[front]; } //显示队列所有元素 public void listQueue() { //判空 if (isEmpty()) { throw new RuntimeException("队列空,无数据"); } for (int i = 0; i < arr.length; i++) { if (i==0){ System.out.print("[ "+arr[i]); }else if (i==maxSize-1){ System.out.print(","+arr[i]+" ]"); }else { System.out.print(","+arr[i]); } } } //显示头数据 public int headQueue() { if (isEmpty()){ throw new RuntimeException("队列为空,无数据"); } return arr[front+1]; } }
缺点:使队列空间只能使用一次,不能重复使用。
这种情况称为"假溢出"(入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用,队列队列满时rear=maxSize-1,还要添加元素,则rear+1就会>数组的索引)
在实际使用队列时,为了使队列空间能重复使用,往往对队列的使用方法稍加改进:无论插入或删除,一旦rear指针增1或front指针增1 时超出了所分配的队列空间,就让它指向这片连续空间的起始位置。
在循环队列中,当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。为了区别这两种情况,规定循环队列最多只能有MaxSize-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。因此,队列判空的条件时front=rear,而队列判满的条件时front=(rear+1)%MaxSize。
队列中有效数据个数为:(rear+maxSize-front)%maxSize
,如上图:(3+4-0)%4=3
实现环形队列:
取出元素front=(front+1)%maxSize
,当front到队列末尾时有回到起点:front=0,形成循环
添加元素:rear=(rear+1)%maxSize
,当队列满时,rear=0,回到起点形成循环
package com.pqy.linear.queue; import java.util.Scanner; /** * @author panqiyi * @date 2021/9/25 - 15:20 */ class CircleArray { private int maxSize; // 数组容量 private int front; // 队列头,默认0 private int rear; // 队列尾 private int[] arr; // 模拟队列的数组 //队列构造器 public CircleArray(int arrMaxSize) { maxSize = arrMaxSize; arr = new int[arrMaxSize]; } //判断队列是否为空 public boolean isEmpty() { return rear == front; } //判断队列是否满 public boolean isFull() { return (rear+1)%maxSize==front; } // 添加数据到队列 public void addQueue(int n) { //判断是否满 if (isFull()) { throw new RuntimeException("队列满,不能添加数据"); } arr[rear] = n; rear=(rear+1)%maxSize; // 后移取模 } //取出队列数据,出队列 public int getQueue() { //判断队列是否为空 if (isEmpty()) { throw new RuntimeException("队列空,不能获取数据"); } int value=arr[front]; front=(front+1)%maxSize; // 后移取模 return value; } //显示队列所有元素 public void listQueue() { //判空 if (isEmpty()) { throw new RuntimeException("队列空,无数据"); } for (int i = front; i < front+size(); i++) { // 队头+有效元素个数 System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]); } } //求有效数据个数 public int size(){ return (rear+maxSize-front)%maxSize; } //显示头数据 public int headQueue() { if (isEmpty()) { throw new RuntimeException("队列为空,无数据"); } return arr[front]; } } public class CircleArrayQueueDemo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); CircleArray circleArray = new CircleArray(3); boolean flag=true; while (flag) { System.out.println("请输入:(s:显示队列,a:添加队列,g:取出数据,h:查看队头数据,e:退出)"); switch (sc.next().charAt(0)) { case 's': try { circleArray.listQueue(); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'a': System.out.println("请输入:"); try { circleArray.addQueue(sc.nextInt()); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'g': try { int queue = circleArray.getQueue(); System.out.println(queue); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'h': try { int i = circleArray.headQueue(); System.out.println(i); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'e': flag=false; break; default: System.out.println("输入有误!!"); break; } } } }
运行:
2、设置数组大小为3
2、输入 11,22
3、取出一个元素
4、添加一个元素
// 添加数据到队列 public void addQueue(int n) { //判断是否满 if (isFull()) { throw new RuntimeException("队列满,不能添加数据"); } arr[rear] = n; //添加元素 rear=(rear+1)%maxSize; // 后移取模 }
5、查看队列
//显示队列所有元素 public void listQueue() { //判空 if (isEmpty()) { throw new RuntimeException("队列空,无数据"); } for (int i = front; i < front+size(); i++) { // 队头开始到 队头+有效元素个数 System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]); } } //求有效数据个数 public int size(){ return (rear+maxSize-front)%maxSize; }
为什么要 i%maxSize ? 因为这样才能得到准确的元素索引(再删除添加一个元素就知道了,添加44)
再查看
因为是循环队列,所有遍历有效的数据,front到front+size()可能是在不同方向的,front与rear都是取模的,所以索引也取模才能得到。