【数据结构】—— 队列(有序队列及环形队列的数组实现)

简介: 【数据结构】—— 队列(有序队列及环形队列的数组实现)

队列的一个使用场景

我们去银行办理业务时,如果人多且假设只有四个窗口时,我们就需要排队等候,先进去的先办理,办理结束之后先出去(先进先出)

什么是队列?

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

使用数组模拟队列示意图

数组模拟队列

队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如上图, 其中 maxSize 是该队列的最大容量。


因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front及 rear分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear则是随着数据输入而改变,如图所示:

                                  使用数组模拟队列示意图

实现思路:

当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:


(首先我们需要知道的是front和rear的初始值都为-1,都指向的是数组首元素的前一位)


1)将尾指针往后移:rear+1 , 当front == rear时 ,说明队列中还没有元素。


2)若尾指针 rear 小于队列的最大下标 maxSize-1(因为数组下标是从0开始的),则将数据存入 rear所指的数组元素中,否则无法存入数据( rear  == maxSize - 1[队列满] )


编写一个ArrayQueue类的代码实现过程

class ArrayQueue{
  private int maxSize;// 表示数组的最大容量
  private int front;// 队列头
  private int rear;// 队列尾
  private int[] arr;// 该数组用于存放数据,模拟队列
  // 创建队列的构造器
  public ArrayQueue(int arrMaxSize) {
    maxSize = arrMaxSize;
    arr = new int[maxSize];
    front = -1;// 指向队列头部,分析出front是指向队列头的前一个位置
    rear = -1;// 指向队列尾,指向队列尾的数据(即就是队列最后一个数据)
  }
}

判断队列是否满和空

  // 判断队列是否满
  public boolean isFull() {
    return rear == maxSize - 1;
  }
  // 判断队列是否空
  public boolean isEmpty() {
    return rear == front;
  }

入队出队

    // 添加数据到队列
  public void addQueue(int n) {
    // 判断队列是否满
    if (isFull()) {
      System.out.println("队列已满,不能再添加数据");
      return;
    }
    rear++;// 让rear后移
    arr[rear] = n;
  }
  // 获取队列数据,出队
  public int getQueue() {
    // 判断队列是否为空
    if (isEmpty()) {
      // 这里不能直接return,而是通过抛出异常来处理程序
      throw new RuntimeException("队列空不能取数据");
      // 这个位置不要再写代码(到不了没意义)
      // 另外需要返回值的方法,一定要给返回值或者抛出异常
    }
    front++;// front后移(因为front指向的是数组第一个元素的前一个)
    return arr[front];
  }

显示队列数据

  // 显示队列所有的数据
  public void showQueue() {
    // 遍历
    if (isEmpty()) {
      System.out.println("队列中没有数据");
      return;
    }
    for (int i = 0; i < arr.length; i++) {
      System.out.printf("arr[%d]=%d\n", i, arr[i]);
    }
  }
  // 显示队列的头数据(不是取出数据)
  public int headQueue() {
    if (isEmpty()) {
      throw new RuntimeException("队列中没有数据");
    }
    return arr[front + 1];
  }

问题

用数组模拟队列你会发现使用一次之后就不能使用了 ,没有达到复用的效果。

数组模拟环形队列

思路如下:

front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素 front 的初始值 = 0


rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定. rear 的初始值 = 0


当队列满时,条件是  (rear  + 1) % maxSize == front 【满】


对队列为空的条件, rear == front 空


当我们这样分析, 队列中有效的数据的个数   (rear + maxSize - front) % maxSize   // rear = 1 front = 0


我们就可以在原来的队列上修改得到,一个环形队列

                                使用数组模拟队列示意图

 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,


       这个在判断队列满的时候要注意


       (rear + 1)% maxSize == front  ---> 满


          rear == front ---> 空

网络异常,图片无法展示
|

代码实现

package com.atguigu.queue;
import java.util.Scanner;
public class CircleArrayQueueDemo {
  public static void main(String[] args) {
    //测试一把
    System.out.println("测试数组模拟环形队列的案例~~~");
    // 创建一个环形队列
    CircleArray queue = new CircleArray(4); //说明设置4, 其队列的有效数据最大是3
    char key = ' '; // 接收用户输入
    Scanner scanner = new Scanner(System.in);//
    boolean loop = true;
    // 输出一个菜单
    while (loop) {
      System.out.println("s(show): 显示队列");
      System.out.println("e(exit): 退出程序");
      System.out.println("a(add): 添加数据到队列");
      System.out.println("g(get): 从队列取出数据");
      System.out.println("h(head): 查看队列头的数据");
      key = scanner.next().charAt(0);// 接收一个字符
      switch (key) {
      case 's':
        queue.showQueue();
        break;
      case 'a':
        System.out.println("输出一个数");
        int value = scanner.nextInt();
        queue.addQueue(value);
        break;
      case 'g': // 取出数据
        try {
          int res = queue.getQueue();
          System.out.printf("取出的数据是%d\n", res);
        } catch (Exception e) {
          // TODO: handle exception
          System.out.println(e.getMessage());
        }
        break;
      case 'h': // 查看队列头的数据
        try {
          int res = queue.headQueue();
          System.out.printf("队列头的数据是%d\n", res);
        } catch (Exception e) {
          // TODO: handle exception
          System.out.println(e.getMessage());
        }
        break;
      case 'e': // 退出
        scanner.close();
        loop = false;
        break;
      default:
        break;
      }
    }
    System.out.println("程序退出~~");
  }
}
class CircleArray {
  private int maxSize; // 表示数组的最大容量
  //front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素 
  //front 的初始值 = 0
  private int front; 
  //rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
  //rear 的初始值 = 0
  private int rear; // 队列尾
  private int[] arr; // 该数据用于存放数据, 模拟队列
  public CircleArray(int arrMaxSize) {
    maxSize = arrMaxSize;
    arr = new int[maxSize];
  }
  // 判断队列是否满
  public boolean isFull() {
    return (rear + 1) % maxSize == front;
  }
  // 判断队列是否为空
  public boolean isEmpty() {
    return rear == front;
  }
  // 添加数据到队列
  public void addQueue(int n) {
    // 判断队列是否满
    if (isFull()) {
      System.out.println("队列满,不能加入数据~");
      return;
    }
    //直接将数据加入
    arr[rear] = n;
    //将 rear 后移, 这里必须考虑取模
    rear = (rear + 1) % maxSize;
  }
  // 获取队列的数据, 出队列
  public int getQueue() {
    // 判断队列是否空
    if (isEmpty()) {
      // 通过抛出异常
      throw new RuntimeException("队列空,不能取数据");
    }
    // 这里需要分析出 front是指向队列的第一个元素
    // 1. 先把 front 对应的值保留到一个临时变量
    // 2. 将 front 后移, 考虑取模
    // 3. 将临时保存的变量返回
    int value = arr[front];
    front = (front + 1) % maxSize;
    return value;
  }
  // 显示队列的所有数据
  public void showQueue() {
    // 遍历
    if (isEmpty()) {
      System.out.println("队列空的,没有数据~~");
      return;
    }
    // 思路:从front开始遍历,遍历多少个元素
    // 动脑筋
    for (int i = front; i < front + size() ; i++) {
      System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
    }
  }
  // 求出当前队列有效数据的个数
  public int size() {
    // rear = 2
    // front = 1
    // maxSize = 3 
    return (rear + maxSize - front) % maxSize;   
  }
  // 显示队列的头数据, 注意不是取出数据
  public int headQueue() {
    // 判断
    if (isEmpty()) {
      throw new RuntimeException("队列空的,没有数据~~");
    }
    return arr[front];
  }
}

●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                                ——By 作者:大耳朵宋宋

相关文章
|
2月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
281 9
|
9天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
125 75
|
9天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
34 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
9天前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
34 9
|
9天前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
27 7
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
86 5
|
2月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
68 4
|
2月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
43 1
|
2月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
103 21
|
2月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。

热门文章

最新文章