队列的一个使用场景
银行排队的案例:
队列介绍
队列是一个有序列表,可以用数组或是链表来实现。
遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
示意图:(使用数组模拟队列示意图)
数组模拟队列
队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front及 rear分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear则是随着数据输入而改变,如图所示:
当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析
将尾指针往后移:rear+1 , 当front == rear 【空】
若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear所指的数组元素中,否则无法存入数据。
rear == maxSize - 1[队列满]
代码实现
问题分析并优化
class ArrayQueue(arrMaxSize: Int) { val maxSize: Int = arrMaxSize
val array = new Array[Int](arrMaxSize)
var front: Int = -1
var rear: Int = -1
}
//初始化
val queue = new ArrayQueue(3)
rear 是队列最后[含]
front 是队列最前元素[不含]
数组模拟队列
出队列操作getQueue
显示队列的情况showQueue
查看队列头元素headQueue
退出系统exit
将原来的队列的查看队列头元素的代码写完.
数组模拟环形队列
对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过取模的方式来实现即可)
分析说明:
尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear + 1) % maxSize == front 满]
rear == front [空]
(cq.rear + cq.maxSize – cq.front) % cq.maxSize
class CircleQueue {
private int maxSize;
private int[] arr; // 该数组存放数据,模拟队列
private int front; // 指向队列头部
private int rear; // 指向队列的尾部
public CircleArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
}
public boolean isFull() {
//尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定(!!!)
return (rear + 1) % maxSize == front; }
public boolean isEmpty() {
this.tail == this.head }
public void addQueue(int n) {
if (isFull()) {
System.out.println("队列满,无法加入..");
return;}
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 int size() {
return (rear + maxSize - front) % maxSize;}}
小结
队列是有序列表
front 初始化为-1, 表示队列的头,但是约定不包含头元素, 即指向队列的第一个元素的前一个位置.
rear 初始化-1, 指向队列尾部,包含最后这个元素
判断队列空,front == rear 表示空
判断队列满, rear == maxSize -1
队列图解
队列代码
package datastructure
import scala.io.StdIn
/**
* 环形队列
* 如果不使用环形队列,则从队列中取出元素时不会释放队列的使用空间
* @author cherry
* @create 2019-09-17-22:45
*/
class CircleArrayQueueDemo(arrMaxSize: Int) {
val maxSize = arrMaxSize // 指定队列的大小
val arr = new Array[Int](maxSize) // 队列中数据,存放在数组,即数组模拟队列
//front 初始化为0, 表示队列的头,指向队列的第一个元素
var front = 0
//rear 初始化0, 指向队列最后这个元素的后一个位置
var rear = 0
//判断队列空
def isEmpty(): Boolean = rear == front
//判断满
def isFull(): Boolean = (rear + 1) % maxSize == front
//添加数据到队列
def addQueue(num: Int): Unit = {
if (isFull()) {
println("队列满,不能加入")
return
}
arr(rear) = num
//将rear 后移
rear = (rear + 1) % maxSize
}
//从队列中取出数据, 可能取得数据,可能取不到数据(异常)
def getQueue(): Any = {
if (isEmpty()) return new Exception("队列空,没有数据")
//因为front指向队列的第一个元素
val res = arr(front) //先将保存到临时变量
front = (front + 1) % maxSize // front后移
res //返回临时变量
}
//遍历显示队列, 动脑筋
//思路
// 1. 从front 开始打印,打印多少个元素
// 2. 所以,需要统计出该队列有多少个有效元素
def show(): Unit = {
if (isEmpty()) {
println("队列空")
return
}
//这里使用%方式解决
for (i <- front until front + size()) {
printf("arr(%d)=%d \t", i % maxSize, arr(i % maxSize))
}
}
//编写一个方法,统计当前有多少个元素
def size(): Int = (rear + maxSize - front) % maxSize
//查看队列的头元素,但是不取出
def peek(): Any = {
if (isEmpty()) return new Exception("队列空,无数据")
arr(front) //front 不要动
}
}
object CircleArrayQueueDemo {
def main(args: Array[String]): Unit = {
//测试一把
val queue = new CircleArrayQueueDemo(4)
//菜单演示
var key = ""
while (true) {
print("请选择菜单:show: 显示队列;add : 添加数据;get : 获取数据;peek : 取出数据;exit: 退出程序:")
println()
key = StdIn.readLine()
key match {
case "show" => queue.show()
case "add" =>
println("请输入一个数")
val num = StdIn.readInt()
queue.addQueue(num)
case "get" =>
//对取回的值,进行判断
val res = queue.getQueue()
//如果是异常
if (res.isInstanceOf[Exception]) {
println(res.asInstanceOf[Exception].getMessage)
} else {
//Int
printf("队列取出的值=%d", res)
}
case "peek" =>
//查看头元素值,进行判断
val res = queue.peek()
//如果是异常
if (res.isInstanceOf[Exception]) {
println(res.asInstanceOf[Exception].getMessage)
} else {
//Int
printf("队列当前头元素=%d", res)
}
}
}
}
}
测试代码
发现从数组队列中取出元素后还能再插入元素