在上一章中,使用了数组模拟了队列。可是留下的问题是,把数据取完后,再往里加数据就不行了。html
这是由于数组的末尾已经被占用了,入队会继续在数组后面增长,因而产生数组越界。
可是实际上,数组里是有空闲位置的,这种也能够叫“假溢出”。java
为了解决“假溢出”的问题,因而乎有了循环队列。数组
既然数组后面满了,头部有空,那继续加进来的元素从头开始放便可。oop
接着上图,这时候有a6入队,因而rear的下标指向a6的下一个元素位置,看起来没什么问题。测试
可是继续有新的元素a7入队的时候,由于front一直没变,这时候rear指针跟front就重合了,也就是说此时front=rear
。
3d
但是在上一章的代码里,咱们是用front=rear
来判断是否为空数组的。指针
// 判断队列是否为空 public boolean isEmpty() { return rear == front; }
如今是相等了,条件知足,可是数组是满的。code
这种状况看起来确实比较辣手,那若是不让这种状况出现不就能够了么?htm
假设,咱们让数组始终保留一个元素空间,也就是让rear指向队列中最后一个元素的后一个位置。
好比,当a6放入以后,此时就当作队列已经满了,这样的话就不会出现上述的状况了。
blog
为了实现这种思路,front也须要作调整。在上一章中,front初始位置是指在了队头元素的前一个,如今咱们让它就指在
第一个元素。队列的最大尺寸仍是maxSize,那么,如今判断队列满的条件就能够是这样:(rear+1)%maxSize = front
验证一下,下面的2种队列满状况:
继续验证一下,队列没满的状况:
队列的长度,也就是说队列中实际存了多少个元素。
此时须要考虑rear与front之间的三种状况:
第一种天然都清楚,队列为空。
第二种,rear>front,此时队列的长度为:rear-front
第三种,rear<front,比较复杂些。
队列长度分为2段:一段是maxSize-front,另外一段则是:0+rear(结合右图理解)。故此时队列总长度为两段相加:rear-front+maxSize
因此队列长度的通用计算公式为:(rear-front+maxSize)% maxSize
既然如今队列是尽是空的判断条件有了,队列长度也能计算出来了,那么代码就好改造了(基于上一章),变成循环队列。
package circle; import java.util.Scanner; public class CircleArrayQueue { public static void main(String[] args) { System.out.println("-----测试循环队列-----"); // 建立一个队列 CircleArray circleArray = new CircleArray(4); char key = ' '; //接受用户输入 Scanner scanner = new Scanner(System.in); boolean loop = true; // 输出一个菜单 while (loop) { System.out.println("s(show): 显示队列"); System.out.println("e(exit): 退出程序"); System.out.println("a(add): 添加数据到队列"); System.out.println("g(get): 从队列取出数据"); System.out.println("h(head): 显示队首的数据"); key = scanner.next().charAt(0); // 接收一个字符 switch (key) { case 's': circleArray.showQueue(); break; case 'a': System.out.println("请要添加的数"); int value = scanner.nextInt(); circleArray.addQueue(value); break; case 'g': try { int res = circleArray.getQueue(); System.out.printf("取出的数据是:%d", res); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'h': try { int headValue = circleArray.showHeadQueue(); System.out.printf("队首数据是:%d", headValue); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'e': scanner.close(); loop = false; break; } } System.out.println("退出程序"); } } class CircleArray { //表示数组最大容量 private int maxSize; // 队列头,由以前调整为指向队列第一个元素 private int front; // 队列尾,由以前调整为指向队列最后一个元素的后一个位置,为了空出一个元素位置 private int rear; // 用于存放数据的数组 private int[] arr; // 构造器 public CircleArray(int arrMaxSize) { maxSize = arrMaxSize; arr = new int[maxSize]; front = 0; rear = 0; } // 判断队列是否已经存满 public boolean isFull() { return (rear + 1) % maxSize == front; } // 判断队列是否为空 public boolean isEmpty() { return rear == front; } // 添加数据到队列 public void addQueue(int num) { // 判断队列是否满了 if (isFull()) { System.out.println("队列已满,不可加入数据"); return; } arr[rear] = num; // 将rear后移,要考虑取模 rear = (rear + 1) % maxSize; } // 拿出队列数据 public int getQueue() { // 判断队列是否空 if (isEmpty()) { // 抛出异常 throw new RuntimeException("队列为空,不可取数据"); } int tempValue = arr[front]; front = (front + 1) % maxSize; //也要考虑取模,防止front不停的增长,致使越界 return tempValue; } // 显示队列全部数据 public void showQueue() { // 遍历 if (isEmpty()) { System.out.println("队列为空"); return; } // 从front开始遍历,遍历多少个实际存的元素便可 for (int i = front; i < front + CircleQueueSize(); i++) { System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]); } } // 求出当前队列有效数据的个数 public int CircleQueueSize() { return (rear - front + maxSize) % maxSize; } // 显示队里的队首数据 public int showHeadQueue() { if (isEmpty()) { // 抛出异常 throw new RuntimeException("队列为空,不可取数据"); } return arr[front]; } }
重点的改动在于取模。