稀疏数组(sparse array)

看一个实际的需求

编写的五子棋程序中, 有存盘退出, 继续上盘的功能
image.png
如果是默认的话, 我们可以采用二维数组来记录五子棋的位置

分析问题

因为该二位数组的很多值默认都是0,因此记录了很多没有意义的数据
那该则么解决呢? 没错 使用稀疏数组解决

基本介绍

当一个数组中大部分元素为0, 或者为同一个值的数组时, 可以采用稀疏数组来保存该数组
稀疏数组的处理方法是:

  1. 记录数组一共有几行几列,有多少个不同的值
  2. 把具有不同值的元素的行列及值记录在一个小规模的数组中, 从而缩小程序的规模

    举例说明

    原始二位数组
0 0 0 22 0 0 15
0 1 0 0 0 17 0
0 0 0 -6 0 0 0
0 0 0 0 0 39 0
91 0 0 0 0 0 0
0 0 28 0 0 0 0

将一个6x7的二位数组转化为一个3x9的稀疏数组,从42缩小到27, 缩小了15个单位

描述
0 6 7 8 原二位数组为6行7列,8个位置有值
1 0 3 22 第1行第3列,值为22, 因为索引从0开始
2 0 6 15
3 1 1 1
4 1 5 17
5 2 3 -6
6 3 5 39
7 4 0 91
8 5 2 28

应用实例

  1. 使用稀疏数组, 来保留类似前面的二维数组(棋盘, 地图等等)
  2. 把稀疏数组存盘, 并可以重新恢复成原始的二位数组
  3. 整体思路分析

image.png

二维数组转稀疏数组的思路

  1. 遍历原始的二维数组, 得到有效数据的个数 sum, 在这里我们的无效数据就默认为0, 如果不是, 需要先定义无效数据的值
  2. 根据有效数据的个数, 创建稀疏数组sparse array = new int[sum + 1][3]
    1. sum + 1代表需要多一行来存放 行数,列数,和值的个数
    2. 3代表稀疏数组固定3列
  3. 将二维数组的有效数据存入到 稀疏数组中

    1. 将横坐标存入第1列, 纵坐标存入第二列, 值存入第三列

      稀疏数组恢复成原始二位数组的思路

  4. 先读取稀疏数组的第一行, 根据第一行的数据, 创建原始的二维数组, 比如上面的new int[11][11]

  5. 在读取稀疏数组的第二行到第N行的数据, 并根据横坐标+纵坐标+值, 设置到原始二维数组中

    代码实现

    创建项目

    IDEA创建一个mavend的项目就可以,视频中是Eclipse,但是我用IDEA

    编写代码

    ```java package com.dance.sparearray;

import java.util.ArrayList; import java.util.Arrays; import java.util.List;

/**

  • 稀疏数组 */ public class SpareArray {

    public static void main(String[] args) {

    1. int[][] chessArr = createSourceArray();
    2. // 原始数组转为稀疏素组
    3. int[][] sparseArray = convertSparseArray(chessArr);
    4. // 稀疏数组转为原始数组
    5. int[][] sourceArray = convertSourceArray(sparseArray);

    }

    public static int[][] convertSourceArray(int[][] sparseArray){

    1. int[][] sourceArray = sparseArrayToArray(sparseArray);
    2. System.out.println("稀疏数组转为原始数组:");
    3. printf(sourceArray);
    4. return sourceArray;

    }

    public static int[][] convertSparseArray(int[][] chessArr){

    1. System.out.println("原始数组:");
    2. printf(chessArr);
    3. // 原始数组转为稀疏数组
    4. int[][] sparseArray = arrayToSparseArray(chessArr);
    5. System.out.println("原始数组转为稀疏数组:");
    6. printf(sparseArray);
    7. return sparseArray;

    }

    public static int[][] createSourceArray(){

    1. // 创建原始二维数组
    2. int[][] chessArr = new int[11][11];
    3. // 1表示黑子, 2表示蓝子
    4. chessArr[1][2] = 1;
    5. chessArr[2][3] = 2;
    6. return chessArr;

    }

    public static int[][] sparseArrayToArray(int[][] sparseArray){

    1. // 根据第一行创建原始数组
    2. int x = sparseArray[0][0];
    3. int y = sparseArray[0][1];
    4. int sum = sparseArray[0][2];
    5. int[][] sourceArray = new int[x][y];
    6. // 根据sum还原数据
    7. for (int i = 1; i < sparseArray.length; i++) {
    8. sourceArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
    9. }
    10. return sourceArray;

    }

    /**

    • 原始数组转为稀疏数组
    • @param chessArr 原始数组
    • @return 稀疏数组 */ public static int[][] arrayToSparseArray(int[][] chessArr){ // 将二维数组转为稀疏数组 int sum = 0; // 记录数据位置, 下面赋值可以不用二次循环 List valueIndices = new ArrayList<>();

      // 获取非0值的个数 for (int x = 0; x < chessArr.length; x++) {

      1. for (int y = 0; y < chessArr[x].length; y++) {
      2. int value = chessArr[x][y];
      3. if (value != 0) {
      4. ValueIndex valueIndex = new ValueIndex();
      5. valueIndex.x = x;
      6. valueIndex.y = y;
      7. valueIndex.value = value;
      8. valueIndices.add(valueIndex);
      9. sum++;
      10. }
      11. }

      } System.out.println(“数值个数: “ + sum);

      // 根据数值个数创建稀疏数组 int[][] sparseArray = new int[sum + 1][3]; // 给稀疏数字赋值 // 行 sparseArray[0][0] = chessArr.length; // 列 sparseArray[0][1] = chessArr[0].length; // 数值个数 sparseArray[0][2] = sum;

      // 稀疏数组赋值 for (int i = 1; i < sparseArray.length; i++) {

      1. // 获取一个存入
      2. ValueIndex valueIndex = valueIndices.remove(0);
      3. sparseArray[i][0] = valueIndex.x;
      4. sparseArray[i][1] = valueIndex.y;
      5. sparseArray[i][2] = valueIndex.value;

      } return sparseArray; }

      /**

    • 输出二维数组
    • @param chessArr 二维数组 */ public static void printf(int[][] chessArr) { Arrays.stream(chessArr).forEach(x -> {

      1. Arrays.stream(x).forEach(y -> System.out.printf("%d\t", y));
      2. System.out.println();

      }); }

      static class ValueIndex { int x; int y; int value; }

}

  1. <a name="q5s7j"></a>
  2. ## 测试执行
  3. 执行结果
  4. ```java
  5. 原始数组:
  6. 0 0 0 0 0 0 0 0 0 0 0
  7. 0 0 1 0 0 0 0 0 0 0 0
  8. 0 0 0 2 0 0 0 0 0 0 0
  9. 0 0 0 0 0 0 0 0 0 0 0
  10. 0 0 0 0 0 0 0 0 0 0 0
  11. 0 0 0 0 0 0 0 0 0 0 0
  12. 0 0 0 0 0 0 0 0 0 0 0
  13. 0 0 0 0 0 0 0 0 0 0 0
  14. 0 0 0 0 0 0 0 0 0 0 0
  15. 0 0 0 0 0 0 0 0 0 0 0
  16. 0 0 0 0 0 0 0 0 0 0 0
  17. 数值个数: 2
  18. 原始数组转为稀疏数组:
  19. 11 11 2
  20. 1 2 1
  21. 2 3 2
  22. 稀疏数组转为原始数组:
  23. 0 0 0 0 0 0 0 0 0 0 0
  24. 0 0 1 0 0 0 0 0 0 0 0
  25. 0 0 0 2 0 0 0 0 0 0 0
  26. 0 0 0 0 0 0 0 0 0 0 0
  27. 0 0 0 0 0 0 0 0 0 0 0
  28. 0 0 0 0 0 0 0 0 0 0 0
  29. 0 0 0 0 0 0 0 0 0 0 0
  30. 0 0 0 0 0 0 0 0 0 0 0
  31. 0 0 0 0 0 0 0 0 0 0 0
  32. 0 0 0 0 0 0 0 0 0 0 0
  33. 0 0 0 0 0 0 0 0 0 0 0

ok, 我们成功的实现了将原始数组转换为稀疏数组,再将稀疏数组恢复成了原始数组

队列

队列的使用场景

例如: 银行叫号,排队等待
image.png

队列介绍

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

image.png

数组模拟队列

思路

  1. 队列本身是有序列表, 若使用数组的结构来存储队列的数组, 则队列数组的声明如上图, 其中MaxSize是该队列的最大容量
  2. 因为队列的输出, 输入是分别从前后端来处理的, 因此需要两个变量front, 及rear, 分别记录队列的前后端的下标, front会随着数据的输出而改变, 而rear则是随着数据输入而改变, 如图所示
  3. 当我们将数据存入队列时称为入队(“addQueue”)
    1. addQueue的处理需要有两个步骤
    2. 将尾部指针向后移动: rear + 1, 当front == rear 就是无数据[空]
    3. 若尾部指针rear小于队列的最大下标maxSize-1, 则将数据存入rear所指的数组元素中,否则无法存入数据, 当rearr = maxSize -1 就是队列已经满了[队列满]

      代码实现

      ```java package com.dance.queue;

import java.util.Scanner;

public class ArrayQueueDemo {

public static void main(String[] args) {
    ArrayQueue queue = new ArrayQueue(3);
    menuMain(queue);
}

public static void menuMain(ArrayQueue queue) {
    Scanner scanner = new Scanner(System.in);
    char key = ' ';
    while (key != 'e') {
        printMenu();
        // 获取用户输入
        key = scanner.next().charAt(0);
        executeMenu(key, scanner, queue);
    }
}

public static void printMenu() {
    System.out.println("a(addQueue) : 添加队列数据");
    System.out.println("g(getQueue) : 获取队列数据");
    System.out.println("s(showQueue) : 展示全部数据");
    System.out.println("h(headQueue) : 获取头部数据");
    System.out.println("e(exit) : 退出程序");
}

public static void executeMenu(char key, Scanner scanner, ArrayQueue queue) {
    switch (key) {
        case 'a':
            System.out.println("请输入要添加的数据:");
            int i = scanner.nextInt();
            System.out.println("添加数据到队列: " + i);
            queue.addQueue(i);
            break;
        case 'g':
            try {
                System.out.println("获取到的数据为: "+queue.getQueue());
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            break;
        case 's':
            queue.showQueue();
            break;
        case 'h':
            try {
                System.out.println("队列头部数据为: " + queue.headQueue());
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            break;
        case 'e':
            scanner.close();
            System.out.println("程序退出~~~");
    }
}

}

/**

  • 使用数组模拟-队列 */ class ArrayQueue {

    /**

    • 数组最大值 */ private int maxSize;

      /**

    • 队列首指针 */ private int front;

      /**

    • 队列尾指针 */ private int rear;

      /**

    • 数据存放数组 */ private int[] array;

      /**

    • 创建队列的构造器
    • front = -1 , 指向队列头部, 分析出front是指向队列头的前一个位置
    • rear = -1 , 指向队列尾部, 指向队列尾部的数据(即就是队列的最后一个数据) *
    • @param maxSize 队列最大容量 */ public ArrayQueue(int maxSize) { this(maxSize, -1, -1); }

      private ArrayQueue(int maxSize, int front, int rear) { this.maxSize = maxSize; this.front = front; this.rear = rear; this.array = new int[maxSize]; }

      /**

    • 队列是否有可用空间 *
    • @return true: 没有 , false: 有 */ public boolean arrayIsFull() { return rear == maxSize - 1; }

      /**

    • 队列是否为空 *
    • @return true: 为空, false: 不为空 */ public boolean arrayIsEmpty() { return front == rear; }

      /**

    • 添加数据到队列中 *
    • @param newValue 要入队的值 */ public void addQueue(int newValue) { if (arrayIsFull()) {

       System.out.println("队列已经存满, 不能存入: " + newValue);
       return;
      

      } array[++rear] = newValue; }

      /**

    • 获取元素 *
    • @return 返回最先进入的数据, 保证队列的先入先出 */ public int getQueue() { if (arrayIsEmpty()) {

       throw new ArrayIndexOutOfBoundsException("数组为空");
      

      } // 开始位置后移 return array[++front]; }

      /**

    • 展示队列的全部数据 */ public void showQueue() { if (arrayIsEmpty()) {

       System.out.println("数组为空");
       return;
      

      } for (int i = 0; i < array.length; i++) {

       System.out.printf("arr[%d]=%d\n", i, array[i]);
      

      } }

      /**

    • 获取队列的头节点 *
    • @return 队列头部的数据 */ public int headQueue() { if (arrayIsEmpty()) {
       throw new ArrayIndexOutOfBoundsException("数组为空");
      
      } // 不能使用++ 因为会改变自身的值, 这里并不取数据只是 查看头部 return array[front + 1]; } }
      <a name="FxpnF"></a>
      ### 执行测试
      ```java
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      a
      请输入要添加的数据:
      10
      添加数据到队列: 10
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      s
      arr[0]=10
      arr[1]=0
      arr[2]=0
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      g
      获取到的数据为: 10
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      g
      数组为空
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      s
      数组为空
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      a
      请输入要添加的数据:
      20
      添加数据到队列: 20
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      s
      arr[0]=10
      arr[1]=20
      arr[2]=0
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      h
      队列头部数据为: 20
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      g
      获取到的数据为: 20
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      g
      数组为空
      a(addQueue) : 添加队列数据
      g(getQueue) : 获取队列数据
      s(showQueue) : 展示全部数据
      h(headQueue) : 获取头部数据
      e(exit) : 退出程序
      e
      程序退出~~~
      

      问题分析并优化

  1. 数组使用一次就不能用了, 没有达到内存复用效果

    a(addQueue) : 添加队列数据
    g(getQueue) : 获取队列数据
    s(showQueue) : 展示全部数据
    h(headQueue) : 获取头部数据
    e(exit) : 退出程序
    a
    请输入要添加的数据:
    11
    添加数据到队列: 11
    a(addQueue) : 添加队列数据
    g(getQueue) : 获取队列数据
    s(showQueue) : 展示全部数据
    h(headQueue) : 获取头部数据
    e(exit) : 退出程序
    a
    请输入要添加的数据:
    12
    添加数据到队列: 12
    a(addQueue) : 添加队列数据
    g(getQueue) : 获取队列数据
    s(showQueue) : 展示全部数据
    h(headQueue) : 获取头部数据
    e(exit) : 退出程序
    a
    请输入要添加的数据:
    13
    添加数据到队列: 13
    a(addQueue) : 添加队列数据
    g(getQueue) : 获取队列数据
    s(showQueue) : 展示全部数据
    h(headQueue) : 获取头部数据
    e(exit) : 退出程序
    a
    请输入要添加的数据:
    14
    添加数据到队列: 14
    队列已经存满, 不能存入: 14
    a(addQueue) : 添加队列数据
    g(getQueue) : 获取队列数据
    s(showQueue) : 展示全部数据
    h(headQueue) : 获取头部数据
    e(exit) : 退出程序
    s
    arr[0]=11
    arr[1]=12
    arr[2]=13
    a(addQueue) : 添加队列数据
    g(getQueue) : 获取队列数据
    s(showQueue) : 展示全部数据
    h(headQueue) : 获取头部数据
    e(exit) : 退出程序
    
  2. 将这个数组使用算法, 改进成一个环形队列, 取模: %

    使用数组模拟环形队列-优化

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

    分析说明

  3. 尾部索引的下一个为头部索引时表示队列满, 即将队列容量空出一个作为约定, 这个在做判断队列满的时候需要注意: (rear + 1) % maxSize == front , 表达式成立代表队列满

  4. rear == front 代表队列为空
  5. 分析示意图

image.png

思路如下

  1. front变量的含义做一个调整: front就指向队列的第一个元素, 也就是说arr[front]就是队列的第一个元素, front的初始值为0
  2. rear变量的含义做一个调整: rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定,rear的初始值为0
  3. 当队列满时, 条件是 (rear + 1) % maxSize = front
  4. 当队列为空时, 条件是 rear == front
  5. 当我们这样分析. 队列中有效的数据的个数 (rear + maxSize - front ) % maxSize // rear=1 front = 0
  6. 我们就可以在原来的队列上修改得到一个环形队列

    代码实现

    ```java package com.dance.queue;

import java.util.Scanner;

public class CircleArrayQueueDemo {

public static void main(String[] args) {
    CircleArrayQueue queue = new CircleArrayQueue(3);
    menuMain(queue);
}

public static void menuMain(CircleArrayQueue queue) {
    Scanner scanner = new Scanner(System.in);
    char key = ' ';
    while (key != 'e') {
        printMenu();
        // 获取用户输入
        key = scanner.next().charAt(0);
        executeMenu(key, scanner, queue);
    }
}

public static void printMenu() {
    System.out.println("a(addQueue) : 添加队列数据");
    System.out.println("g(getQueue) : 获取队列数据");
    System.out.println("s(showQueue) : 展示全部数据");
    System.out.println("h(headQueue) : 获取头部数据");
    System.out.println("e(exit) : 退出程序");
}

public static void executeMenu(char key, Scanner scanner, CircleArrayQueue queue) {
    switch (key) {
        case 'a':
            System.out.println("请输入要添加的数据:");
            int i = scanner.nextInt();
            System.out.println("添加数据到队列: " + i);
            queue.addQueue(i);
            break;
        case 'g':
            try {
                System.out.println("获取到的数据为: " + queue.getQueue());
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            break;
        case 's':
            queue.showQueue();
            break;
        case 'h':
            try {
                System.out.println("队列头部数据为: " + queue.headQueue());
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            break;
        case 'e':
            scanner.close();
            System.out.println("程序退出~~~");
    }
}

}

/**

  • 使用数组模拟-队列 */ class CircleArrayQueue {

    /**

    • 数组最大值 */ private int maxSize;

      /**

    • 队列首指针 */ private int front;

      /**

    • 队列尾指针 */ private int rear;

      /**

    • 数据存放数组 */ private int[] array;

      /**

    • 创建队列的构造器
    • front = -1 , 指向队列头部, 分析出front是指向队列头的前一个位置
    • rear = -1 , 指向队列尾部, 指向队列尾部的数据(即就是队列的最后一个数据) *
    • @param maxSize 队列最大容量 */ public CircleArrayQueue(int maxSize) { this(maxSize, 0, 0); }

      private CircleArrayQueue(int maxSize, int front, int rear) { this.maxSize = maxSize; this.front = front; this.rear = rear; this.array = new int[maxSize]; }

      /**

    • 队列是否有可用空间 *
    • @return true: 没有 , false: 有 */ public boolean arrayIsFull() { return (rear + 1) % maxSize == front; }

      /**

    • 队列是否为空 *
    • @return true: 为空, false: 不为空 */ public boolean arrayIsEmpty() { return front == rear; }

      /**

    • 添加数据到队列中 *
    • @param newValue 要入队的值 */ public void addQueue(int newValue) { if (arrayIsFull()) {

       System.out.println("队列已经存满, 不能存入: " + newValue);
       return;
      

      } array[rear] = newValue; // 将rear后移 这里必须考虑取模 rear = (rear + 1) % maxSize; }

      /**

    • 获取元素 *
    • @return 返回最先进入的数据, 保证队列的先入先出 */ public int getQueue() { if (arrayIsEmpty()) {

       throw new ArrayIndexOutOfBoundsException("数组为空");
      

      } int resultIndex = front;

      // 这里需要分析出 front是指向队列的第一个元素 // 先把 front 保存到临时的变量 // 将front 后移 // 使用临时变量返回 // 后移时考虑取模, 防止移动到最后 front = (front + 1) % maxSize;

      return array[resultIndex]; }

      /**

    • 展示队列的全部数据 */ public void showQueue() { if (arrayIsEmpty()) {

       System.out.println("数组为空");
       return;
      

      } // 从front 开始遍历 // 获取有效数据的个数 int length = (rear + maxSize - front) % maxSize; for (int i = front; i < front + length; i++) {

       System.out.printf("arr[%d]=%d\n", i % maxSize, array[i % maxSize]);
      

      } }

      /**

    • 获取队列的头节点 *
    • @return 队列头部的数据 */ public int headQueue() { if (arrayIsEmpty()) {
       throw new ArrayIndexOutOfBoundsException("数组为空");
      
      } // 不能使用++ 因为会改变自身的值, 这里并不取数据只是 查看头部 return array[front]; } } ```

      测试执行

      ```java a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 a 请输入要添加的数据: 10 添加数据到队列: 10 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 a 请输入要添加的数据: 20 添加数据到队列: 20 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 a 请输入要添加的数据: 30 添加数据到队列: 30 队列已经存满, 不能存入: 30 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 g 获取到的数据为: 10 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 g 获取到的数据为: 20 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 a 请输入要添加的数据: 10 添加数据到队列: 10 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 a 请输入要添加的数据: 50 添加数据到队列: 50 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 a 请输入要添加的数据: 60 添加数据到队列: 60 队列已经存满, 不能存入: 60 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 s arr[2]=10 arr[0]=50 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 g 获取到的数据为: 10 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 g 获取到的数据为: 50 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 s 数组为空 a(addQueue) : 添加队列数据 g(getQueue) : 获取队列数据 s(showQueue) : 展示全部数据 h(headQueue) : 获取头部数据 e(exit) : 退出程序 e 程序退出~~~
成功解决了,数据存放的问题, 但是里面还是放弃了一个数据位置用来做约定
<a name="sSIHv"></a>
### 图解代码
因为我对算法也不懂, 看弹幕上说的是都懂了,但是我~,图解一下吧<br />先说一下取模是什么吧, 我也不太懂, 感觉应该是取余数<br />在控制台执行了一下, 就是这样的结果
```java
1 % 3
1 剩余1
2 % 3
2 剩余2
3 % 3
0 剩余0
4 % 3
1 剩余1 后面应该是循环了

02-数据结构与算法 稀疏数组与队列 - 图6