一、稀疏sparsearray数组

1、先看一个实际的需求

问题编写的五子棋程序中,有存盘退出和续上盘的功能
我们以前使用的就是使用一个二维数组,把棋盘上的存在和不存在的点都用不同的标志保存起来如下图:
image.png
问题分析:
因为这个棋盘上很多都是空的,但是我们二维数组空的也得有值对应,导致很多值是默认值0,因此记录了很多没有意义的数据。这时候我们要是使用稀疏数组。

2、基本介绍

当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。

2.1稀疏数组的处理方法

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

2.2稀疏数组举例说明

image.png

3、应用实例

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

image.png

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

  1. 便利 原始的二维数组,得到有效数据的个数 sum
  2. 根据sum就可以创建稀疏数组 sparseArr 行int[sum+1] 列[3]
  3. 将二维数组中的有效数据存储到稀疏数组中

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

  1. 先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如 上面的chessArr2=int[11][11]
  2. 再读取稀疏数组后几行的数据,并赋值给原始的二维数组即可。

    3.1代码

    ```java package datastructures.sparsearray;

/**

  • @author : [Zara-cat]
  • @version : [v1.0]
  • @className : SparseArray
  • @description : [稀疏数组]
  • @createTime : [2021/10/9 13:22]
  • @updateUser : [Zara-cat]
  • @updateTime : [2021/10/9 13:22]
  • @updateRemark : [描述说明本次修改内容] */ public class SparseArray { public static void main(String[] args) {

    1. //创建一个原始的二维数组 11*11
    2. //0:表示没有棋子 1:表示黑子 2:表示蓝子
    3. int chessArr[][] = new int[11][11];
    4. chessArr[1][2] = 1;
    5. chessArr[2][3] = 2;
    6. chessArr[4][5] = 2;
    7. //打印一下原始数组
    8. //取出二维数组每一行
    9. System.out.println("原始的二维数组");
    10. for (int[] row : chessArr){
    11. //遍历每一行
    12. for (int data : row){
    13. System.out.printf("%d\t",data);
    14. }
    15. System.out.println();
    16. }
    17. //将二维数组 转 稀疏数组
    18. //1.先遍历二维数组,得到非0数据的个数
    19. int sum = 0;
    20. for (int i = 0 ;i<11;i++){
    21. for (int j = 0; j < 11; j++) {
    22. if (chessArr[i][j]!=0)
    23. sum++;
    24. }
    25. }
    26. System.out.println("sum = "+sum);
    27. //2.创建对应的稀疏数组
    28. int sparseArr[][] = new int[sum + 1][3];
    29. //3、给稀疏数组赋值
    30. sparseArr[0][0] = 11;
    31. sparseArr[0][1] = 11;
    32. sparseArr[0][2] = sum;
    33. //遍历二维数组,将非0的值赋值到稀疏数组中
    34. //count 用于记录是第几个非0的数据
    35. int count = 0;
    36. for (int i = 0 ;i<11;i++){
    37. for (int j = 0; j < 11; j++) {
    38. if (chessArr[i][j]!=0){
    39. count++;
    40. sparseArr[count][0] = i;
    41. sparseArr[count][1] = j;
    42. sparseArr[count][2] = chessArr[i][j];
    43. }
    44. }
    45. }
    46. //输出稀疏数组的形式
    47. System.out.println();
    48. System.out.println("得到的稀疏数组为:");
    49. for (int[] row :sparseArr){
    50. for (int data : row){
    51. System.out.printf("%d\t",data);
    52. }
    53. System.out.println();
    54. }
    55. //将稀疏数组===>恢复到原来的二维数组
    56. //1.先读取稀疏数据第一行,根据第一行数据,创建原始二维数组
    57. int chessArrOld[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
    58. //2.再读取稀疏数组后几行的数据,并赋给原来的二维数组即可
    59. //第二行开始遍历
    60. for (int i = 1;i<sparseArr.length;i++){
    61. chessArrOld[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
    62. }
    63. System.out.println();
    64. System.out.println("恢复后的二维数组:");
    65. for (int[] row : chessArrOld){
    66. for (int data : row){
    67. System.out.printf("%d\t",data);
    68. }
    69. System.out.println();
    70. }

    }

}

**运行结果:**<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/12768333/1633769267428-d17669c2-6d61-46d4-a104-f67ab4b80bdc.png#clientId=ud48e3e74-136a-4&from=paste&height=714&id=ua05df387&margin=%5Bobject%20Object%5D&name=image.png&originHeight=714&originWidth=608&originalType=binary&ratio=1&size=22950&status=done&style=shadow&taskId=uf4d0ea61-3de1-4339-bb5e-54d1cff2a7c&width=608)
<a name="Jo5FH"></a>
# 二、队列
<a name="ijLRL"></a>
## 1、队列的一个使用场景
银行排队的案例
<a name="XaXjT"></a>
## ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12768333/1633772137863-0d84a5b8-ee75-42bf-9ce2-2f879e5913b6.png#clientId=u2fb03bed-fb1f-4&from=paste&height=243&id=ud9d30169&margin=%5Bobject%20Object%5D&name=image.png&originHeight=243&originWidth=372&originalType=binary&ratio=1&size=41577&status=done&style=none&taskId=u31a319a3-25b5-42b7-af26-2f7256c9cc5&width=372)
<a name="SG4S9"></a>
## 2、队列介绍

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

![image.png](https://cdn.nlark.com/yuque/0/2021/png/12768333/1633772611078-6ee8ea9b-fcf8-409f-b5c2-77bf70127feb.png#clientId=u2fb03bed-fb1f-4&from=paste&height=177&id=u81d562ad&margin=%5Bobject%20Object%5D&name=image.png&originHeight=177&originWidth=471&originalType=binary&ratio=1&size=54921&status=done&style=none&taskId=u26b96c9e-da38-4ce3-8cc8-b545eb9f725&width=471)
<a name="HEtEv"></a>
## 3、数组模拟队列思路

1. 队列本身就是有有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如上图,其中maxSize是该队列的最大容量
1. 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front和rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变。
1. 当我们将数据存入队列时称为“addQueue”,addQueue的处理需要有两个步骤:思路分析
   - 将尾指针往后移:rear+1,当front==rear【空】
   - 若尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所指的数组元素中,否者无法存入数据。rear == maxSize -1【队列满】
<a name="cy4u6"></a>
### 3.1 代码实现
```java
package datastructures.queue;

import java.util.Scanner;

/**
 * @author : [Zara-cat]
 * @version : [v1.0]
 * @className : ArrayQueue
 * @description : [使用数组模拟队列]
 * @createTime : [2021/10/9 18:11]
 * @updateUser : [Zara-cat]
 * @updateTime : [2021/10/9 18:11]
 * @updateRemark : [描述说明本次修改内容]
 */
public class ArrayQueue {
    //表示数组的最大容量
    private int maxSize;
    //队列头
    private int front;
    //队列尾
    private int rear;
    //该数组用于存放数据,模拟队列
    private int[] arr;

    //创建队列的构造器
    public  ArrayQueue(int arrMaxSize){
        this.maxSize = arrMaxSize;
        this.arr = new int[maxSize];
        //指向队列头部,分析出front是指向队列头的前一个位置
        this.front = -1;
        //指向队列尾部,指向队列尾的数据(即就是队列最后一个数据)
        this.rear = -1;
    }

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

    /**
     * 判断队列是否为空
     * @return
     */
    public boolean isEmpty(){
        return rear == front;
    }

    /**
     *
     * 添加数据到队列
     * @param n
     */
    public void addQueue(int n){
        //判断队列是否满
        if (isFull()){
            System.out.println("队列满,不能加入数据");
            return;
        }
        //rear++; //让rear后移 ,与下方++rear一样效果
        arr[++rear] = n;
    }

    /**
     * 获取队列的数据 ,出队列
     * @return
     */
    public int getQueue(){
        //判断队列是否空
        if (isEmpty()){
            throw new RuntimeException("队列为空,不能取数据");
        }
        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]);
        }
    }

    /**
     * 显示队列头数据,注意不是取出数据
     * @return
     */
    public int headQueue(){
        //判断
        if (isEmpty()){
            throw new RuntimeException("队列空的,没有数据");
        }
        return arr[front+1];
    }
    public static void main(String[] args) {
        //创建一个队列
        ArrayQueue arrayQueue = new ArrayQueue(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':
                    arrayQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("请输入一个数");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int queue = arrayQueue.getQueue();
                        System.out.println("取出的数据是:"+queue);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int i = arrayQueue.headQueue();
                        System.out.println("队列头的数据是:"+i);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    System.out.println("程序退出");
                    loop = false;
                    break;
            }
        }
    }
}

3.2 运行演示

自行运行

3.3 问题分析并优化

  1. 目前数组使用一次就不能用,也就是,当存储数组满了以后,同时也全部取出后,由于fron已经 == maxSize-1,已经不能再进行存储,但是其实我们队列已经是逻辑为空了。没有达到复用的效果。
  2. 将这个数组使用算法,改进成一个环形的队列,去模:%

    4、数组模拟环形队列

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

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

  4. rear变量的含义做一个调整:rear指向队列的最后一个元素的后一个位置。因为希望空出一个空间作为约定。rear的初始值为0
  5. 当队列满的时候,条件是(rear+1)%maxSize = front
  6. 当队列为空的时候,条件是 rear == front
  7. 队列中有效的数据的个数(rear+maxSize-front)% maxSize
  8. 这个队列总有一个格子为预留位,并且预留位位子是不断改变的,预留位不能存储有效数据

    4.1 代码实现

    ```java package datastructures.queue;

import java.util.Scanner;

/**

  • @author : [王振宇]
  • @version : [v1.0]
  • @className : CircleArrayQueue
  • @description : [数组模拟环形队列]
  • @createTime : [2021/10/11 10:31]
  • @updateUser : [王振宇]
  • @updateTime : [2021/10/11 10:31]
  • @updateRemark : [描述说明本次修改内容] */ public class CircleArrayQueue { //表示数组的最大容量 private int maxSize; //队列头 private int front; //队列尾 private int rear; //该数组用于存放数据,模拟队列 private int[] arr;

    //创建队列的构造器 public CircleArrayQueue(int arrMaxSize){

     this.maxSize = arrMaxSize;
     this.arr = new int[maxSize];
     //front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素。front的初始化值为0;
     this.front = 0;
     //rear指向队列的最后一个元素的后一个位置。因为希望空出一个空间作为约定。rear的初始值为0
     this.rear = 0;
    

    }

    /**

    • 判断队列是否满
    • @return */ public boolean isFull(){ return (rear + 1) % maxSize == front; }

      /**

    • 判断队列是否为空
    • @return */ public boolean isEmpty(){ return rear == front; }

      /*

    • 添加数据到队列
    • @param n */ public void addQueue(int n){ //判断队列是否满 if (isFull()){

       System.out.println("队列满,不能加入数据");
       return;
      

      } //将数据加入 arr[rear] = n; //rear 后移 rear = (rear + 1) % maxSize; }

      /**

    • 获取队列的数据 ,出队列
    • @return */ public int getQueue(){ //判断队列是否空 if (isEmpty()){
       throw new RuntimeException("队列为空,不能取数据");
      
      } //这里需要分析出front是指向队列的第一个元素 //1.先将front对应的值保留到一个临时变量 int value = arr[front]; //2.将front后移 front = (front + 1) % maxSize; return value; } /**
    • 显示队列的所有数据 */ public void showQueue(){ //遍历 if (isEmpty()){

       System.out.println("队列空,无法遍历");
       return;
      

      } //思路:从front开始遍历,遍历size个有效元素 for (int i=front;i< front+size();i++){

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

      } }

      /**

    • 显示队列头数据,注意不是取出数据
    • @return */ public int headQueue(){ //判断 if (isEmpty()){

       throw new RuntimeException("队列空的,没有数据");
      

      } return arr[front]; }

      /**

    • 求出当前队列有效数据的个数
    • @return */ public int size(){ return (rear + maxSize - front) % maxSize; } public static void main(String[] args) { //创建一个队列 CircleArrayQueue arrayQueue = new CircleArrayQueue(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':
               arrayQueue.showQueue();
               break;
           case 'a':
               System.out.println("请输入一个数");
               int value = scanner.nextInt();
               arrayQueue.addQueue(value);
               break;
           case 'g':
               try {
                   int queue = arrayQueue.getQueue();
                   System.out.println("取出的数据是:"+queue);
               } catch (Exception e) {
                   System.out.println(e.getMessage());
               }
               break;
           case 'h':
               try {
                   int i = arrayQueue.headQueue();
                   System.out.println("队列头的数据是:"+i);
               } catch (Exception e) {
                   System.out.println(e.getMessage());
               }
               break;
           case 'e':
               scanner.close();
               System.out.println("程序退出");
               loop = false;
               break;
       }
      
      } } }

```

3.2 运行演示

自行运行