队列

队列的一个使用场景

银行排队的案例:

 

队列介绍

  1. 队列是一个有序列表,可以用数组或是链表来实现。
  2. 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
  3. 示意图:(使用数组模拟队列示意图)

数组模拟队列

  1. 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下其中 maxSize 是该队列的最大容量。
  2. 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front(head) rear(tail)分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear则是随着数据输入而改变,如图所示:

    说明

    代码实现

    package com.atguigu.chapter18

    import scala.io.StdIn

    object ArrayQueueDemo {

    def main(args: Array[String]): Unit = {

    //初始化一个队列

    val queue = new ArrayQueue(3)

    var key = ""

    while (true) {

    println("show: 表示显示队列")

    println("exit: 表示退出程序")

    println("add: 表示添加队列数据")

    println("get: 表示取出队列数据")

    println("head: 查看队列头的数据(不改变队列)")

    key = StdIn.readLine()

    key match {

    case "show" => queue.showQueue()

    case "add" => {

    println("请输入一个数")

    val value = StdIn.readInt()

    queue.addQueue(value)

    }

    case "get" => {

    val res = queue.getQueue()

    if (res.isInstanceOf[Exception]) {

    println(res.asInstanceOf[Exception].getMessage)

    } else {

    println(s"取出数据是 $res")

    }

    }

    case "head" => {

    val res = queue.headQueue()

    if(res.isInstanceOf[Exception]) {

    //显示错误信息

    println(res.asInstanceOf[Exception].getMessage)

    }else {

    println("队列头元素值为=" + res)

    }

    }

    case "exit" => System.exit(0)

    }

    }

    }

    }

    //使用数组模拟队列

    //队列存在的问题是,数据空间不能复用

    class ArrayQueue(arrMaxSize: Int) {

    val maxSize = arrMaxSize

    val arr = new Array[Int](maxSize) //该数组存放数据,模拟队列

    var front = -1 // 指向队列头部, 分析出front 是指向队列数据的前一个位置

    var rear = -1 // 指向队列的尾部,分析出rear 是指向队列的最后数据()

    //判断队列是否满

    def isFull(): Boolean = {

    rear == maxSize - 1

    }

    //判断队列是否空

    def isEmpty(): Boolean = {

    front == rear

    }

    //添加数据到队列

    def addQueue(n: Int): Unit = {

    //判断是否满

    if (isFull()) {

    println("队列满,无法加入..")

    return

    }

    rear += 1 //先让rear 后移

    arr(rear) = n

    }

    def getQueue(): Any = {

    if (isEmpty()) {

    return new Exception("队列空~")

    }

    front += 1

    return arr(front)

    }

    //显示队列的所有数据

    def showQueue(): Unit = {

    if (isEmpty()) {

    println("队列空的,没有数据..")

    return

    }

    for (i <- front + 1 to rear) {

    printf("arr[%d]=%d ", i, arr(i))

    }

    }

    //查看队列的头元素,但是不是改变队列

    def headQueue(): Any = {

    if (isEmpty()) {

    return new Exception("队列空~")

    }

    //这里注意,不要去改变fornt

    return arr(front + 1)

    }

    }

    对上面代码的说明: 虽然实现了队列,但是数据空间不能复用,因此我们需要对其进行优化,使用取模的方式实现环形队列.

数组模拟环形队列

  1. 对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过取模的方
    式来实现即可)

    因为数组也可以通过取模的方式当做一个环形结构来使用,那么也可以解决约瑟夫问题.(提示,可以通过修改元素的值,来标志该元素对应的小孩是否出圈)

  • 分析说明:
  1. 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的
    时候需要注意 (rear + 1) % maxSize == front ]
  2. rear == front []
  3. 代码实现

    package com.atguigu.chapter18

    import scala.io.StdIn

    object CircleArrayQueueDemo {

    def main(args: Array[String]): Unit = {

    println("~~~环形队列的案例~~~")

    //初始化一个队列

    val queue = new CircleArrayQueue(4)

    var key = ""

    while (true) {

    println("show: 表示显示队列")

    println("exit: 表示退出程序")

    println("add: 表示添加队列数据")

    println("get: 表示取出队列数据")

    println("head: 查看队列头的数据(不改变队列)")

    key = StdIn.readLine()

    key match {

    case "show" => queue.showQueue()

    case "add" => {

    println("请输入一个数")

    val value = StdIn.readInt()

    queue.addQueue(value)

    }

    case "get" => {

    val res = queue.getQueue()

    if (res.isInstanceOf[Exception]) {

    println(res.asInstanceOf[Exception].getMessage)

    } else {

    println(s"取出数据是 $res")

    }

    }

    case "head" => {

    val res = queue.headQueue()

    if(res.isInstanceOf[Exception]) {

    //显示错误信息

    println(res.asInstanceOf[Exception].getMessage)

    }else {

    println("队列头元素值为=" + res)

    }

    }

    case "exit" => System.exit(0)

    }

    }

    }

    }

    //环形的队列和前面的单向队列有类似的地方,因为我们修改即可

    class CircleArrayQueue(arrMaxSize: Int) {

    val maxSize = arrMaxSize

    val arr = new Array[Int](maxSize) //该数组存放数据,模拟队列

    var front = 0 // 指向队列头部

    var rear = 0 // 指向队列的尾部

    //判断队列满的方法

    //队列容量空出一个作为约定

    def isFull(): Boolean = {

    //1 => rear 1

    //2 => rear 2

    //3 => rear 3

    (rear + 1) % maxSize == front

    }

    //判断队列空的条件

    def isEmpty(): Boolean = {

    rear == front

    }

    //添加数据到队列

    def addQueue(n: Int): Unit = {

    //判断是否满

    if (isFull()) {

    println("队列满,无法加入..")

    return

    }

    //将数据加入

    arr(rear) = n

    //然后将rear 后移, 这里必须考虑取模

    rear = (rear + 1) % maxSize

    }

    //取出队列的数据(按先进先出的原则)

    def getQueue(): Any = {

    if (isEmpty()) {

    return new Exception("队列空~")

    }

    //这里我们需要分析处理 front 已经指向了队列的头元素

    //1. 先把front 对应的数据保存到变量

    //2. front后移

    //3. 返回前面保存的变量值

    val value = arr(front)

    front = (front + 1) % maxSize

    return value

    }

    //显示队列的所有数据

    def showQueue(): Unit = {

    if (isEmpty()) {

    println("队列空的,没有数据..")

    return

    }

    //思路: front 取,取出几个元素

    //动脑筋

    for (i <- front until front + size()) {

    printf("arr[%d]=%d ", i % maxSize, arr(i % maxSize))

    }

    }

    //求出当前环形队列有几个元素

    //动脑筋

    //基础【问题/需求 ---> 设计算法】

    def size(): Int = {

    //rear = 1

    //front = 0

    //maxSize = 3

    (rear + maxSize - front) % maxSize //求出当前队列实际有多少个数据

    }

    //查看队列的头元素,但是不是改变队列

    def headQueue(): Any = {

    if (isEmpty()) {

    return new Exception("队列空~")

    }

    //这里注意,不要去改变fornt

    return arr(front)

    }

    }

课堂练习:

  1. 同学们完成环形数组模拟的队列的输出

    (cq.tail + cq.maxSize - cq.head) % cq.maxSize

原文地址:https://www.cnblogs.com/shuzhiwei/p/11210119.html