环形数组队列问题解析
初学算法被环形数组队列问题折腾了一上午,思来想去终于理清了思路。
1 private int maxsize; //数组的最大容量
2 private int rear; //指向队列最后一个元素的后一个位置
3 private int front; //队列首部,指向队列的第一个元素
4 private int[] arr; //该数据用于存放数据,模拟队列
这里是一些需要用到的属性。
数组的最大容量不必说了,就是数组最大能够承装的数据值。但是需要注意的是,在此解法中,这里定义的最大容量在实际运行时是会减一的。即 ActualLength = maxsize - 1;
front即队列头部位置第一个元素在数组中的索引;
rear指向数组最后一个元素之后一个位置,指向的位置往往可以看作空,这也是为什么定义的最大容量在实际情况下会减一的原因。这样定义是为了方便判断队列是否为满或空
1 //判断队列是否满 2 public boolean isFull() { 3 return (rear + 1) % maxsize == front; 4 }
判断队列是否满员的情况可以看成判断指针front和指针rear是否相邻,若队伍满员,那么rear+1则正好会和front的指针重合。此时为了避免数组角标越界进行取模运算,返回true。
1 //判断队列是否为空 2 public boolean isEmpty() { 3 return rear == front; 4 }
判断队列是否为空,很简单两指针若刚好重合则说明队列为空
1 //添加数据到队列 2 public void addQueue(int n) { 3 //判断队列是否满员 4 if (isFull()) { 5 System.out.println("队列已满,无法添加数据"); 6 return; 7 } else { 8 //直接将数据加入 9 arr[rear] = n; 10 //将rear后移,这里必须考虑取模 11 rear = (rear + 1) % maxsize; 12 } 13 }
向队列中添加数据,首先判断队伍是否满员,若未满员则进入else语句。首先将需要加入的n值添加进当前数组rear索引位置,然后需要将rear后移,因为需要考虑环形问题,若rear此时刚好处于数组的最尾部,则它需要循环返回数组首端,即将rear+1后取模。
1 //取出队列头部的数据,出队列 2 public int getQueue() { 3 //判断队列是否为空 4 if (isEmpty()) { 5 //通过抛异常处理 6 throw new RuntimeException("队列为空"); 7 } else { 8 //分析出front是指向队列的第一个元素 9 //1. 先把front对应的值保留到一个临时变量 10 //2. 将front后移,考虑取模 11 //3. 将临时保存的变量返回 12 int value = arr[front]; 13 front = (front + 1)% maxsize; 14 return value; 15 } 16 }
此方法先取出当前头部的数值,然后将其抹去,同时需要将front指针后移一位。这里和上面添加数据到队列类似,但是是针对front指针的操作。同样需要考虑数组角标越界问题。
1 //显示队列的所有数据 2 public void showQueue() { 3 //遍历 4 if (isEmpty()) { 5 System.out.println("队列为空,无数据"); 6 } else { 7 //思路:从front开始遍历,遍历多少个元素 8 for (int i = front; i < front + size(); i++) { 9 System.out.printf("arr[%d] = %d\n", i % maxsize, arr[i % maxsize]); 10 } 11 } 12 } 13 14 //求出当前队列有效数据的个数 15 public int size(){ 16 return (rear + maxsize - front) % maxsize; 17 }
显示所有数据需要和获取有效数据的方法共同使用,因为遍历数组所有数据需要从头部开始,也就是front指针处,遍历长度即为size,但是由于遍历是从front开始的,需要前面加上front。
(rear-front+maxsize)%maxsize可以求出当前从front到rear的有效长度。