数据结构与算法——环形队列
程序员文章站
2022-07-09 15:43:52
...
数据结构与算法——环形队列
数组模拟环形队列
- 单列队列的缺点:浪费内存,当front指针后移之后,front指针前面的内存空间无法再进行利用,且指定数组长度后,无法开辟新的空间进行存储数据。
- 对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过取模的方式来实现即可)
分析说明
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear + 1) % maxSize == front ,此时队列已满。
- rear == front ,此时依旧表示队列是空的。
- front变量的含义做了一个调整:front指向队列的第一个元素(而非第一个元素的前一个位置),即初始值为0。
- rear变量的含义做了一个调整:rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定,其实也就是说定义数组时候的数组长度比实际数组存储数据的长度多1,rear的初始值也为0。
- 而此时,我们在遍历数组的时候,就不应该从索引为0的位置开始,因为当经过我们重复的添加、删除数据后,此时第一个数据的位置不一定是0,而应该是front所指向的位置,而数据的有效长度,应该和rear有关,即(rear - front + maxSize) % size。其中,rear - front即为数据有效长度,而取模是为了因为此时的队列已经是个环形队列(想象成一个圆),多次的增加与删除后,front和rear的数值已超过数组的最大长度,需要通过取模来将二者的值限定在[0,maxSize)之间。
代码实现
1、在单列队列的基础上对于front和rear定义的修改。
2、对于一些方法体的内容的增加与修改。(注释中有所体现)
class circleQueueTest{
private int maxSize;// 队列最大长度
private int front;// 头部指针,用来输出
private int rear;// 尾部指针,用来输入
private int[] arr;// int型数组
//构造结构体
public circleQueueTest(int cirlceMaxSize){
maxSize = cirlceMaxSize;//此处的cirlceMaxSize比实际存储的数据多1,为了给指针rear预留一个空间
arr = new int[cirlceMaxSize];
//front变量调整为指向第一个元素所在的位置索引
front = 0;
//rear变量调整为指向最后一个元素的后一个位置的索引,即一个新的空间作为环形的约定
rear = 0;
}
// 写方法体
// 1.判断队列是否满
public boolean isFull() {
if((rear + 1) % maxSize == front){
return true;
}else{
return false;
}
}
// 2.判断队列是否空
public boolean isEmpty() {
return front == rear;
}
// 3.添加数据到队列
public void addQueeu(int num) {
if (isFull()) {
System.out.println("队列已满,无法存入数据~~");
}
arr[rear] = num;
rear = (rear + 1) % maxSize;//rear后移,因为是环形,所以这里需要考虑取模
}
// 4.从队列取出数据
public int getQueue() {
if (isEmpty()) {
System.out.println("队列里当前没有数据~~~");
}
//因为这里需要将front指向的数据取出来,同时还需要将front指针后移一位
//1.首先,将front指向的值赋值给一个临时变量
//2.front后移。需要考虑环形队列,所以需要取模运算
//3.将临时变量输出出来
int temp = arr[front];
front = (front + 1) % maxSize;
return temp;
}
// 5.显示队列所有数据,从front位置开始,到rear前一个位置结束,即队列里的有效值
public void show() {
if (isEmpty()) {
System.out.println("当前队列没有数据~~~");
}else{
for (int i = front; i < front + size(); i++) {
System.out.println(arr[i%maxSize]);//考虑到环形,所以需要取模
}
}
}
//6.队列里的有效值的个数
public int size(){
return (rear - front + maxSize) % maxSize;
//1.rear - front其实就是当前队列里的有效值的个数
//2.加上一个maxSize的长度,是考虑到环形,再取模得到的余数,即为实际个数
}
// 7.显示头部数据
public void showHead() {
if (isEmpty()) {
System.out.println("当前队列没有数据~~~");
}
System.out.println(arr[front]);
}
}
3、主函数部分依旧是调用对象,然后使用类似于单列队列的循环和分支结构来实现“增删查”的循环操作。
import java.util.Scanner;
public class CircleQueue {
public static void main(String[] args) {
circleQueueTest queue = new circleQueueTest(5);
char key = ' ';
Scanner sc = new Scanner(System.in);
boolean isFlag = true;
while (isFlag) {
System.out.println("s(show):显示队列");
System.out.println("a(add):添加数据");
System.out.println("g(get):取出数据");
System.out.println("h(head):查看队列头数据");
System.out.println("e(exit):退出队列");
System.out.println("请输入选择(s/a/g/h/c):");
key = sc.next().charAt(0);
switch (key) {
case 's':
queue.show();
break;
case 'a':
if (queue.isFull()) {
System.out.println("队列满了,请重新选择~~");
break;
}
System.out.println("请输入要添加的数字:");
int n = sc.nextInt();
queue.addQueeu(n);
break;
case 'g':
if (queue.isEmpty()) {
System.out.println("队列空了,请重新选择~~");
break;
}
queue.getQueue();
case 'h':
queue.showHead();
break;
case 'e':
sc.close();
isFlag = false;
break;
default:
break;
}
}
System.out.println("程序退出~~~");
}
}
其中部分内容如果有表示或者表达不当,希望有所指正。
共勉。