【Java数据结构】用数组模拟队列与环形队列

应用场景

银行、医院等场所的叫号系统等,先拿到号码的人先被服务,后拿到号码的人后被服务,这个时候就用到了队列

队列

队列是一个有序列表,可以用数组或是链表来实现
队列遵循先入先出的原则:即先存入队列的数据先取出,后存入的数据后取出

数组模拟队列示意图及思路:

【Java数据结构】用数组模拟队列与环形队列_第1张图片

  • 其中MaxSize表示该队列的最大容量
  • 由于队列的输出和输入分别由前后端来处理,因此需要两个变量来记录队列前后端的下标,其中 front 记录头数据的前一个位置的下标,rear 记录尾数据的下标,添加取出数据前都先变动指针
  • 添加数据时:
  1. 将尾指针往后移一位:rear+1。当 front ==rear 时队列为空
  2. 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。当rear ==maxSize-1 时队列是满的
  • 取出数据时:将头指针后移一位

数组模拟队列代码实现

import java.util.Scanner;
public class TestQueueArray {
	public static void main(String[] args) {
		boolean loop = true;
		//创建一个容量为3的模拟队列
		QueueArray queue = new QueueArray(3);
		Scanner scan = new Scanner(System.in);
		while(loop) {
			System.out.println(""
					+ "e(exit):退出系统\n"
					+ "a(add):向队列中添加数据\n"
					+ "g(get):从队列中取出数据\n"
					+ "h(head):查看头数据\n"
					+ "s(show):查看队列所有数据");
			System.out.print("请输入您的指令:");
			String key = scan.next();
			switch (key) {
			case "e":	//退出系统
				loop = false;
				scan.close();
				break;
			case "a":	//向队列中添加数据
				System.out.print("请输入一个数据:");
				int value = scan.nextInt();
				queue.addQueue(value);
				break;
			case "g":	//从队列中取出数据
				try {
					System.out.printf("取出的数据为:%d\n",queue.getQueue());
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case "h":	//查看头数据(不是取出)
				try {
					System.out.printf("头数据为:%d\n",queue.headQueue());
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case "s":	//查看队列中所有的数据(不是取出)
				try {
					queue.showQueue();
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;	
			default:
				System.out.println("请检查您的指令是否正确!");
				break;
			}
		}
		System.out.println("退出程序!");
	}
}
//用数组模拟一个队列
class QueueArray {
	private int maxSize;// 标记数组队列的最大容量
	private int front;	// 标记数组队列的头数据前一个位置的下标
	private int rear;	// 标记数组队列的尾数据的下标
	private int[] arr;	//声明一个数组作为模拟队列的容器
	public QueueArray(int maxSize) {
		this.maxSize = maxSize;
		this.front = -1;
		this.rear = -1;
		this.arr = new int[this.maxSize];
	}
	// 判断数组队列是否已满
	public boolean isFull() {
		return this.rear == this.maxSize-1;
	}
	// 判断数组队列是否为空
	public boolean isEmpty() {
		return this.rear == this.front;
	}
	// 向数组队列里添加数据--入队
	public void addQueue(int data) {
		if (isFull()) {
			System.out.println("数组队列满了,不能添加数据");
			return;
		}
		this.rear++;
		this.arr[rear] = data;
	}
	//从数组队列里读取数据--出队
	public int getQueue() {
		if(isEmpty()) {
			throw new RuntimeException("数组队列是空的");
		}
		this.front ++;
		return this.arr[this.front];
	}
	//获取数组队列的全部数据
	public void showQueue() {
		if(isEmpty()) {
			throw new RuntimeException("数组队列是空的");
		}
		for (int i = this.front+1; i < this.rear+1; i++) {
			System.out.printf("arr[%d] = %d\n",i,arr[i]);
		}
	}
	//获取数组队列的头数据
	public int headQueue() {
		if(isEmpty()) {
			throw new RuntimeException("数组队列是空的");
		}
		return this.arr[this.front+1];
	}
}

问题:当依次执行指令a、g后再执行s可以发现提示"数组队列是空的",理论上还可以存入3个数据,但是再执行两次指令a后再执行指令a,就会出现下面的提示:
【Java数据结构】用数组模拟队列与环形队列_第2张图片
原因:目前数组使用一次就不能用, 没有达到复用的效果
解决办法:将这个数组使用算法,改进成一个环形的队列 (取模:%)

数组模拟队列示意图及思路:

【Java数据结构】用数组模拟队列与环形队列_第3张图片
思路如下:

  1. front 就指向队列的第一个元素,front 的初始值为0
  2. rear 指向队列的最后一个元素的后一个位置,空出一个空间做为约定,rear 的初始值为0
  3. 当队列满时,条件是 (rear + 1) % maxSize == front
  4. 当队列为空时,条件是 rear == front
  5. 队列中有效数据的个数为 (rear + maxSize - front) % maxSize
  6. 将front与rear的增长设置为以下形式:front/rear=(front/rear + 1)%maxSize

数组模拟环形队列代码实现

import java.util.Scanner;
public class TestCircleQueueArray {
	public static void main(String[] args) {
		boolean loop = true;
		CircleQueueArray queue = new CircleQueueArray(4);
		Scanner scan = new Scanner(System.in);
		while(loop) {
			System.out.println(""
					+ "e(exit):退出系统\n"
					+ "a(add):向队列中添加数据\n"
					+ "g(get):从队列中取出数据\n"
					+ "h(head):查看头数据\n"
					+ "s(show):查看队列所有数据");
			System.out.print("请输入您的指令:");
			String key = scan.next();
			switch (key) {
			case "e":
				loop = false;
				scan.close();
				break;
			case "a":
				System.out.print("请输入一个数据:");
				int value = scan.nextInt();
				queue.addQueue(value);
				break;
			case "g":
				try {
					System.out.printf("取出的数据为:%d\n",queue.getQueue());
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case "h":
				try {
					System.out.printf("头数据为:%d\n",queue.headQueue());
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case "s":
				try {
					queue.showQueue();
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;	
			default:
				System.out.println("请检查您的指令是否正确!");
				break;
			}
		}
		System.out.println("退出程序!");
	}
}

//用数组模拟一个环形队列
class CircleQueueArray {
	private int maxSize; // 标记数组队列的最大容量
	private int front; // 标记数组队列的头数据的下标,初始值为0
	private int rear; // 标记数组队列的尾数据后一个位置的下标,初始值为0
	private int[] arr;

	public CircleQueueArray(int maxSize) {
		this.maxSize = maxSize;
		this.arr = new int[this.maxSize];
	}
	// 判断数组队列是否已满
	public boolean isFull() {
		//实际上还有一个位置,但是留下来作为约定
		return (this.rear + 1) % this.maxSize == this.front;
	}
	// 判断数组队列是否为空
	public boolean isEmpty() {
		return this.rear == this.front;
	}
	// 向数组队列里添加数据--入队
	public void addQueue(int data) {
		if (isFull()) {
			System.out.println("数组队列满了,不能添加数据");
			return;
		}
		//将传入的数据赋值给当前rear指向的位置
		this.arr[rear] = data;
		//将rear下移一位,使其始终指向尾数据的后一位
		this.rear = (this.rear + 1) % this.maxSize;
	}
	//从数组队列里读取数据--出队
	public int getQueue() {
		if(isEmpty()) {
			throw new RuntimeException("数组队列是空的");
		}
		//记录当前front位置的数据
		int value = this.arr[this.front];
		//将front下移一位,使其始终指向环形队列的头数据
		this.front = (this.front + 1) % this.maxSize;
		return value;
	}
	//获取数组队列的全部数据
	public void showQueue() {
		if(isEmpty()) {
			throw new RuntimeException("数组队列是空的");
		}
		for (int i = this.front; i < this.front + size(); i ++) {
			//i可能会大于队列的maxSize,因此对maxSize取模
			System.out.printf("arr[%d] = %d\n", i%this.maxSize, arr[i%this.maxSize]);
		}
	}
	
	//获取数组队列的有效数据个数
	public int size() {
		//对于非环形队列而言,rear-front即是有效数据个数
		//而在环形队列中,rear的值可能比front小,因此需要加上maxSize并对maxSize取模
		return (this.rear - this.front + this.maxSize) % this.maxSize;
	}
	
	//获取数组队列的头数据
	public int headQueue() {
		if(isEmpty()) {
			throw new RuntimeException("数组队列是空的");
		}
		//该方法只是查看头数据,而非取出,因此不需要移动front指针
		return this.arr[this.front];
	}
}

如此一来就实现了环形队列,可以复用

你可能感兴趣的:(DataStructure)