一、稀疏数组
1、稀疏数组实际需求
编写一个五子棋程序,有存盘退出和续上盘的功能。
分析问题:因为这个二维数组中很多的默认值都是0,因此记录了很多没有意义的数据,这时就可以用到稀疏数组
2、稀疏数组基本介绍
当一个数组中大部分元素都是0,或者都是同一个值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方法是:**
- 稀疏数组的第一行是 记录数组一共有几行几列(这里不是索引,从1开始),有多少个不同的值
- 把具有不同值得元素的行列及值记录在一个小规模的数组(稀疏数组)中,从而缩小程序的规模 —->
- ——————————>原来的二维数组是42个值,使用稀疏数组后变成27个值(节省了很大空间)
- 稀疏数组 永远是一个 行不确定(有效值的数量+1 = 真实行数),但是就三个列(行,列,值)的一个二维数组
- 稀疏数组第二行及以下 都是存放原始二维数组中 有效值的坐标(行和列分别是 下标[索引])
3、稀疏数组的应用实例
- 使用稀疏数组,来保留类似前面的二维数组(棋盘、地图等等)
- 把稀疏数组存盘,并且可以从新恢复原来的二维数组数
- 整体思路分析
4、二维数组转稀疏数组的思路:
- 遍历原始的二维数组,得到要保存的有效的数据的个数 sum。
- 根据sum就可以创建稀疏数组 sparseArray,
- 创建稀疏数组: 行是有效数据+1,列永远是3 ||
**int[][] sparseArray = new int[sum+1][3] ;** - 将二维数组的 有效数据的 坐标 和 值 存入到稀疏数组中
4、稀疏数组转原始的二维数组的思路:
- 读取到稀疏数组的第一行,读到这个原始二维数组 有几行 几列,几个有效值
- 根据第一行的数据创建原始二维数组,
**int[][] chessArray = new int[行][列];** - 在读取稀疏数组后几行的数据,并赋给这个原始的二维数组,【根据下标赋值】

二、稀疏数组代码实现
1、原始二维数组转换稀疏数组
//创建原始二维数组int[][] chessArray = new int[11][11];//给里面赋值,这个里面就是一个棋盘,有两个棋子chessArray[1][2] = 1;chessArray[2][3] = 2;//查看棋盘for (int[] ints : chessArray) {for (int anInt : ints) {System.out.print(anInt + "\t");}System.out.println("\n");}//现在将这个原始二维数组转换成 稀疏数组//1.先遍历数组,找到有多少有效数据【非0的数据】//sum:有效值数量int sum = 0;for (int[] ints : chessArray) {for (int anInt : ints) {if (anInt != 0){sum ++;}}}System.out.println("有效数据共有:" + sum + "个");//得到原始二维数组的行和列int hang = chessArray.length;int lie = chessArray[0].length;//2.创建稀疏数组//稀疏数组的行永远是 有效数据的数量+1 , 列永远是 3int[][] sparseArray = new int[sum + 1][3];//3.给稀疏数组里面添加数据//分别是 原始二维数组的 行数,列数,有效数据有几个sparseArray[0][0] = hang;sparseArray[0][1] = lie;sparseArray[0][2] = sum;//4.把原始二维数组里面的数据坐标 及 数据放到 稀疏数组里面//定义一个计数器,用来做稀疏数组的下标使用int count = 0;for (int i = 0; i < chessArray.length; i++){for (int j = 0; j < chessArray[i].length; j++){//如果原始二维数组里面的数据不为0,表示可以放到稀疏数组里面if (chessArray[i][j] != 0){count++;// 把原始数据的行 放到 稀疏数组sparseArray[count][0] = i;//列sparseArray[count][1] = j;//有效数据sparseArray[count][2] = chessArray[i][j];}}}//查看稀疏数组for (int[] ints : sparseArray) {for (int anInt : ints) {System.out.print(anInt + "\t");}System.out.println();}
2、稀疏数组转二维数组
//先获得一个稀疏数组int[][] sparseArray = new int[3][3];sparseArray[0][0] = 11;sparseArray[0][1] = 11;sparseArray[0][2] = 2;sparseArray[1][0] = 1;sparseArray[1][1] = 2;sparseArray[1][2] = 1;sparseArray[2][0] = 2;sparseArray[2][1] = 3;sparseArray[2][2] = 2;//1.读取稀疏数组第一行,得到原始数组的行和列,并创建原始数组int hang = sparseArray[0][0];int lie = sparseArray[0][1];int[][] chessArray = new int[hang][lie];//2.把稀疏数组里面的值拿出来 给原始二维数组赋上for (int i = 1; i < sparseArray.length; i++){//稀疏数组里面第二行 前两列都是 值得下标,chessArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];}for (int[] ints : chessArray) {for (int anInt : ints) {System.out.print(anInt + "\t");}System.out.println();}

三、队列
1、队列是使用场景
- 队列是一个有序列表,可以用数组或者是链表来实现
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
- 示意图:(使用数组模拟队列示意图) —————->
- 队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
- 队列是一种先进先出(first in first out) 的线性表,简称 fifo,允许插入的那一端为队尾,允许删除的那一段叫对头
3、数组模拟队列
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 头 及 rear 尾 分别记录队列前后端的下标, front 会随着数据输出而改变[取],而 rear 则是随着数据输入而改变[加],如图所示:
当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析
- 将尾指针往后移:rear+1, 当 front == rear 【空】 【头和尾相同时,表示此队列为空】
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。 rear ==maxSize-1[队列满] 【尾== 数组.length-1 说明队列满了】
4、普通队列出现的问题:
- 普通队列的缺点: 该队列只能使用一次【不能重复使用】,front 【头部下标】不会回溯,而要解决这个问题就需要使用到 环形队列
- 将原来的数组,使用一个算法,变成环形队列。 使用取模的方式:%
5、数组模拟环形队列思路分析:
- front 【头部】变量做一个调整:front就指向队列的第一个元素下标, 之前是指向-1,现在是指向0, 也就是说 arr[front] == 队列中的第一个元素 【front的初始值是 0 】
- rear 【尾部】变量做一个调整:rear就指向队列中的最后一个元素的后一个位置下标,因为希望空出一个空间做约定 【rear的初始值是 0】
- 能储存的数据量是 长度 - 1 , 因为rear 要预留一个空的
- 当队列满时的条件是,
**(rear + 1) % maxSize == front**【true 则满】- 代数: maxSize 是 4, 长度是4,下标为 0,1,2,3 因为rear要预留出一个空间,所以按照上面的 最后一个元素的后一个位置下标 只能是 3 【下标为2 的后面一个元素的下标】
- rear因为是指向最后一个元素的后面那个位置的下标,rear指向下标为2的元素的后面那个位置的下标,所以是3
- 带入条件 (3 + 1) % 4 == 0; 如果当前这个环形列队中的头部变量是 0 则表示此队列满了
- 当队列为空的条件, rear == front 【true 则空】
- 按照上面分析的话,队列中有效的数据的个数是 :
**(rear + maxSize - front) % maxSize**- 代数:如果 rear = 1,则表示在队列中下标为 0 的位置有1个 数据【为什么是rear = 1,因为他指向最后一个元素的后一个位置的下标】
- (1 + 3 - 0) % 3 = 1 所以等于1
四、队列代码实现
1、普通队列代码实现
//此类为队列public class ArrayQueue {private int maxSize; //队列最大容量private int front; //队列头部下标 最初是指向 -1private int rear; //队列尾部下标 没有元素则是指向 -1private int[] arrayQueue; //队列//通过构造方法 来指定队列的长度,并且初始化一些成员变量public ArrayQueue(int maxSize){this.maxSize = maxSize;front = -1;rear = -1;arrayQueue = new int[maxSize]; //创建指定大小的数组【队列】}//判断此队列是不是为空的方法public boolean isEmpty(){//如果头部下标 == 尾部下标 则表示队列中没有数据,为空return front == rear;}//判断队列是否已满的方法public boolean isFull(){//如果尾部下标 == 数组.length - 1 则表示队列满了return rear == maxSize - 1;}//往队列里面放数据的方法public void addQueue(int n){if (isFull()){throw new RuntimeException("队列已满,无法加入数据");}//存数据从尾部存,后存后出rear ++; //最初是指向-1的,每添加一次数据时 自增1arrayQueue[rear] = n;}//从队列里面取出数据public int getQueue(){if (isEmpty()){throw new RuntimeException("队列为空,没有数据可取");}front ++; //取数据是往头部取,先进先出return arrayQueue[front];}//查看队列全部元素public void showQueue(){if (isEmpty()){throw new RuntimeException("队列为空,没有数据展示");}for (int i = 0; i < maxSize; i++){System.out.println("arr["+ i +"]:" + arrayQueue[i] + "\n");}}//显示队列头部数据public int headQueue(){if (isEmpty()){throw new RuntimeException("队列为空,没有数据展示");}return arrayQueue[front+1];}}// -------------------------------------------------------测试public class ArrayQueueTest {public static void main(String[] args) {ArrayQueue queue = new ArrayQueue(3);boolean loop = true;Scanner scanner = new Scanner(System.in);while (loop){System.out.println("-------------------------------->");System.out.println("s 查看队列中所有元素");System.out.println("a 向队列中添加一个元素");System.out.println("g 向队列中得到一个元素");System.out.println("h 得到队列中的头部元素");System.out.println("e 退出循环");String next = scanner.next();switch (next){case "s":try{queue.showQueue();}catch (Exception e){System.out.println(e.getMessage());}break;case "a":try{queue.addQueue(scanner.nextInt());}catch (Exception e){System.out.println(e.getMessage());}break;case "g":try{int queue1 = queue.getQueue();System.out.println("得到的元素是: " + queue1);}catch (Exception e){System.out.println(e.getMessage());}break;case "h":try{int queue1 = queue.headQueue();System.out.println("头部的元素是: " + queue1);}catch (Exception e){System.out.println(e.getMessage());}break;case "e":scanner.close();loop = false;break;}}System.out.println("退出成功");}}
2、环形列队代码实现
public class CircleQueue {private int maxSize; //队列最大容量private int front; //队列头部下标 最初是指向 0private int rear; //队列尾部下标 没有元素则是指向 0private int[] arrayQueue; //队列//通过构造方法 来指定队列的长度,并且初始化一些成员变量public CircleQueue(int maxSize){this.maxSize = maxSize;arrayQueue = new int[maxSize]; //创建指定大小的数组【队列】}//判断此队列是不是为空的方法public boolean isEmpty(){//如果头部下标 == 尾部下标 则表示队列中没有数据,为空return front == rear;}//判断队列是否已满的方法public boolean isFull(){//如果尾部下标 == 数组.length - 1 则表示队列满了return (rear + 1) % maxSize == front ;}//往队列里面放数据的方法public void addQueue(int n){if (isFull()){throw new RuntimeException("队列已满,无法加入数据");}//直接上数据放到 尾部arrayQueue[rear] = n;//将rear后移,因为要考虑到 如果rear在最后面位置的话,可能要往头部放rear = (rear + 1) % maxSize;}//从队列里面取出数据public int getQueue(){if (isEmpty()){throw new RuntimeException("队列为空,没有数据可取");}// 这里需要分析出 front 是指向队列的第一个元素// 1. 先把 front 对应的值保留到一个临时变量// 2. 将 front 后移, 考虑取模// 3. 将临时保存的变量返回int value = arrayQueue[front];front = (front + 1)%maxSize;return value;}//查看队列全部元素public void showQueue() {if (isEmpty()) {throw new RuntimeException("队列为空,没有数据展示");}// 思路:从 front 开始遍历,遍历多少个元素// 动脑筋for (int i = front; i < front + size(); i++) {System.out.printf("arr[%d]=%d\n", i % maxSize, arrayQueue[i % maxSize]);}}//求出当前队列的有效数据public int size(){return (rear + maxSize - front) % maxSize;}//显示队列头部数据public int headQueue(){if (isEmpty()){throw new RuntimeException("队列为空,没有数据展示");}return arrayQueue[front];}}//-------------------------------------------------------------------测试下面为测试上面的那个队列public class CircleQueueTest {public static void main(String[] args) {CircleQueue queue = new CircleQueue(3);boolean loop = true;Scanner scanner = new Scanner(System.in);while (loop){System.out.println("-------------------------------->");System.out.println("s 查看队列中所有元素");System.out.println("a 向队列中添加一个元素");System.out.println("g 向队列中得到一个元素");System.out.println("h 得到队列中的头部元素");System.out.println("e 退出循环");String next = scanner.next();switch (next){case "s":try{queue.showQueue();}catch (Exception e){System.out.println(e.getMessage());}break;case "a":try{queue.addQueue(scanner.nextInt());}catch (Exception e){System.out.println(e.getMessage());}break;case "g":try{int queue1 = queue.getQueue();System.out.println("得到的元素是: " + queue1);}catch (Exception e){System.out.println(e.getMessage());}break;case "h":try{int queue1 = queue.headQueue();System.out.println("头部的元素是: " + queue1);}catch (Exception e){System.out.println(e.getMessage());}break;case "e":scanner.close();loop = false;break;}}System.out.println("退出成功");}}
